Passing multiple checkbox values to deeper level of model .net mvc - c#

A few similar questions have been asked before but my use case is a bit different.
So this is my model:
public class YourModel
{
public string[] Suburb { get; set; }
}
And my view:
<input name="Suburb" type="checkbox" value="sydney" /><span>sydney</span>
<input name="Suburb" type="checkbox" value="melbourne" /><span>melbourne</span>
Controller:
public ActionResult AdvancedSearch(YourModel s)
{
// logic
}
So MVC is smart enough to retrieve the multiple checkbox values to put them in the Suburb array in YourModel model. I can inspect all values there. But my use case is that the YourModel is just the nested model inside another model MyModel:
public class MyModel
{
//other properties
public YourModel m { get; set; }
}
So now how do I make MVC post the checkbox values to a deeper model MyModel.YourModel? I have tried #Html.CheckBoxFor and #Html.CheckBox but neither of them worked.
Right now my work around is to add a temporary array placeholder in the outside model and then assign all the data to the inside model when available, but that is definitely not ideal.

You need to use add MyModel
<input name="m.Suburb" type="checkbox" value="sydney" /><span>sydney</span>
<input name="m.Suburb" type="checkbox" value="melbourne" /><span>melbourne</span>

In Razor, you don't have to define the name of the top-most Model, only the names of the properties of inner models:
<input name="m.Suburb" type="checkbox" value="sydney" /><span>sydney</span>
<input name="m.Suburb" type="checkbox" value="melbourne" /><span>melbourne</span>
However, I'd strongly suggest you to change that m name to something more significant.

Related

Get checkbox names ASP.NET Core

I want to take only selected checkboxes. I tried this.
<input class="form-check-input" type="checkbox" name="Box" value="LDL" />
<label for="Box">LDL</label><br>
<input class="form-check-input" type="checkbox" name="Box" value="LDL" />
<label for="Box">LDL</label><br>`enter code here`
<input class="form-check-input" type="checkbox" name="Box" value="HDL" />
<label for="Box">HDL</label><br>
<div class="button-holder d-flex justify-content-center">
<button type="submit" class="btn btn-success">Send</button>
</div>
This is the Action
[HttpPost]
public IActionResult SendTest(ListParams listParams)
{
}
This is the DTO
public class CheckboxParams
{
public string Box { get; set; }
public bool IsChecked { get; set; }
}
public class ListParams
{
public ListParams()
{
this.AllCheckedParams = new List<CheckboxParams>();
}
public List<CheckboxParams> AllCheckedParams { get; set; }
}
In asp.net core, Model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix. Besides, the nested array matches the [index].property_name or property_name[index].property_name.
In your code, the name of the checkbox could not match the property in ListParams model. The correct way should be AllCheckedParams[index].box.
But for your scenario, your model design is not correct. The browser will only send the selected checkbox to the backend by default when form submit. If you choose the first two or last two(anyway,that is to say the index should be consecutive) checkbox, it works well. If you choose the first and third checkbox, it does not work any more. Because the name of them is AllCheckedParams[0].box and AllCheckedParams[2].box. They are not consecutive index of the array, so only the first checkbox could be matched and passed to the backend.
The correct way is that just change your backend received data like below:
[HttpPost]
public IActionResult SendTest(string[] box)
{
}
If you still want to use the model, you need change the model like below:
public class ListParams
{
public List<string> Box { get; set; }
}
Controller:
[HttpPost]
public IActionResult SendTest(ListParams listParams)
{
}
The HtmlHelper class provides two extension methods to generate a element in an ASP.NET MVC view. They are as follows:
CheckBox()
CheckBoxFor()
for creating form in razor page you can use
#model Student
#Html.CheckBoxFor(m => m.isActive)
The Html.CheckBox() is a loosely typed method which generates a with the specified name, isChecked boolean, and HTML attributes.
#Html.CheckBox("isActive", true)

Returning a partial view from controller with proper id and name in inputs

