asp.net - MVC model binding to nested collection - asp.net-mvc-4

I have a class:
public class ClientInformation{
public string UserName {get; set;}
public ICollection<RegionDistrictCity> RegionDistrictCity
{
get;
set;
}
public class RegionDistrictCity
{
public string Region { get; set; }
public string District { get; set; }
public string City { get; set; }
}
}
How should be formated the name attribute of input elements for properties Region, Distirct, City in html in order to make model binder populate collection "ICollection RegionDistrictCity"?
I tried to have an action method with parameter of type "ClientInformation" and html name attributes formated like "[index].PropertyName" but in that case only the property "UserName" is binded.
I tried to have action method with parameter name "client" and have html names attributes formated like "client[index].PropertyName" but it doesn't work. (in tha case if I there is a "List client" then it would get populated)
Thanks.

In MVC4 you should use a for loop instead of a foreach to bind your collection. Then the model binder will be able to populate your model when you submit your data.
#for (int i = 0; i < Model.RegionDistrictCity.Count; i++)
{
#Html.EditorFor(model => Model.RegionDistrictCity[i].Region)
}
But this will only work if you are not deleting or adding items to your collection dynamically.
If you want to do that, you should use the BeginCollectionItem helper created by steve sanderson. http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

Related

How to bind a collection with objects?

