List within the model not reaching controller when form is submitted










0















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)

...










share|improve this question



















  • 1





    Does your view specify the model? It would typically be the first line of the view. @model SectionVM

    – Dan Sorensen
    Feb 16 '18 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks

    – LH7
    Feb 16 '18 at 2:17















0















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)

...










share|improve this question



















  • 1





    Does your view specify the model? It would typically be the first line of the view. @model SectionVM

    – Dan Sorensen
    Feb 16 '18 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks

    – LH7
    Feb 16 '18 at 2:17













0












0








0








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)

...










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 16 '18 at 2:16







LH7

















asked Feb 16 '18 at 1:39









LH7LH7

3341411




3341411







  • 1





    Does your view specify the model? It would typically be the first line of the view. @model SectionVM

    – Dan Sorensen
    Feb 16 '18 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks

    – LH7
    Feb 16 '18 at 2:17












  • 1





    Does your view specify the model? It would typically be the first line of the view. @model SectionVM

    – Dan Sorensen
    Feb 16 '18 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks

    – LH7
    Feb 16 '18 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 '18 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 '18 at 1:49














Yes it does, I just included it in the question to avoid confusion, Thanks

– LH7
Feb 16 '18 at 2:17





Yes it does, I just included it in the question to avoid confusion, Thanks

– LH7
Feb 16 '18 at 2:17












2 Answers
2






active

oldest

votes


















2














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.






share|improve this answer

























  • I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?

    – LH7
    Feb 16 '18 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 '18 at 2:55











  • I played around with it a bit more a provided a sample solution to pass that data.

    – Dan Sorensen
    Feb 16 '18 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 '18 at 2:22


















1














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






share|improve this answer
























    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',
    autoActivateHeartbeat: false,
    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
    );



    );













    draft saved

    draft discarded


















    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









    2














    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.






    share|improve this answer

























    • I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?

      – LH7
      Feb 16 '18 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 '18 at 2:55











    • I played around with it a bit more a provided a sample solution to pass that data.

      – Dan Sorensen
      Feb 16 '18 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 '18 at 2:22















    2














    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.






    share|improve this answer

























    • I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?

      – LH7
      Feb 16 '18 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 '18 at 2:55











    • I played around with it a bit more a provided a sample solution to pass that data.

      – Dan Sorensen
      Feb 16 '18 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 '18 at 2:22













    2












    2








    2







    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.






    share|improve this answer















    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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Feb 16 '18 at 5:18

























    answered Feb 16 '18 at 2:44









    Dan SorensenDan Sorensen

    6,425146093




    6,425146093












    • I see that, but is there a way to pass Model.Items as List<SectionItemCollector> or as JSON to the controller?

      – LH7
      Feb 16 '18 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 '18 at 2:55











    • I played around with it a bit more a provided a sample solution to pass that data.

      – Dan Sorensen
      Feb 16 '18 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 '18 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 '18 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 '18 at 2:55











    • I played around with it a bit more a provided a sample solution to pass that data.

      – Dan Sorensen
      Feb 16 '18 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 '18 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 '18 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 '18 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 '18 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 '18 at 2:55













    I played around with it a bit more a provided a sample solution to pass that data.

    – Dan Sorensen
    Feb 16 '18 at 5:39





    I played around with it a bit more a provided a sample solution to pass that data.

    – Dan Sorensen
    Feb 16 '18 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 '18 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 '18 at 2:22













    1














    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






    share|improve this answer





























      1














      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






      share|improve this answer



























        1












        1








        1







        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






        share|improve this answer















        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







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 12 '18 at 10:47

























        answered Nov 12 '18 at 9:54









        mathieu mauronmathieu mauron

        112




        112



























            draft saved

            draft discarded
















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

            Syphilis

            Darth Vader #20