I have a question regarding Model Binding in MVC
Say I have a simple POCO like below.
public class Person
{
public Title Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool LeadPassenger { get; set; }
}
I have a controller that passes a collection of Persons to a View and within that View I want to render a radio button to allow the user to select the LeadPassenger from the collection of Persons.
Now, for this to work (and only one radio button be selected at a time) all the "LeadPassenger" radio buttons must share the same name. But this seems to be impossible using the Model Binding conventions whereby each control is named after each property of the item in the collection.
<input id="Passengers_0_LeadPassenger " name="PolicyMembers[0].LeadPassenger" type="radio" value="true" />
<input id="Passengers_1_LeadPassenger " name="PolicyMembers[1].LeadPassenger" type="radio" value="false" />
How do I get round this limitation? I could use jQuery to force all the radio buttons to switch off when one is selected but that seems to be a 'fudge'!
Any ideas?
The trick here is the task you are trying to accomplish isn't really something for model binding, which isn't really aware of data outside it's "row" so to speak. What you want to do here is introduce a new field for lead passenger selection, binding the value to the unique identifier for person and the name to the same thing. Then pick it up as a separate variable in your controller and map it from there.
Related
I've been trying to get my head around how to solve this problem in the cleanest way.
How to get the model binder to NOT create empty objects when submitting empty strings for complex types?
Say I've got this model:
public class CreateCaseModel
{
[Required]
public UserModel Contact { get; set; }
public UserModel Assigned {get; set; }
}
and the user-model:
public class UserModel {
[Required]
public string Id {get;set;}
public string Name {get;set;
}
And in the view i have these inputs:
<input type="hidden" name="Assigned.Id" value="" />
<input type="hidden" name="Contact.Id" value="userId1" />
I post this to a controller something like:
public ActionResult Create(CreateCaseModel model){
if (!ModelState.IsValid)
{
//handle error
}else {
//create
}
}
In the controller now my model.Contact and model.Assigned will never be null.
Only the model.Contact will however contain anything (the Idwill be set)
At this point the modelstate will also be invalid due to the fact that the UserModel.Id field is marked as required.
What I want to happen is for the binder to not create the UserModel object at all if the input is empty.
The entire reason for me needing this is that I'm using the UserModel.Name field elsewhere in the view, and I'd like to group the two fields together. And I want to use the ModelState.IsValidcheck for serverside validation.
I could of course go with extracting the Id and Name fields of each userobjects and put them directly in the CreateCaseModel, but I want to explore if what I describe is even possible.
Grateful for any tips or pointers!
How to get the model binder to NOT create empty objects when submitting empty strings for complex types?
Write your own custom ModelBinder. The default model binder will always attempt to create complex types for recursive modelbinding.
I am a long time backend .Net developer who is new to web application development. I am using MVC 4, Razor, EF 5, and I have a basic understanding on how to make a routine DB driven MVC 4 site with these tools.
I need to create a custom form capability for a workflow scenario. I have the entire code-first schema designed and classes for different formfield types, and formvalue type.
The model that will be passed to a view will be a form class with a list of formvalues, which contain form field specifications. So the view will have to iterate through the form fields and dynamically choose what editors to use and so on.
My problem is that this prevents me from using any data annotation for client side validation. And every place I find advice on custom validation assumes (not surprisingly) that all the fields are members of a class. Whereas, in this case, all the fields are on a list.
My goal is to end up with validation messages on each field and a validation summary as you would normally have if the view was simply bound to an annotated class.
Any suggestions on how to approach this? Thanks in advance.
Imagine view logic that has the following:
#foreach (var fieldvalue in Model.FormFieldValues) {
// Logic that chooses the appropriate editor based on typeof(fieldvalue.FormField)
// Binds the editor to fieldvalue.Value
// Validation specs are contained in the Formfield obtained by fieldValue.FormField
// And my problem is setting up the validation without the aid of the normal method
// of data annotation or class level custom validation.
}
and the fieldvalue class looks like this:
public class FormFieldValue : EboEntity
{
public string Value { get; set; }
[Required]
public int FormFieldId { get; set; }
[Required]
public virtual FormField FormField { get; set; }
[Required]
public int FormId { get; set; }
[Required]
public virtual Form Form { get; set; }
}
And imagine that the FormField object has fields such as Required, Maxlength, and so on, depending on the kind of field it is (i. e. FormFieldText would be a subclass of FormField)
ANSWER:
Ok, you can tell I am new at MVC.
The answer to my question is that the specific editors take htmlAttributes which can control validation. So in the case where one of my formfields is a required textfield of stringlenth(10), I can invoke the editor as follows:
<div class="editor-field">
#Html.TextBoxFor(model => model.NoTexty, new {
required = true,
maxlength = 10,
placeholder = "Texty"})
#Html.ValidationMessageFor(model => model.NoTexty)
</div>
but in my case the htmlAddtributes won't be hard coded, but rather they will come from the fields in the formvalue.FormField object.
Sorry if I wasted anyone's time on this. But I will leave this here for any other newbies like me.
Can somebody please provide code to allow [] to be used so as to save contained collection within a #model?
Edit View:
#model MVC3.Models.A
// I need to save collection values but can't use [] here to setup model binding.
#Html.EditorFor(model => model.Bs[0].Val)
Models:
public class A
{
public A()
{
this.Bs = new HashSet<B>();
}
public int Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public int Val { get; set; }
public virtual A A { get; set; }
}
The problem is that your property is an ICollection<T>, which doesn't provide indexed access to elements.
Change it to an IList<T> or just a List<T> and you can write your view code as it is in your first example.
If you can't change the property type for some reason, then you'll need to create a specialized view model for your particular view and then map it to the A model in the controller POST action.
Alternatively, it is possible (albeit a little awkward) to bind to simple collection types directly as long as you follow the proper HTML conventions, which means using multiple input elements with the same name, as described by Phil Haack, i.e.:
<input type="text" name="ints" value="1" />
<input type="text" name="ints" value="4" />
<input type="text" name="ints" value="2" />
<input type="text" name="ints" value="8" />
But this only works for simple types, i.e. primitives or strings, not complex types like whatever B is in this context.
One final comment: If you're using editor templates, you can usually just bind to the entire collection and the MVC framework will figure out how to put it back together, as in just:
#Html.EditorFor(m => m.Bs);
Of course this gives you no control over what goes between the editor templates for each item, so if you're trying to generate <li> elements for them or something like that, then you'll have to embed it directly into the editor template which possibly means creating a custom editor template specifically for this collection binding and using the corresponding override of EditorFor (the one that takes a template name).
It's a bit of a pain, but could be less work than trying to switch to a parallel-model system, if you've been sharing classes between your UI and data layers up 'til now.
I use helper #Html.EditorForModel() on all my views.
There is a desire that he skip two fields in my model, but only on this view, the other he must continue to display these fields as usual.
How can I skip these two fields only in this view?
Use the [ScaffoldColumn(false)] attribute.
E.g.
public class Person {
[ScaffoldColumn(false)]
public int PersonID { get; set; }
...
Solution and example sourced from: Pro ASP.NET MVC 3 Framework, Third Edition
I'd recommend writing viewmodels for any view that you want to deviate from default behaviour.
Side note: It's probably a good idea to write a viewmodel for every view, as you get separation of concerns, and it's easier to control the behaviour of each view.
Anyway...
For example, say your model is
class Herps {
public string Name { get; set; }
public int SecretToSomePeople { get; set; }
}
and you don't want to have SecretToSomePeople shown on one of your views, create a viewmodel that doesn't contain SecretToSomePeople
class Herps {
public string Name { get; set; }
}
and use that as the model for the desired view. Make sure you copy to/from the actual model somewhere though.
Strictly speaking, if you don't want to display the fields then they shouldn't be on the Model - the point of Models to to hold exactly the data required for the View.
I have a class:
class Item
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
I have a view where I want objects for the class above created using inputs (so I have a textbox for Name and a date select type thing for Date). However, I want thev user to be able to click a link and through jquery/javascript another textbox and date select will be added to the form, and this can happen unlimited times.
How can I bind this to a model so that I can return it to my action method? Ideally the model would be something like:
class MyModel
{
public string AProperty { get; set; }
public List<Item> Items { get; set; }
}
Apologies for the poor wording, struggling to describe what I want but I think this should get the point across.
You want to use a client-side template and then return JSON to your controller. If you are using MVC 3, JSON model binding is built-in, but in MVC 2 you need to set up your own binder. There is an example here.
I recommend using KnockoutJS for your client side. It's very simple for working with dynamic collections and very well documented. You can see an example similar to what you're trying to do here as well as in the previous link.