I need to bind a collection of objects from a querystring, but I cannot find the proper querystring format.
My controller code:
public class Filter
{
public string Name { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
}
public void Get(IEnumerable<Filter> filters)
{
....
}
If you do want to pass the objects with querystring you could try as below:
https://localhost:44389/Test/Index?filters[0].Name=n1&filters[1].Name=n2&filters[2].Name=n3&filters[2].Value=v3
The result:
the offcial document related:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-6.0#collections
but i don't think it's a good solution,because the length of Url is limited,if your model has plenty properties and your collection has many elements ,you may get some error

Null viewmodel passed to controller

I have a basic 'create' view scaffolded from a domain model, so it is typed to the model
#model TblProduct
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="Artist" class="form-control" />
...
I'm trying to add functionality and use a view model instead, and I'm starting with a very basic viewmodel with only that domain model within it:
public class ProductViewModel
{
public TblProduct P { get; set; }
}
Now I've changed the 'create' view to use the view model instead
#model ProductViewModel
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="P.Artist" class="form-control" />
...
So I expect the model to be valid given that (aside from editing the variable names) I'm populating all the same fields from the form, and effectively no other fields have been added to the model.
An error occurs when I post the form, I pass a ProductViewModel parameter to the create method but on inspection it is null. However ModelState.IsValid is true. So the code tries to write to the db and fails.
public async Task<IActionResult> Create([Bind("ID,Artist,ProductTitle... (long list removed)...] ProductViewModel productAndItems)
{
var prod = productAndItems.P;
if (ModelState.IsValid)
{
_context.Add(prod);
...FAIL
Any idea what I should be checking here - what am I missing?
How do I get the view (typed to a viewmodel) to pass the model data to the controller? And if it's null, how can ModelState.IsValid be true? In the example above I have debugged, the parameter passed in productAndItems is null.
Your current Bind attribute is looking for the following properties ID,Artist,ProductTitle... (White-list) and it's not finding them so therefore it's ignore everything and treating it is as a (Black-list) item.
You can either decorate your ProductViewModel with the Bind attribute as follows:
[Bind(Include = "P")]
public class ProductViewModel
{
public TblProduct P { get; set; }
}
This will of course mean all the properties in the TblProduct will be bound when submitting
If you do not want all of the properties to be bound on submit for TblProduct then you can decorate the TblProduct with the Bind attribute as follows
public class ProductViewModel
{
public TblProduct P { get; set; }
}
[Bind(Include = "ID,Artist,ProductTitle")]
public class TblProduct
{
public int ID { get; set; }
public string Artist { get; set; }
public string ProductTitle { get; set; }
public string ProductSubTitle { get; set; } //we will not include this in our (White-list)
//more props
}
More reading at MSDN
you need to add a name attribute to your form so the controller will pick it up.

How do you generate a hyperlink from a Model in MVC Razor

I want to store website addresses in a table in the database as either "http://some_website.com" or just as "some_website.com". Then in the view I have access to the text through "Model.Website". How can I generate a hyperlink? Is there a helper to do this?
Simply embed the model properties in a hyperlink element appropriately:
public class YourModel
{
public string Href { get; set; }
public string LinkText { get; set; }
}
...
#Model.LinkText

asp.net mvc4 create subform to add multiple adresses for user during the registration

In my asp.net mvc4 app I want to allow users to add multiple regions and cities to their account during the registration. I want to add some subform in which would be the dropdownlist for Region and City and the user should be able to add multiple regions and cities during the registration Process. I know how to do this with jquery but I want to use view model for validation and for creation of this registration form but I do not know how to create this view model and how to define this form in view. I am stating my current registration view model and I want to ask if You can help me to modify it so it will work as I need.
public class RegisterUserModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
public string Password { get; set; }
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
}
Thank you.
You will need to add a collection to your view model that will contain the Regions and Cities. You should create another type to encapsulate these two properties, say Place, so your View Model will look like this:
public class RegisterUserModel
{
// other properties
public List<Place> Places { get; set; }
}
public class Place
{
public string Region { get; set; }
public string City { get; set; }
}
To display the current places in the View Model you simply iterate over them with a foreach and use the helpers to display the Region and City for each one. To add a new Place the key is to name the input correctly so the default Model Binder will treat it as an item in the collection. The default Model Binder uses indexes to do this. For example, the inputs for the first Place in the Places collection should be named like this:
<input name="Places[0].Region" />
<input name="Places[0].City />
The next Place in the collection would be [1] and so on. Since you are familiar with jQuery I will skip how these can be added to the DOM.

Enumerable objects in ViewBag in MVC4

I have the below model say F:
public partial class F
{
[Key, Display(Name = "Id")]
public int FId { get; set; }
public int RId { get; set; }
public int FTId { get; set; }
public string C { get; set; }
public string U { get; set; }
public string D { get; set; }
[ScaffoldColumn(false)]
public System.DateTimeOffset Created { get; set; }
}
In the controller I have to read all the records of 'F' from database and assign those to an enumerable list of records.
For ex:
ViewBag.Cs = enumerable C column items (textbox)
ViewBag.Us= enumerable U column items (textbox)
ViewBag.FTIDs = enumerable FTId column items (this has to be a dropdown)
In my I have to show
#Html.Textbox(Cs);
#Html.Dropdown(FTIDs);
I gave only textbox and dropdows as an example, there could be many other controls like datetimes, checkboxes etc.,
I should be able to written each column as list a in viewbag and show it in MVC View.
Can somebody advise if this achievable and how?
Many thanks...
Don't use a viewbag for something like this - instead strongly bind your view to a model. Only use a view bag if you have something really small to pass.. anything complex you should always use a strongly typed view model, you get intellisence and its must cleaner for unit testing
View Model:
Public class MyViewModel
{
public List<F> MyListOfFObjects { get; set; }
}
Now when you create your view you can bind it to this view model in the popup or if you don't want to recreate it simply add a reference to it at the top of your view like so:
#model <your project>.<folder view model is in>.<view model name>
for example
#model AdventureWorks.Models.EmployeeViewModel.
In your controller you simply create this view model and pass it to your view such as:
public ActionResult Index()
{
MyViewModel vm = new MyViewModel();
// Initialize your view model
// Get all the F objects from the database and populate the list
return View(vm); // now your view will have the view model
}
Now in the view you can iterate through this view model
#foreach(var fObject in Model)
{
#Html.TextBoxFor(m => m.fId)
#Html.TextBoxFor(m => m.rID)
}
Here is a link to a list of the different #Html helpers that you can use btw
http://qkview.com/techbrij/aspnet-mvc-4
Reference for strongly binded views:
http://www.asp.net/mvc/tutorials/views/dynamic-v-strongly-typed-views