简体   繁体   中英

ASP.NET MVC 5 : Binding all the partial views to the main controller

I am developing a web app using ASP.NET MVC 5. I have multiple partial views and their corresponding models, but only one controller corresponding to the main view, that uses below code to display each of the partial views.

@{Html.RenderPartial("PartialViewName");} 

My question is, how do I pass the form data from all the partial views (including the main view) to the main controller?

Initially, when I added only 1 partial view, I added it's model as a parameter to the main controller's public ActionResult Index(MainModelName model1, PartialModelName pmodel1) . and that worked fine and I could access the data from the partial view in the main controller.

But, when I added the 2nd partial view and updated the public ActionResult Index(...) with an additional partial model parameter, it didn't help, as in the data is not being passed from the partial view to the main controller. though I don't get any error.

My main model class has the object references of the partial models as its data members. From what I read, I need to have a list of models as a parameter to the method in the main controller, but my models are not sub-types.

What's the idiomatic way of handling such a situation?

Thank you!

EDIT1 : Here's the code for a sample app, I get null reference error while accessing mainModel.PartialModel2.Pm2 in MainController.cs, while I can access mainModel.PartialModel1.Pm1 properly.

MainModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class MainModel
    {
        private int mm1;
        private PartialModel1 partialModel1;
        private PartialModel2 partialModel2;

        public int Mm1 { get => mm1; set => mm1 = value; }
        public PartialModel1 PartialModel1 { get => partialModel1; set => partialModel1 = value; }
        public PartialModel2 PartialModel2 { get => partialModel2; set => partialModel2 = value; }
    }
}

PartialModel1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class PartialModel1
    {
        private int pm1;

        public PartialModel1()
        {
        }

        public int Pm1 { get => pm1; set => pm1 = value; }
    }
}

PartialModel2.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class PartialModel2
    {
        private int pm2;

        public PartialModel2()
        {
        }

        public int Pm2 { get => pm2; set => pm2 = value; }
    }
}

Index.cshtml

@model TestApp.Models.MainModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>MainModel</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.Mm1, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Mm1, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Mm1, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @{ Html.RenderPartial("Partial1", Model);  }
                @{ Html.RenderPartial("Partial2", Model);  }
            </div>

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
                    }

    @*<div>
            @Html.ActionLink("Back to List", "Index")
        </div>*@
</body>
</html>

Partial1.cshtml

@model TestApp.Models.MainModel

@using (Html.BeginForm("MainModel")) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>PartialModel1</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.PartialModel1.Pm1, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PartialModel1.Pm1, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.PartialModel1.Pm1, "", new { @class = "text-danger" })
            </div>
        </div>

        @*<div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>*@
    </div>
}

@*<div>
    @Html.ActionLink("Back to List", "Index")
</div>*@

Partial2.cshtml

@model TestApp.Models.MainModel

@using (Html.BeginForm("MainModel")) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>PartialModel2</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.PartialModel2.Pm2, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PartialModel2.Pm2, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.PartialModel2.Pm2, "", new { @class = "text-danger" })
            </div>
        </div>

        @*<div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>*@
    </div>
}

@*<div>
    @Html.ActionLink("Back to List", "Index")
</div>*@

MainController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TestApp.Models;

namespace TestApp.Controllers
{
    public class MainController : Controller
    {
        // GET: Main

        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(MainModel mainModel)
        {
            //Access mainModel.PartialModel1.Pm1
            //Access mainModel.PartialModel2.Pm2 

            string msg = mainModel.PartialModel1.Pm1 + " " + mainModel.PartialModel2.Pm2;
            ViewBag.message = msg;


            return View("Success");
        }
    }
}

EDIT 2: If I just keep it @{ Html.RenderPartial("Partial2"); } @{ Html.RenderPartial("Partial2"); } , I get this error - The model item passed into the dictionary is of type 'TestApp.Models.Main', but this dictionary requires a model item of type 'TestApp.Models.Partial2'.

MainController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TestApp.Models;

namespace TestApp.Controllers
{
    public class MainController : Controller
    {
        // GET: Main

        [HttpGet]
        public ActionResult Index()
        {
            return View(new Main() { Partial1 = new Partial1(), Partial2 = new Partial2() });

        }

        [HttpPost]
        public ActionResult Index(Main mainModel)
        {
            //Access mainModel.PartialModel1.Pm1
            //Access mainModel.PartialModel2.Pm2            

            string msg = mainModel.Partial1.Pm1 + " " + mainModel.Partial2.Pm2;
            ViewBag.message = msg;

            return View("Success");
        }
    }
}

Main.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Main
    {
        public int Mm1 { get; set; }
        public Partial1 Partial1 { get; set; }
        public Partial2 Partial2 { get; set; }
    }
}

Partial1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Partial1
    {
        public Partial1()
        {
        }
        public int Pm1 { get; set; }
    }
}

Partial2.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Partial2
    {
        public Partial2()
        {
        }
        public int Pm2 { get; set; }
    }
}

Index.cshtml

@model TestApp.Models.Main

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>MainModel</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.Mm1, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Mm1, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Mm1, "", new { @class = "text-danger" })
                </div>               

            </div>

            @{ Html.RenderPartial("Partial2");  }
            @{ Html.RenderPartial("Partial1");  }

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
                    }

    @*<div>
            @Html.ActionLink("Back to List", "Index")
        </div>*@
</body>
</html>

Partial1.cshtml

@model TestApp.Models.Partial1


@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>PartialModel1</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Pm1, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Pm1, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Pm1, "", new { @class = "text-danger" })
        </div>
    </div>


</div>


@*<div>
        @Html.ActionLink("Back to List", "Index")
    </div>*@

Partial2.cshtml

@model TestApp.Models.Partial2


@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>PartialModel2</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.Pm2, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Pm2, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Pm2, "", new { @class = "text-danger" })
        </div>
    </div>
</div>


@*<div>
        @Html.ActionLink("Back to List", "Index")
    </div>*@

You can change your code:

public ActionResult Index(MainModelName model1, PartialModelName pmodel1)

To

public ActionResult Index(NewMainModelName  newModel)

In class NewMainModelName you can woke like this:

   public class NewMainModelName
    {
        public MainModelName model1 { get; set; }
        public PartialModelName pmodel1{ get; set; }
    }

There's a bunch of stuff here you should change. For starters, you don't need to use the form of getter you're using.

Instead of

private int pm1;
public int Pm1 { get => pm1; set => pm1 = value; }

You only have to have this:

public int Pm1 { get; set; }

You don't need the private backing variable.

You only need to populate the get and set methods if you are going to actually provide methods that do something.

Second, you need to instantiate the PartialModel's. It doesn't matter a lot in this case, since you are not referencing any actual data, just metadata.. but as soon as you add real data it can cause issues.

[HttpGet]
public ActionResult Index()
{
    return View(new MainModel() { PartialModel1 = new PartialModel1(), PartialModel2 = new PartialModel2 });
}

Next, I'm not sure what you're trying to accomplish with this, but it's probably not what you think it's doing:

@using (Html.BeginForm("MainModel")) 

What you have here is nested forms, and that's not legal HTML.

Instead, you should remove the forms from the partials, even if it would be legal then a form will only submit the data within that form. This is your main problem.

also, remove the submit buttons in the partials. Just have the one over-arching form and submit button in the Index.cshtml.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM