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)
{
...
}









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 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks
    – LH7
    Feb 16 at 2:17















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)
{
...
}









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 at 1:49












  • Yes it does, I just included it in the question to avoid confusion, Thanks
    – LH7
    Feb 16 at 2:17













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)
{
...
}









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 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














  • 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












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.






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 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


















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






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',
    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








    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.






    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 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















    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.






    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 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













    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.






    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 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


















    • 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












    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






    share|improve this answer



























      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






      share|improve this answer

























        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






        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 at 10:47

























        answered Nov 12 at 9:54









        mathieu 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.





            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.




            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

            Guess what letter conforming each word

            Port of Spain

            Run scheduled task as local user group (not BUILTIN)