List within the model not reaching controller when form is submitted
up vote
0
down vote
favorite
Why when I submit the form, the controller does not receive any data from Model.Items?
I am using asp.net core mvc 2.0
This is my View-Model
public class SectionVM
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<SectionItemCollector> Items { get; set; }
}
public class SectionItemCollector
{
public bool IsSelected { get; set; }
public long ItemId { get; set; }
public string ItemName { get; set; }
}
Here I create a new view-model, populate it with data and send it to the view. The view receives the view model and I am currently able of accessing all the data in the view-model without any problems.
// Controller Actions
// GET: MenuSections/Create
public async Task<IActionResult> Create()
{
var viewModel = new SectionVM();
var allItems = await _context.MenuItem.ToListAsync();
var vmItems = new List<SectionItemCollector>();
foreach (var i in allItems)
{
vmItems.Add(new SectionItemCollector() { IsSelected = false, ItemId = i.Id, ItemName = i.Name });
}
viewModel.Items = vmItems;
return View(viewModel);
}
My view shows the data contained in the view model and user can choose to select some Items
// View
@model ROO_App.Models.SectionVM
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
<input asp-for="@item.IsSelected" type="checkbox" class="form-check-input" id="@item.ItemId"/>
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
when the form is submitted Items = null
// POST: MenuSections/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items")] SectionVM menuSection)
{
...
}
razor asp.net-core-mvc model-binding
add a comment |
up vote
0
down vote
favorite
Why when I submit the form, the controller does not receive any data from Model.Items?
I am using asp.net core mvc 2.0
This is my View-Model
public class SectionVM
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<SectionItemCollector> Items { get; set; }
}
public class SectionItemCollector
{
public bool IsSelected { get; set; }
public long ItemId { get; set; }
public string ItemName { get; set; }
}
Here I create a new view-model, populate it with data and send it to the view. The view receives the view model and I am currently able of accessing all the data in the view-model without any problems.
// Controller Actions
// GET: MenuSections/Create
public async Task<IActionResult> Create()
{
var viewModel = new SectionVM();
var allItems = await _context.MenuItem.ToListAsync();
var vmItems = new List<SectionItemCollector>();
foreach (var i in allItems)
{
vmItems.Add(new SectionItemCollector() { IsSelected = false, ItemId = i.Id, ItemName = i.Name });
}
viewModel.Items = vmItems;
return View(viewModel);
}
My view shows the data contained in the view model and user can choose to select some Items
// View
@model ROO_App.Models.SectionVM
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
<input asp-for="@item.IsSelected" type="checkbox" class="form-check-input" id="@item.ItemId"/>
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
when the form is submitted Items = null
// POST: MenuSections/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items")] SectionVM menuSection)
{
...
}
razor asp.net-core-mvc model-binding
1
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
Why when I submit the form, the controller does not receive any data from Model.Items?
I am using asp.net core mvc 2.0
This is my View-Model
public class SectionVM
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<SectionItemCollector> Items { get; set; }
}
public class SectionItemCollector
{
public bool IsSelected { get; set; }
public long ItemId { get; set; }
public string ItemName { get; set; }
}
Here I create a new view-model, populate it with data and send it to the view. The view receives the view model and I am currently able of accessing all the data in the view-model without any problems.
// Controller Actions
// GET: MenuSections/Create
public async Task<IActionResult> Create()
{
var viewModel = new SectionVM();
var allItems = await _context.MenuItem.ToListAsync();
var vmItems = new List<SectionItemCollector>();
foreach (var i in allItems)
{
vmItems.Add(new SectionItemCollector() { IsSelected = false, ItemId = i.Id, ItemName = i.Name });
}
viewModel.Items = vmItems;
return View(viewModel);
}
My view shows the data contained in the view model and user can choose to select some Items
// View
@model ROO_App.Models.SectionVM
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
<input asp-for="@item.IsSelected" type="checkbox" class="form-check-input" id="@item.ItemId"/>
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
when the form is submitted Items = null
// POST: MenuSections/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items")] SectionVM menuSection)
{
...
}
razor asp.net-core-mvc model-binding
Why when I submit the form, the controller does not receive any data from Model.Items?
I am using asp.net core mvc 2.0
This is my View-Model
public class SectionVM
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<SectionItemCollector> Items { get; set; }
}
public class SectionItemCollector
{
public bool IsSelected { get; set; }
public long ItemId { get; set; }
public string ItemName { get; set; }
}
Here I create a new view-model, populate it with data and send it to the view. The view receives the view model and I am currently able of accessing all the data in the view-model without any problems.
// Controller Actions
// GET: MenuSections/Create
public async Task<IActionResult> Create()
{
var viewModel = new SectionVM();
var allItems = await _context.MenuItem.ToListAsync();
var vmItems = new List<SectionItemCollector>();
foreach (var i in allItems)
{
vmItems.Add(new SectionItemCollector() { IsSelected = false, ItemId = i.Id, ItemName = i.Name });
}
viewModel.Items = vmItems;
return View(viewModel);
}
My view shows the data contained in the view model and user can choose to select some Items
// View
@model ROO_App.Models.SectionVM
...
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
<input asp-for="@item.IsSelected" type="checkbox" class="form-check-input" id="@item.ItemId"/>
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
when the form is submitted Items = null
// POST: MenuSections/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items")] SectionVM menuSection)
{
...
}
razor asp.net-core-mvc model-binding
razor asp.net-core-mvc model-binding
edited Feb 16 at 2:16
asked Feb 16 at 1:39
LH7
3241411
3241411
1
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17
add a comment |
1
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17
1
1
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17
add a comment |
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
Cause:
No variable named "Items" is being sent to the server since the form does not include an input named "Items". Instead an array named "item.IsSelected" is being sent by the checkbox inputs.
In your view the following line: (showing the important part)
<input asp-for="@item.IsSelected"
is scaffolding a HTML input field that looks like this: (showing the important part)
<input type="checkbox" name="item.IsSelected" value="true">
With Google Chrome, press F12 before you submit the form. Look at the Network tab > Headers > Form data, you can confirm that it submitted the following field for each checked item.
item.IsSelected:true
So the controller is not receiving an input named "Items". it is receiving a string array called "item.IsSelected" with the default value of "true".
Working towards a solution:
You can override the default name by providing the optional 'name' parameter to the input tag.
<input asp-for="@item.IsSelected" name="Items"
You are also setting the Id attribute. In this context, the Id is used for HTML and is not submitted with the form. Only the value of the checkbox is submitted.
id="@item.ItemId" />
If you want to submit the checked IDs back as an array, set it as the value, not the id.
value="@item.ItemId" />
If you prefer to use the ItemName as the value:
value="@item.ItemName" />
However the HTML checkbox input will not allow you to pass both ItemName and Id vales as it only passes a single value for selected items.
The MVC controller will not be able to map the string called "Items" to your SectionItemCollector class as the types do not match. It may detect the count of Items in the array, but the values will remain null.
With that limitation, it may be best to serialize the form data into a JSON object and post via AJAX.
Solving it with a Form Post
To solve with a Form Post, you will need to make a few changes to bind and process the form data:
Add the following properties to your class SectionVM:
public string CheckedItems { get; set; }
public string CheckedValues { get; set; }
Update Create.cshtml to pass the isChecked and ID as part of the checkbox value. Add a hidden input to pass the ItemName.
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
Finally, update your controller action to collect the checkbox and hidden values and re-combine them into the expected object:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
Maybe Asp.Net has another way to abstract away the complexity of submitting a checkbox with multiple properties. If so, I hope someone submits that answer too.
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
add a comment |
up vote
1
down vote
it's my first post, so sorry in advance (especially for my English)
You can use tag helper for collection and get directly model when post form.
this is a little example but i think its the best solution to have a view and controller code clean and standard :
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
so just use the asp-for like another property
and in controller your collection have directly binded elements after post
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f48818986%2flistt-within-the-model-not-reaching-controller-when-form-is-submitted%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
Cause:
No variable named "Items" is being sent to the server since the form does not include an input named "Items". Instead an array named "item.IsSelected" is being sent by the checkbox inputs.
In your view the following line: (showing the important part)
<input asp-for="@item.IsSelected"
is scaffolding a HTML input field that looks like this: (showing the important part)
<input type="checkbox" name="item.IsSelected" value="true">
With Google Chrome, press F12 before you submit the form. Look at the Network tab > Headers > Form data, you can confirm that it submitted the following field for each checked item.
item.IsSelected:true
So the controller is not receiving an input named "Items". it is receiving a string array called "item.IsSelected" with the default value of "true".
Working towards a solution:
You can override the default name by providing the optional 'name' parameter to the input tag.
<input asp-for="@item.IsSelected" name="Items"
You are also setting the Id attribute. In this context, the Id is used for HTML and is not submitted with the form. Only the value of the checkbox is submitted.
id="@item.ItemId" />
If you want to submit the checked IDs back as an array, set it as the value, not the id.
value="@item.ItemId" />
If you prefer to use the ItemName as the value:
value="@item.ItemName" />
However the HTML checkbox input will not allow you to pass both ItemName and Id vales as it only passes a single value for selected items.
The MVC controller will not be able to map the string called "Items" to your SectionItemCollector class as the types do not match. It may detect the count of Items in the array, but the values will remain null.
With that limitation, it may be best to serialize the form data into a JSON object and post via AJAX.
Solving it with a Form Post
To solve with a Form Post, you will need to make a few changes to bind and process the form data:
Add the following properties to your class SectionVM:
public string CheckedItems { get; set; }
public string CheckedValues { get; set; }
Update Create.cshtml to pass the isChecked and ID as part of the checkbox value. Add a hidden input to pass the ItemName.
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
Finally, update your controller action to collect the checkbox and hidden values and re-combine them into the expected object:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
Maybe Asp.Net has another way to abstract away the complexity of submitting a checkbox with multiple properties. If so, I hope someone submits that answer too.
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
add a comment |
up vote
2
down vote
accepted
Cause:
No variable named "Items" is being sent to the server since the form does not include an input named "Items". Instead an array named "item.IsSelected" is being sent by the checkbox inputs.
In your view the following line: (showing the important part)
<input asp-for="@item.IsSelected"
is scaffolding a HTML input field that looks like this: (showing the important part)
<input type="checkbox" name="item.IsSelected" value="true">
With Google Chrome, press F12 before you submit the form. Look at the Network tab > Headers > Form data, you can confirm that it submitted the following field for each checked item.
item.IsSelected:true
So the controller is not receiving an input named "Items". it is receiving a string array called "item.IsSelected" with the default value of "true".
Working towards a solution:
You can override the default name by providing the optional 'name' parameter to the input tag.
<input asp-for="@item.IsSelected" name="Items"
You are also setting the Id attribute. In this context, the Id is used for HTML and is not submitted with the form. Only the value of the checkbox is submitted.
id="@item.ItemId" />
If you want to submit the checked IDs back as an array, set it as the value, not the id.
value="@item.ItemId" />
If you prefer to use the ItemName as the value:
value="@item.ItemName" />
However the HTML checkbox input will not allow you to pass both ItemName and Id vales as it only passes a single value for selected items.
The MVC controller will not be able to map the string called "Items" to your SectionItemCollector class as the types do not match. It may detect the count of Items in the array, but the values will remain null.
With that limitation, it may be best to serialize the form data into a JSON object and post via AJAX.
Solving it with a Form Post
To solve with a Form Post, you will need to make a few changes to bind and process the form data:
Add the following properties to your class SectionVM:
public string CheckedItems { get; set; }
public string CheckedValues { get; set; }
Update Create.cshtml to pass the isChecked and ID as part of the checkbox value. Add a hidden input to pass the ItemName.
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
Finally, update your controller action to collect the checkbox and hidden values and re-combine them into the expected object:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
Maybe Asp.Net has another way to abstract away the complexity of submitting a checkbox with multiple properties. If so, I hope someone submits that answer too.
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
Cause:
No variable named "Items" is being sent to the server since the form does not include an input named "Items". Instead an array named "item.IsSelected" is being sent by the checkbox inputs.
In your view the following line: (showing the important part)
<input asp-for="@item.IsSelected"
is scaffolding a HTML input field that looks like this: (showing the important part)
<input type="checkbox" name="item.IsSelected" value="true">
With Google Chrome, press F12 before you submit the form. Look at the Network tab > Headers > Form data, you can confirm that it submitted the following field for each checked item.
item.IsSelected:true
So the controller is not receiving an input named "Items". it is receiving a string array called "item.IsSelected" with the default value of "true".
Working towards a solution:
You can override the default name by providing the optional 'name' parameter to the input tag.
<input asp-for="@item.IsSelected" name="Items"
You are also setting the Id attribute. In this context, the Id is used for HTML and is not submitted with the form. Only the value of the checkbox is submitted.
id="@item.ItemId" />
If you want to submit the checked IDs back as an array, set it as the value, not the id.
value="@item.ItemId" />
If you prefer to use the ItemName as the value:
value="@item.ItemName" />
However the HTML checkbox input will not allow you to pass both ItemName and Id vales as it only passes a single value for selected items.
The MVC controller will not be able to map the string called "Items" to your SectionItemCollector class as the types do not match. It may detect the count of Items in the array, but the values will remain null.
With that limitation, it may be best to serialize the form data into a JSON object and post via AJAX.
Solving it with a Form Post
To solve with a Form Post, you will need to make a few changes to bind and process the form data:
Add the following properties to your class SectionVM:
public string CheckedItems { get; set; }
public string CheckedValues { get; set; }
Update Create.cshtml to pass the isChecked and ID as part of the checkbox value. Add a hidden input to pass the ItemName.
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
Finally, update your controller action to collect the checkbox and hidden values and re-combine them into the expected object:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
Maybe Asp.Net has another way to abstract away the complexity of submitting a checkbox with multiple properties. If so, I hope someone submits that answer too.
Cause:
No variable named "Items" is being sent to the server since the form does not include an input named "Items". Instead an array named "item.IsSelected" is being sent by the checkbox inputs.
In your view the following line: (showing the important part)
<input asp-for="@item.IsSelected"
is scaffolding a HTML input field that looks like this: (showing the important part)
<input type="checkbox" name="item.IsSelected" value="true">
With Google Chrome, press F12 before you submit the form. Look at the Network tab > Headers > Form data, you can confirm that it submitted the following field for each checked item.
item.IsSelected:true
So the controller is not receiving an input named "Items". it is receiving a string array called "item.IsSelected" with the default value of "true".
Working towards a solution:
You can override the default name by providing the optional 'name' parameter to the input tag.
<input asp-for="@item.IsSelected" name="Items"
You are also setting the Id attribute. In this context, the Id is used for HTML and is not submitted with the form. Only the value of the checkbox is submitted.
id="@item.ItemId" />
If you want to submit the checked IDs back as an array, set it as the value, not the id.
value="@item.ItemId" />
If you prefer to use the ItemName as the value:
value="@item.ItemName" />
However the HTML checkbox input will not allow you to pass both ItemName and Id vales as it only passes a single value for selected items.
The MVC controller will not be able to map the string called "Items" to your SectionItemCollector class as the types do not match. It may detect the count of Items in the array, but the values will remain null.
With that limitation, it may be best to serialize the form data into a JSON object and post via AJAX.
Solving it with a Form Post
To solve with a Form Post, you will need to make a few changes to bind and process the form data:
Add the following properties to your class SectionVM:
public string CheckedItems { get; set; }
public string CheckedValues { get; set; }
Update Create.cshtml to pass the isChecked and ID as part of the checkbox value. Add a hidden input to pass the ItemName.
<div class="form-group">
@{
foreach (var item in Model.Items)
{
<div class="row">
<div class="col-sm-1">
@* If checked, the value will be posted as the CheckedItems string *@
<input asp-for="@item.IsSelected" name="CheckedItems" type="checkbox" class="form-check-input" value="@item.ItemId" />
@* posting a hidden value as the CheckedValues string *@
<input name="CheckedValues" type="hidden" value="@item.ItemName" />
</div>
<div class="col-sm-4">@item.ItemName</div>
</div>
}
}
</div>
Finally, update your controller action to collect the checkbox and hidden values and re-combine them into the expected object:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Description, Items, CheckedItems, CheckedValues")] SectionVM menuSection)
{
// convert CheckedItems and CheckedValues into List<SectionItemCollector>
var selectedItems = new List<SectionItemCollector>();
for (int i = 0; i < menuSection.CheckedItems.Length; i++)
{
// if the checked ID is number, find the related ItemName.
long id;
if (long.TryParse(menuSection.CheckedItems[i], out id))
{
var item = new SectionItemCollector
{
ItemId = id,
// inferred as HTML only posts checkboxes that are selected.
IsSelected = true,
// get the ItemName with the same index.
ItemName = menuSection.CheckedValues[i]
};
selectedItems.Add(item);
}
}
var viewmodel = new SectionVM()
{
Items = selectedItems
};
// TODO: Set the breakpoint to the next line and inspect viewmodel to see your result.
return View(viewmodel);
}
Maybe Asp.Net has another way to abstract away the complexity of submitting a checkbox with multiple properties. If so, I hope someone submits that answer too.
edited Feb 16 at 5:18
answered Feb 16 at 2:44
Dan Sorensen
6,405136093
6,405136093
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
add a comment |
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?
– LH7
Feb 16 at 2:53
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I also tried to bind the properties this way: public async Task<IActionResult> Create([Bind("Id,Name,Description")] SectionVM menuSection, List<SectionItemCollector> Items)
– LH7
Feb 16 at 2:55
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
I played around with it a bit more a provided a sample solution to pass that data.
– Dan Sorensen
Feb 16 at 5:39
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
That one works fine. In the end, I just passed the id of the selected items, so I can use it to get the item from the data context. Thanks
– LH7
Feb 17 at 2:22
add a comment |
up vote
1
down vote
it's my first post, so sorry in advance (especially for my English)
You can use tag helper for collection and get directly model when post form.
this is a little example but i think its the best solution to have a view and controller code clean and standard :
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
so just use the asp-for like another property
and in controller your collection have directly binded elements after post
add a comment |
up vote
1
down vote
it's my first post, so sorry in advance (especially for my English)
You can use tag helper for collection and get directly model when post form.
this is a little example but i think its the best solution to have a view and controller code clean and standard :
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
so just use the asp-for like another property
and in controller your collection have directly binded elements after post
add a comment |
up vote
1
down vote
up vote
1
down vote
it's my first post, so sorry in advance (especially for my English)
You can use tag helper for collection and get directly model when post form.
this is a little example but i think its the best solution to have a view and controller code clean and standard :
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
so just use the asp-for like another property
and in controller your collection have directly binded elements after post
it's my first post, so sorry in advance (especially for my English)
You can use tag helper for collection and get directly model when post form.
this is a little example but i think its the best solution to have a view and controller code clean and standard :
<div class="row">
@{
foreach (var element in Model.Elements)
{
int index = Model.Elements.IndexOf(element);
<input type="text" asp-for="Elements[index].Text" />
<input type="checkbox" asp-for="Elements[index].IsSelected" />
}
}
</div>
so just use the asp-for like another property
and in controller your collection have directly binded elements after post
edited Nov 12 at 10:47
answered Nov 12 at 9:54
mathieu mauron
112
112
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f48818986%2flistt-within-the-model-not-reaching-controller-when-form-is-submitted%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Does your view specify the model? It would typically be the first line of the view. @model SectionVM
– Dan Sorensen
Feb 16 at 1:49
Yes it does, I just included it in the question to avoid confusion, Thanks
– LH7
Feb 16 at 2:17