I'm developing a web MVC application with net core 2.2.
I have the following classes:
public class A
{
public IList<B> Bs { get; set; }
}
public class B
{
public string Id { get; set; }
public string Name { get; set; }
}
The following view:
#model A
#for (int i = 0; i < Model.Bs.Count; i++)
{
<partial name="_BsPatialView" for="Bs[i]" />
}
And the following partial view (_BsPatialView.cshtml):
<input type='hidden' asp-for="#Model.Id" />
<input asp-for="#Model.Name" />
Until here, everything it-s been generated fine. An example of the created inputs in the partial view is:
<input type="hidden" id="Bs_3__Id" name="Bs[3].Id" />
<input type="text" id="Bs_3__Name" name="Bs[3].Name" />
With the elements name and ids the model binder in the controller can properly bind everything.
The problem is when I try to return the partial view from the controller. What I do is:
public IActionResult AddBElement(A a)
{
a.Bs.Add(new B() { Id = Guid.NewGuid() });
return PartialView("_BsPatialView", a.Bs.Last());
}
The resulting html is:
<input type="hidden" id="Id" name="Id" />
<input type="text" id="Name" name="Name" />
So then, when I submit a form in which these inputs are, the model binding fails.
So, how should I return the partial view from the controller to fix this? Is there any equivalent to the partial's tag helper for attribute to use on the controller?
Model binding uses field names to map them to Model properties. Now because your name does not contain any information about the parent class, A, model binder does not know how to bind them.
So in other words, model binder would know how to bind this input:
<input type="hidden" id="Bs_3__Id" name="A.Bs[3].Id" />
But not this input:
<input type="hidden" id="Bs_3__Id" name="Bs[3].Id" />
One solution would be pass the prefix, A to the partial view: See this answer
A better solution would be to use EditorTemplate instead of Partial View, which would generate correct name for your input field. To use EditorTemplate Rename your partial view to B.cshtml (this should be your class name) and put it under /Views/Shared/EditorTemplates folder... then you can use it like this:
#Html.EditorFor(m => m.Bs)
Check this tutorial for more info about Editor Templates
Also check this question, which is very similar to yours

Obtaining list of objects from client side in asp.net core 2.0

I'm new to asp.net core. I've been reading and I know now how to submit data on a form as an object to the code behind, through post methods. That's fine. But how about sending a list of objects? What I would need to do would be (in this example it would be for an hotel management room types and rate types) something like a grid where I have a list of room types, where each one has a list of rate types. How can I do this? as I've done it, I can pass through the name of the room type (only as one object, though) but not any rates.
I have it like this (models):
public class RateType
{
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class RoomType
{
public string Name { get; set; }
public List<RateType> Rates { get; set; } = new List<RateType>();
}
In the HomeController:
public IActionResult RoomConfig()
{
return View(room);
}
[HttpGet]
public ViewResult SaveRoomConfig()
{
return View("RoomConfig");
}
[HttpPost]
public ViewResult SaveRoomConfig(RoomType room)
{
return View("RoomConfig");
}
And in the view:
#model RoomType
#{
ViewData["Title"] = "RoomConfig";
}
<h2>#Model.Name</h2>
<form class="p-a-1" asp-action="SaveRoomConfig" method="post">
#foreach (var item in Model.Rates)
{
<label asp-for="Name">#item.Name </label>
<input class="form-control" asp-for="Name" />
<input asp-for="#item.IsActive" type="checkbox" />
}
<button type="submit">Send</button>
</form>
So, I send an initial object with some pre-defined values just for testing and they are shown on the page. But then, when I click "send" I can only send the first object (makes sense, since I only have an object parameter, so it will send only the first one. Still, I tried putting a list of RoomType's to see what it did but it shows count as zero. So, how can I do it? Pass a list of objects with another list of objects (nested). Is this possible? It doesn't make sense to make to only have the possibility to pass one and only single object.
Ok, I realized why it wasn't working. I was doing everything right, except for the fact that I was binding items to an empty list which, by definition, didn't have any items. So, instead of a 'foreach', I wrote a 'for' statement, in order to get to the index. That way, I binded the object to the position on the list, instead of a non-existing object. Like this:
#model RoomType
#{
ViewData["Title"] = "RoomConfig";
}
<form class="p-a-1" asp-action="SaveRoomConfig" method="post">
<input asp-for="Name" />
#for (int i=0;i< Model.Rates.Count;i++)
{
<div class=" form-control">
<label asp-for="#Model.Rates[i].Name">#Model.Rates[i].Name </label>
<input asp-for="#Model.Rates[i].Name" />
<input asp-for="#Model.Rates[i].IsActive" type="checkbox" />
</div>
}
<button type="submit">Send</button>
</form>
Of course, if anyone has a better way to do this I'm all ears. :)

Getting values of input fields in MVC

