Custom editor template not rendered / fired - c#

I'm using custom templates within my MVC project to display various object types in different ways. Some are working and others are not! For any that don't work, they are being passed into my object.cshtml custom template rather than their own.
Here's an example. In this example, I'm creating an address lookup type which I want to render a first line of address and postcode field with a lookup button.
My ViewModel has the following:
namespace MyProject.Views
{
public class AddressLookup
{
public string Postcode { get; set; }
public string FirstLine { get; set; }
}
public class RegistrationViewModel
{
[DisplayName("Address Lookup")]
public AddressLookup addressLookup { get; set; }
}
}
My view looks like this:
#model RegistrationViewModel
<div class="well" id="form-well">
<h2>Register New User</h2>
<h3>Step 1 of 3 - Login Details</h3>
#using (Html.BeginForm("RegisterNewUser", "Controller", FormMethod.Post, new { #class = "form-horizonal" }))
{
#Html.AntiForgeryToken();
#Html.EditorForModel(Model);
#Html.ValidationSummary();
<div style="padding: 15px;" class="form-group col-md-offset-3">
<input type="submit" value="Next" class="btn btn-lg btn-success" />
</div>
}
</div>
My AddressLookup.cshtml looks like this:
#using MyProject
#model AddressLookup
#Html.TextBoxFor(x => x.FirstLine)
#Html.TextBoxFor(x => x.Postcode)
<p>
<button class="btn btn-info" type="button" onclick="alert('lookup');" value="Add new address">Lookup address</button>
</p>
But when debugging, it runs the code in the object.cshtml in the EditorTemplates folder and not the one for AddressLookup.
Any thoughts?
Thanks
Simon

