1

I'm creating multiple forms in a for in my view, the problem comes when I send to the controller on submit and it comes null.

here an example.

@model List<Project.ViewModels.ValidForm>
        @if (Model != null)
    { 
        for (int i = 0; i < Model.Count; i++)
        {
            <div>
                @using (Html.BeginForm("Method", "Controller", FormMethod.Post, new { id = "valForm" + @Model[i].Id }))
                {
                    <div class="col-md-4">
                        <label>Data 1</label><br />
                        @Html.TextBoxFor(m => m[i].Data1, new { @class = "form-control" })
                    </div>
                    <div class="col-md-4">
                        <label>Data 2 options</label><br />
                        @Html.TextBoxFor(m => m[i].Data2.Option1, new { @class = "form-control" })
                    </div>
                    <div>
                        <button type="submit" class="btn btn-success">Save this form</button>
                    </div>

                }
            </div>
        }
    }

And there are the viewmodels.

public class ValidForm{
    public int data1 { get; set; }
    public Data2 data2 {get;set;}
 }

public class Data2{
 public int option1 {get;set;}
 public int option2 {get;set;}
}

and the controller.

[HttpPost]
public ActionResult validaciones(validform vm){
//do something.
    return view();
}
Ziv Weissman
  • 4,400
  • 3
  • 28
  • 61
  • are you looking for separate forms each with a submit button ? If not so, what you can try is to keep the form out of the loop and also the submit button and try posting it. You have to change the parameter of the action method to a list in that case. – Ravi Sankar Rao Jul 26 '17 at 19:40
  • Show your controller and model/VM it is expecting. Usual culprit is when you post data cannot be mapped properly to your expected object. – Travis Acton Jul 26 '17 at 19:59
  • @RaviSankarRao yeah I want to separate forms with his submit button, actually I want to submit just one object of the list. – DanielUltramar Jul 26 '17 at 20:56
  • @TravisActon there is the complete example of what I'm trying to do!, I can get the Object but all his variables are null – DanielUltramar Jul 26 '17 at 21:45
  • You can only ever submit one form at once so this makes no sense, and there is no point degrading performance by generating all that extra html (not to mention a user my complete one or 2 forms and press the submit button on another). Display the collection in a read-only page with a link to either redirect to an edit page, or display a dialog form for the selected object. –  Jul 26 '17 at 22:32
  • Well I had to create a javascript array with all the values that where selected by the user, and then sending to the controller by an Ajax, and saving without problem. I was thinking that if I did it by forms would be easy. thanks to everyone! – DanielUltramar Jul 28 '17 at 18:33
  • Then if you want to edit multiple items, just use a single form with a `for` loop or `EditorTemplate` (refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943)) - using ajax is pointless –  Jul 29 '17 at 05:45
  • well actually I just want to edit one by one, but show all the "interactions pending" to the user, so the user take one of the list, evaluate the data and save, so maybe I could do partial view, searching the data with a parameter and showing the partial view fully with the data, but I was thinking that this way I could reduce code. @StephenMuecke – DanielUltramar Jul 31 '17 at 23:58
  • No, you will be actually increasing the code required (assuming you want it to work correctly and have correct 2-way model binding and validation and the ability to return the view if there are errors) –  Aug 01 '17 at 00:11
  • Thanks so very much, I change totally the scheme of the view, and I put a partial view with the form inside, that carry the model when is invocated with certain parameters, I thought the other mode was a better idea. – DanielUltramar Aug 03 '17 at 15:39

1 Answers1

0

The reason you get null is that your model is an array of ValidForm and not just one.

Change your controller to:

[HttpPost]
public ActionResult validaciones(ValidForm[] vms){
   ValidForm vm = ValidForm[0];
   //do something with vm.
    return view();
}

If you want only one form at a time you can do this: (I've added Name = "PropName", mind the capital N in Name)

Then you're post action should expect a single VM

<div class="col-md-4">
  <label>Data 1</label><br />
  @Html.TextBoxFor(m => m[i].Data1, new { @class = "form-control", Name="Data1" })
</div>

For data2 the name needs to be Data2.option1, ect...

Fully working example:

HomeController:

    public class HomeController : Controller
    {

        public class ValidForm
        {
            public string Id { get; set; }
            public int data1 { get; set; }
            public Data2 data2 { get; set; }
        }

        public class Data2
        {
            public int option1 { get; set; }
            public int option2 { get; set; }
        }


        public ActionResult Index()
        {
            var model = new ValidForm[2] { new ValidForm { }, new ValidForm {data2 = new Data2{}} };
            return PartialView(model);
        }
        [HttpPost]
        public ActionResult Tester(ValidForm model)
        {
            return View("Index", new ValidForm[1] { model });
        }
}

View:

@model MvcApplication1.Controllers.HomeController.ValidForm[]
@if (Model != null)
{ 
    for (int i = 0; i < Model.Count(); i++)
    {
        <div>
            @using (Html.BeginForm("Tester", "Home", FormMethod.Post, new { id = "valForm" + @Model[i].Id }))
            {
                <div class="col-md-4">
                    <label>Data 1</label><br />
                    @Html.TextBoxFor(m => m[i].data1, new { @class = "form-control", Name = "data1"})
                </div>
                <div class="col-md-4">
                    <label>Data 2 options</label><br />
                    @Html.TextBoxFor(m => m[i].data2.option1, new { @class = "form-control", Name = "data2.option1"})
                </div>
                <div>
                    <button type="submit" class="btn btn-success">Save this form</button>
                </div>

            }
        </div>
    }
}

Also note that the input boxes can only be numbers because you have all ints.

Ziv Weissman
  • 4,400
  • 3
  • 28
  • 61
  • But when I do that I get the whole List that I iterate on the view, and I just want to get one of object of that list, this is possible? – DanielUltramar Jul 26 '17 at 21:03
  • @DanielUltramar added example – Ziv Weissman Jul 26 '17 at 21:09
  • I get the object but null. when I catch on the controller public ActionResult validaciones(ValidForm vm){} – DanielUltramar Jul 26 '17 at 21:24
  • The Name property should be identical, including capital letters to the property names, if they are complex you need dot, as example Data2.Option1 @DanielUltramar – Ziv Weissman Jul 26 '17 at 21:39
  • I just change my code and getting the same, I can get the object but all his variables are null. – DanielUltramar Jul 26 '17 at 21:44
  • @DanielUltramar are you 100% sure? I tried it and it works. Post your new code. – Ziv Weissman Jul 26 '17 at 22:12
  • 1
    That will only ever work for the first form (by default collection indexers must start at zero and be consecutive) –  Jul 26 '17 at 22:27
  • @StephenMuecke he doesn't want a collection, he wants a single form. The full working code I supplied will work for multiple forms. You can try the code for yourself. – Ziv Weissman Jul 27 '17 at 06:27
  • No it will not! (you clearly have not tried it yourself). The first form generates form controls with an indexer which is `0` and will bind. All subsequent forms will have indexers 1, 2, 3 etc. The `DefaultModelBinder` will not bind unless the collection indexer starts at zero and is consecutive (unless a property for the `Index` is included). But its is a really bad UI design anyway. –  Jul 27 '17 at 06:31
  • @StephenMuecke A. I tried it myself, that's why I wrote full working example. B. I am not generating ID's, only name. Each form has a unique ID, inside each unique form there are attributes with same name. It is TOTALLY VALID HTML. – Ziv Weissman Jul 27 '17 at 06:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150257/discussion-between-stephen-muecke-and-ziv-weissman). –  Jul 27 '17 at 06:36