I'm a bit new to MVC, and maybe I'm just misunderstanding something, but I can't figure out how to do the following in an elegant way:
I have the following Entity that I wan't updated:
Model:
public class Entity
{
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public int FieldInt { get; set; }
public DateTime FieldDate { get; set; }
public int FieldOther {get; set; }
}
}
View:
My view displays a bunch textlines with checkboxes attached. The checkboxes are identified by two data-attributes: data-field-int and data-field-date, which is something along the following.
#html.BeginForm("Confirm", "Home", FormMethod.Post) {
...
<input type='checkbox' data-field-int="1" data-field-date="2014/01/01" />
<input type='checkbox' data-field-int="1" data-field-date="2014/02/02" />
<input type='checkbox' data-field-int="1" data-field-date="2014/03/03" />
...
<button id="ConfirmButton" type="submit" class="btn btn-primary">Confirm</button>
}
What I want in the controller is when the ConfirmButton is pressed, create an Entity object for each checkbox that is checked with the value of fieldInt and fieldDate populated with data-field-int and data-field-date attributes respectively.
I can do it by making the controller action take FormCollection as input and by putting a name attribute on the checkboxes with a concatenation of fieldInt and fieldDate and then seperating them in the controller and updating the db. But it seems like there would be a better way, since MVC is so smart with Entity Framework.
I hope you guys can help me understand
Thank you,
Peter
welcome to MVC .
-Using razor engine with model entities is the best practice.
-In the above mentioned code you need to set something like this
#using ( Html.BeginForm("Confirm", "Home", FormMethod.Post))
-As you are new try using strongly typed views with selected templates which generates razor code for you i.e you can analyse deep
-Finally just use model x as parameter to you [HttpPost] action method and convert these entities to you Entity framework entities and save in DB
Additionally :
Data attributes are not included in the data that's posted with the form, so there is no way to read them in your controller action. Try using a hidden field instead
Like
#Html.HiddenFor(m => m.FieldInt) or
<input type="hidden" name="FieldInt" value="1234" />//do similarly for rest
Passing static then #{Model.phoneno = "1234"}
This question consists of two parts:
1.
It is good to specify #model in razor view and use helper methods that take lambda expressions with the model as parameter.
#model MyType
html.Textbox(model => model.FieldOther,...)
Then you create action that takes the model
[HttpPost]
ActionResult MyAction(MyModel model) {
....
}
Mvc will try to create instance of the model and map form fields to the model properties.
2.
You can use entity as model but, believe me, so called Data transfer Objects and/or View Models were created for a reason and as application evolves, single views evolve too to manipulate data from many related database entities.

Why DefaultModelBinder in MVC3 does not Bind?

I do not understand why the DefaultModelBinder in MVC3 does not map the Form Post Data to my action method. I have the following ViewModels:
public class DisplayExcelTableViewModel
{
public string UploadedFileName { get; set; }
public List<string> TableHeaders { get; set; }
public List<TableRowViewModel> TableRows { get; set; }
}
public class TableRowViewModel
{
public List<string> TableColumns { get; set; }
}
They are displayed in a (partial) View using DisplayTemplates:
#using (Html.BeginForm("SubmitExcel", "Home", FormMethod.Post))
{
<fieldset>
<table>
<tr>
<th>#Html.DisplayFor(m => m.TableHeaders)</th>//<input id="TableHeaders_0_" name="TableHeaders[0]" type="text" value="Opportunity Id" />
</tr>
<tr>
<td>#Html.DisplayFor(m => m.TableRows)</td>//<input id="TableRows_0__TableColumns_0_" name="TableRows[0].TableColumns[0]" type="text" value="1-H7PKD9" />
</tr>
</table>
<input type="submit" value="Send" />
</fieldset>
}
And the action method looks like this:
public ActionResult SubmitExcel(DisplayExcelTableViewModel excelTable)
To try whether it worked just with one TableRows I tried:
public ActionResult SubmitExcel([Bind(Prefix = "TableRows")] TableRowViewModel TableRows)
to test I also tried to put List<TableRows> and take out the Bind attribute. It does not work.
I got a runtime exception:
"System.MissingMethodException: No parameterless constructor defined for this object."
May you tell me what I am doing wrong?
Thanks Francesco
The problem is that my ViewModels DID NOT have a parameterless constructor, which is what the Default Model Binder looks for(uses .NET’s Activator.CreateInstance() method, which relies on those types having public parameterless constructors).The solutions in this case are two:
1) Add a parameterless constructor to the ViewModel and the other custom classes wrapped inside it.
2) Create a custom model binder that covers also the case of your ViewModel
Thanks
Source: Pro ASP.NET MVC2 Framework (2nd Edition)
Have you checked (for example with Firebug) whether are form values being posted to the server? I'm asking because Html.DisplayFor usually renders display elements, whereas for posting values you usually have to use Html.EditorFor.

Categories

Resources