Adding a UIHint to the property in my view model worked (although I don't fully understand why).
namespace MyProject.Views
{
public class AddressLookup
{
public string Postcode { get; set; }
public string FirstLine { get; set; }
}
public class RegistrationViewModel
{
[DisplayName("Address Lookup")]
[UIHint("AddressLookup")]
public AddressLookup addressLookup { get; set; }
}
}
Thanks
Simon

Related

Passing dropdown list data and then displaying it in a form

Im extremely new to .net core and trying to teach myself a few things just for the fun of it.
I am trying to pass a list of countries through to a form from a Country Model.
Club Model
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace SoccerExchange.Models
{
public class Club
{
public int Id { get; set; }
public string ClubName { get; set; }
public string LeagueName { get; set; }
public string Standard { get; set; }
public ICollection<Advert> Adverts { get; set; }
}
}
Country Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SoccerExchange.Models
{
public class Country
{
public int Id { get; set; }
public string CountryName { get; set; }
public ICollection<Club> Clubs { get; set; }
}
}
ViewModel
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
using SoccerExchange.Models;
namespace SoccerExchange.ViewModels
{
public class NewClubViewModel
{
public SelectList CountriesList { get; set; }
public Club Club { get; set; }
}
}
Create Function in Controller
public IActionResult Create()
{
var countries = _context.Countries.ToList();
var viewModel = new NewClubViewModel
{
CountriesList = new SelectList(countries, "Id", "CountryName")
};
return View(viewModel);
}
Create View
#model SoccerExchange.ViewModels.NewClubViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Club</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Club.ClubName" class="control-label"></label>
<input asp-for="Club.ClubName" class="form-control" />
<span asp-validation-for="Club.ClubName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Club.LeagueName" class="control-label"></label>
<input asp-for="Club.LeagueName" class="form-control" />
<span asp-validation-for="Club.LeagueName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Club.Standard" class="control-label"></label>
<input asp-for="Club.Standard" class="form-control" />
<span asp-validation-for="Club.Standard" class="text-danger"></span>
</div>
<div>
#Html.DropDownListFor(m => Model.CountriesList, "--Please Select--")
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
I am almost certain that I have the Models along with the ViewModels correct. Im also almost certain that I have the create method correct. But for some reason, I can not pass through the list data for the drop down in the form.
Would anyone have any hints on what I Might be doing wrong?
Html.DropDownListFor expects the data source as the second parameter (see below).
Image of Intellisense for DropDownListFor
This article may be some help for you ASP.NET MVC Dropdown List From SelectList
You need to fix your clab class by adding Country
public class Club
{
public int Id { get; set; }
public string ClubName { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
.....
}
and fix the view
#Html.DropDownListFor(m =>m.Club.CountryId, #Model.CountriesList, "--Please Select--")

ASP.NET MVC EF Core, Property Access in View while on selected ID for different Model

I´m having an issue where I have three models as show below: Person, Competence with PersonCompetence between them. My current controller method gets an Id from previous page and shows that person with a list of this person's Competence and Level. In this View however I want to have a POST for new Competence. So at the same time you are adding a new one and you can see which ones you already have.
With the controller method I have now I can access the PersonCompetence and Competence when showing the list.
I dont have access to the Competence properties for asp-for="Competence" marked ###### in the View for AddComp.
I need the ID of person for POST to right person
I need the CompetenceType for POST to that property
I need PersonCompetence to show the list of current PersonCompetence.
I get that with the current #model CompetenceRadar.Models.Person I only reach Person properties.
I have looked at having a ViewModel with access to all tables with an IEnumerable for each table, but this breaks my current Controller when I search for the Id of the person showing. I have switched the #model in the View, but then I can't access Person ID/name.
So how do I access the Competence properties , list one person and get a list of PersonCompetences for that Person.
Please tell me if you want me to clarify something.
I don't need working code, just something to point me in the right direction for a solution.
Is it a ViewModel?
Can I POST without the asp-forattribute?
Models
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public ICollection<PersonCompetences> PersonCompetences { get; set; }
}
public class PersonCompetence
{
public int ID { get; set; }
public int PersonID { get; set; } // FK
public int CompetenceID { get; set; } // FK
public int Level { get; set; }
public Competece Competence { get; set; }
public Person Person { get; set; }
}
public class Competence
{
public int ID { get; set; }
public string CompetenceType { get; set; }
public string CompetenceCategory { get; set; }
public ICollection<PersonCompetence> PersonCompetences { get; set; }
}
AddComp Kontroller function
public async Task<IActionResult> AddComp(int? id)
{
var person = await _context.Personer
.Include(pk => pk.PersonCompetences)
.ThenInclude(k => k.Competence)
.FirstOrDefaultAsync(m => m.ID == id);
return View(person);
}
View_AddComp View for AddComp
#model CompetenceRadar.Models.Person
<h1>AddComp</h1>
<div class="row">
<form asp-action="AddComp">
<input type="hidden" asp-for="ID" />
<div class="form-group col-sm-4">
<label asp-for="#############" class="control-label col-sm-4"></label>
<input asp-for="#############" class="form-control col-sm-4" />
<span class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-dark" />
</div>
</form>
</div>
#foreach (var item in Model.PersonCompetences)
{
<div class="row py-2 rounded" style="background-color:lightslategray">
<div class="col-sm-3 pt-2">#item.Competence.CompetenceType</div>
<div class="col-sm-1 pt-2">#item.Niva</div>
<div class="col-sm-3 pt-2">#item.Competence.CompetenceCategory</div>
<div class="col-sm-5 d-flex justify-content-end">
<a class="btn btn-dark mr-1" role="button" asp-area="" asp-controller="Competence" asp-action="UpdateLevel" asp-route-id="#item.ID">Update</a>
<a class="btn btn-dark mr-1" role="button" asp-area="" asp-controller="Competence" asp-action="DeleteComp" asp-route-id="#item.CompetenceID">Remove</a>
</div>
</div>
}
Simple anwser is that I needed a ViewModel with all three Models
public class ExampleViewModel {
public Person person { get; set; }
public PersonCompetence personCompetence { get; set; }
public Competence competence { get; set; }}
This alows me to access the different values for ID, CompetenceType and a List for of the current PersonCompetence.
What is ViewModel in MVC?

How to create an entity with a Collection of another entity

As I am new to an ASP.NET Core 2.0 MVC I have a little demo app where I want to add a new entity of Type Lineup which has a ICollection of type Player in its model.
The involved classes are
public class Lineup
{
public int LineupID { get; set; }
public int PlayerID { get; set; }
public ICollection<Player> Attendants { get; set; }
}
and
public class Player
{
public int PlayerID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
My Create Lineup view looks like this
<form asp-action="Create">
<div class="form-horizontal">
<h4>Lineup</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Attendants" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="Attendants" class="form-control"
asp-items="ViewBag.Players" multiple="multiple">
</select>
</div>
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
The ViewBag.Players is part of the LineupsController:
private void PopulatePlayersDropDownList(object selectedPlayer = null)
{
var playersQuery = from p in _context.Player
orderby p.Name
select p;
ViewBag.Players = new SelectList(playersQuery.AsNoTracking(), "PlayerID",
"Name", selectedPlayer);
}
That's how it looks so far:
but inside my Create method
public async Task<IActionResult> Create([Bind("Attendants")] Lineup lineup)
{
if (ModelState.IsValid)
{
_context.Add(lineup);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(lineup);
}
the Attendants count of the Lineup entity is 0 and not 2 as expected from the sample above. What is wrong here? Thanks for any help.
You need to instantiate your property when you create the object. Otherwise, the property is the default, which is null:
public class Lineup
{
public int LineupID { get; set; }
public int PlayerID { get; set; }
public ICollection<Player> Attendants { get; set; } = new List<Player>();
}
ok, these 2 steps solved my problem:
see answer of krillgar
changing the method signature of Create to
public async Task Create(int[] PlayerIDs)
{
...
}

MVC Property of object in model is null on post [duplicate]

This question already has an answer here:
Why is Model Binding not working in my POST action method?
(1 answer)
Closed 5 years ago.
I know there are tons of posts similar to this, but I cannot seem to find the answer after much investigation. On post back CompanySetting.NewSetting.SettingType and CompanySetting.NewSetting.SettingValue are null, despite putting text into the TextBoxFor fields and clicking submit.
Why is this?
Here is the view:
#model Project.Models.CompanySettingView
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<form>
<div class="form-group">
<label>Setting Type</label>
#Html.TextBoxFor(m => m.NewSetting.SettingType, new { #class = "form-control", #id = "SettingType" })
<br />
<label>Setting Value</label>
#Html.TextBoxFor(m => m.NewSetting.SettingValue, new { #class = "form-control", #id = "SettingValue" })
<br />
</div>
<div style="float: right;">
<button type="button" style="width:100px;" onclick="ClearFields()" class="btn btn-default">Clear</button>
<button type="submit" style="width:100px;" class="btn btn-primary">Submit</button>
</div>
</form>
}
Receiving Controller:
[HttpPost]
public ActionResult AddCompanySetting(CompanySettingView model)
{
...
}
CompanySettingView Class:
public class CompanySettingView
{
public List<CompanySetting> Settings = new List<CompanySetting>();
public CompanySetting NewSetting = new CompanySetting();
}
CompanySetting Class:
public class CompanySetting
{
public int CompanySettingId { get; set; }
public int CompanyId { get; set; }
public string SettingType { get; set; }
public string SettingValue { get; set; }
}
Answer from Comment
its to do with your object initialiser in the CompanySettingView. Change those to properties and initialise them in your constructor (make sure the class has a default constructor) and all should be golden
Something like this:
public class CompanySettingView
{
public List<CompanySetting> Settings { get; set; }
public CompanySetting NewSetting { get; set; }
public CompanySettingView()
{
this.Settings = new List<CompanySetting>();
this.NewSetting = new CompanySetting();
}
}

Binding partial to multiple actionresults

I was wondering if anybody could suggest a way of overcoming the following issue. I have the following models
// AModel
public class AModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class AModelViewModel
{
public AModelViewModel()
{
AModel = new AModel();
}
public AModel AModel { get; set; }
}
//BModel
public class BModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class BModelViewModel
{
public BModelViewModel()
{
BModel = new BModel();
}
public BModel BModel { get; set; }
}
This is what my controller looks like
public ActionResult PageA()
{
var model = new AModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageA(AModel aModel)
{
return View();
}
public ActionResult PageB()
{
var model = new BModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageB(BModel bModel)
{
return View();
}
The two views look like this
//PageA
#model WebApplication13.Models.AModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
//PageB
#model WebApplication13.Models.BModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
Both views use the following partial view
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
<input type="text" class="form-control" name="Keyword" />
</div>
The problem I have is that because this partial is shared, and the name attribute of the keyword input is 'Keyword', when I submit the form on either page the keyword property is never binded so it's always null. Is there a way I can share this partial, but alter the prefix depending on what page i'm using. Any help would be greatly appreciated.
Instead of using a paritial view.. Create an EditorTemplate in Views\Shared\EditorTemplates named Keyword.cshtml with code something like
#model string
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
#Hmlt.TextBoxFor(a => a, new {class="form-control"})
</div>
then in your views you just call
#Html.EditorFor(a => a.AModel.Keyword,"Keyword")
if you have to use PartialView.. you can pass in an HtmlFieldPrefix via ViewDataDictionary when you call the partial view. To do this, you would call your partial like this.
#Html.Partial("~/Views/Shared/_RandomView.cshtml",
new ViewDataDictionary { TemplateInfo = new TemplateInfo() {
HtmlFieldPrefix = "AModel" } });
then in your partial view you will need to use the #Html.Textbox helper like so.
<div class="form-group">
<label for="keyword">Keyword</label>
#Html.TextBox("Keyword", string.Empty, new { #class="form-control" })
</div>

Categories

Resources