1

I'm having a problem with posting a view that has multiple partial views within it using one submit button

This is my A model,

:UPDATED:

public class AModel
{
    public int AID { get; set; }

    public int BID { get; set; }

    public int CID { get; set; }

    public int ANumber { get; set; }

    public BModel BModel { get; set; }

    public CModel CModel { get; set; }
}

this is my B model

public class BModel
{       
    public int BID { get; set; }
    public int BName { get; set; }

    public IList<DModel> DModel { get; set; }
}    

this is the D model

public class DModel
{
    public int DID { get; set; }

    public int? Number { get; set; }
}

this is the c Model

public class CModel
{
    public int CID { get; private set; }

    public IList<HModel> HModels { get; set; }
}

and so on this is the main view

@model Project.Web.Models.AModel    
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.Partial("CreateB",Model.BModel);
    @Html.Partial("CreateC",Model.CModel);
    <input style="border-radius:4px;width:100px;" type="button" value="next" id="next" class="btn btn-default" />
}

this is the Get action

public ActionResult Create(){
Models.AModel model = new Models.AModel(){
BModel = new BModel(){
DModel = new List<Models.DModel>(){ new Models.DModel() },

CModel = new CModel(){
HModel = new List<HModel>(){ new Models.HModel() },
};
return View(model);
}

this is the Post action

[HttpPost]
public ActionResult Create(Models.AModel model)
{
   //doing things with data 
   //when I reach this point all object are null  
   return RedirectToAction("index", model);
}

public void SetNumber(Models.BModel model){
model.DModel.Add(new Models.DModel());
}

Partial View For BModel, and CModel is similar to BModel Partial view Note: the SetNumber method only create a new Object and add it to the list

@model Project.Web.Models.BModel

@Html.TextBoxItemFor(x => x.BName)

@for ( int i=0; i< Model.DModel.Count; i++){
@Html.TextBoxItemFor( x => x.DModel[i].Number)
 <a id="addNumber" href="/AController/SetNumber/" data-ajax="true" data-
 ajax-method="GET" >Add Another Number</a>
}

What can I do? What Am I missing?

Lucinda
  • 11
  • 3
  • You cannot use `@Html.Partial()` unless you pass the main model to the method, or you specify the `HtmlFieldPrefix` (refer [this answer](http://stackoverflow.com/questions/29808573/getting-the-values-from-a-nested-complex-object-that-is-passed-to-a-partial-view/29809907#29809907)). But the correct approach is to use an `EditorTemplate` for the sub models –  Aug 04 '16 at 23:19

1 Answers1

0

Would be nice to see your partials as well. You should remember to put those partials names (as properties from main model) to "name" attribute of input as well. So for your case your PartialView should contain inputs like these: <input name="AModel.BModel.BID" ... />

UPDATE:

Try to replace your

@Html.Partial("Name", Model.PartialModel)

to

@{ Html.RenderPartial("Name", Model.PartialModel, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = prefix } }); }

where prefix is Model property name (holding that partial model, e. x. "BModel"). So your view for AModel would look like this:

@model Project.Web.Models.AModel
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @{ Html.RenderPartial("CreateB",Model.BModel, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "BModel" } }) }
    @{ Html.RenderPartial("CreateC",Model.CModel, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "CModel" } }) 
    <input style="border-radius:4px;width:100px;" type="button" value="next" id="next" class="btn btn-default" />
}

UPDATE 2:

In order to work with arrays (which you have in your BModel) you will need to modify this prefix a bit, so view for your BModel would contain:

@{
    for (var i = 0; i < Model.DModel.Count(); i++)
    {
        Html.RenderPartial("Name", Model.DModel[i], new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "DModel["+i+"]" } });
    }
}

UPDATE 3:

Here's a complete example for your case (or pretty similar to your) without JavaScript. Controller:

    public ActionResult Test()
    {
        return View("Test1", new AModel());
    }

    [HttpPost]
    public ActionResult Save(AModel model)
    {
        if (model.PostType == "Add")
        {
            model.BModel.DModels.Add(new DModel());

            return View("Test1", model);
        }

        // Do something with this final model

        return View("Test1");
    }

Models:

public class DModel
{
    public string Name { get; set; }
}

public class CModel
{
    public string SomeName { get; set; }
}

public class BModel
{
    public List<DModel> DModels { get; set; }

    public BModel()
    {
        DModels = new List<DModel>();
    }
}

public class AModel
{
    public BModel BModel { get; set; }

    public CModel CModel { get; set; }

    public string PostType { get; set; }

    public AModel()
    {
        BModel = new BModel();
        CModel = new CModel();
    }
}

AModel View:

@model MVC.Models.AModel
@if (Model == null)
{
    <span>Model saved</span>
}
else
{
    using (Html.BeginForm("Save", "Home", FormMethod.Post))
    {
        Html.RenderPartial("Partials/CModel", Model.CModel, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "CModel" } });
        <hr />
        Html.RenderPartial("Partials/BModel", Model.BModel, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "BModel" } });

        <input type="submit" name="PostType" value="Add"/><br />
        <input type="submit" name="PostType" value="Save"/>
    }
}

BModel Partial View:

@model MVC.Models.BModel

@{
    for (var i = 0; i < Model.DModels.Count(); i++)
    {
        Html.RenderPartial("Partials/DModel", Model.DModels[i], new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = $"{ViewData.TemplateInfo.HtmlFieldPrefix}.DModels[{i}]" } });
    }
}

DModel Partial View:

@model MVC.Models.DModel

Name: @Html.EditorFor(x => x.Name) <br/>

CModel Partial View:

@model MVC.Models.CModel

SomeName: @Html.EditorFor(x => x.SomeName) <br/>

If you can use jQuery then those 2 submit inputs could be replaced with some buttons and onclick event handlers which will take current form and post to different actions on controller.

  • This does not provide an answer to the question. Once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](http://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/13213623) – Murray Foxcroft Aug 03 '16 at 15:50
  • This answers question partially, as for more particular answer more details needed. Most common case is missing correct name attribute for HTML tags (that's mentioned in answer) and if author doesn't use them in his code - that might be an issue. If he uses those names - we should be able to check full code to find other possible issue. – Vladyslav Kushnir Aug 03 '16 at 16:06
  • Every Partial View has it's own model Project.Web.Models.PartialModel so I'm using Html.TextBoxItemFor(x => x.PartialID), Should i use the Main Model like this @model Project.Web.Models.MainView ? – Lucinda Aug 03 '16 at 18:43
  • No, partial view model should be the type this partial is going to be used for. If this partial is for BModel then model should be BModel. I've updated my response to make things more clear with examples. – Vladyslav Kushnir Aug 03 '16 at 21:01
  • Thanks it worked! ^_^, I'm not having null any more, but the for loop in the partial view is not working, I'm having the list of Dmodel empty... – Lucinda Aug 04 '16 at 07:58
  • Try to read http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx article about arrays posting. It should help. – Vladyslav Kushnir Aug 04 '16 at 08:09
  • It's now working but I have another problem now. the SetNumber method receives BModel and as I mentioned before adds a new instance to the list of DModel. Problem is the changes I make are NOT reflected on the AModel After the submit. There are somehow not linked. – Lucinda Aug 04 '16 at 08:16
  • I've updated my response to reflect response to your question about arrays. If you want to add some items dynamically to your array you should add them just using front-end part (views) and javascript and then post final model. Modifying existing model the way you trying to do this is impossible as when you passing model yo method `SetNumber` you're not passing your actual model, but copy of them, that's why all modifications won't be reflected on your real model. The only way to achieve those changes to be reflected is to post whole model, modify it and `return View("Create", model)`. – Vladyslav Kushnir Aug 04 '16 at 08:24
  • I did what you suggested, but i don't know if it worked or not so i wanted to add it to the database to check, the button on the AModel Post my data to the action method called "Create" it initializes something in the given model then sends the model to another view Called SView that have @model Project.Web.Models.AModel and i can use my AModel data in new SView Perfectly the problem is when i Post Everything in the SView in another method it seems that my SView can't submit the AModel Data i get nulls is that true ? – Lucinda Aug 04 '16 at 10:01
  • Updated my answer to cover new details. Basically under UPDATE 3 you can find pretty simply example of implementation which works as you probably expecting. – Vladyslav Kushnir Aug 04 '16 at 13:02
  • the "save" action is exactly what i did but the second return i sent the model again and to a different view like this return("newView",model) and the i have a method "SaveSecondView" and i received the AModel but it have nulls ? – Lucinda Aug 04 '16 at 14:19
  • You don't need second view with this approach, the same as second action. I've added in update 3 just complete solution. So if you create clean project and copy this stuff in there - it will work. – Vladyslav Kushnir Aug 04 '16 at 15:18