Posting to another model from a form in ASP.NET MVC

19,510

Solution 1

Yes, you can strongly type a view to one model and POST it to another model.

In doing so you have two options:

  1. Manually provide correct names for each input field, so that the default binder will understand it and create the model (example).

    While this works, it also means you have to keep an eye on typos and you will not get any compile-time errors if you misspell a property name.

  2. Manually create a HTML helper in the view bound to the new model type. It will then properly generate the HTML for you.

    In order to construct the helper, you need a wrapper object that would expose the instance of your model in the form of the IViewDataContainer interface. You can define that wrapper anywhere, including the model itself:

    public class NewModel
    {
      public int ID { get; set; }
      public int UserID { get; set; }
      public string Description { get; set; }
    
      public class WrapperForHtmlHelper : System.Web.Mvc.IViewDataContainer
      {
        public System.Web.Mvc.ViewDataDictionary ViewData { get; set; }
    
        public WrapperForHtmlHelper(NewModel value)
        {
            this.ViewData = new System.Web.Mvc.ViewDataDictionary<NewModel>(value);
        }
      }
    }
    

    Then in a view you create a helper bound to an instance of NewModel:

    var ModelToPost = new YourApp.Models.NewModel() { ID = 1, UserID = 43, Description = "" }
    
    var hlp = new HtmlHelper<YourApp.Models.NewModel>
             (this.ViewContext,
              new YourApp.Models.NewModel.WrapperForHtmlHelper(ModelToPost)
             );
    

    And then you use the helper as usual:

    @hlp.HiddenFor(m => m.ID)
    @hlp.HiddenFor(m => m.UserID)
    @hlp.TextAreaFor(m => m.Description)
    

    Then your PartialViewResult Add(ViewModels.NewModel model) will properly receive the data.

Solution 2

You have a discrepancy between the name of your model and your action. In the example you have shown the model is called Add whereas in your action you are using ViewModels.NewModel. Even worse, your view is strongly typed to a model called Car. Messy all this.

So start by defining a correct view model:

public class CarViewModel
{
    public int ID { get; set; }
    public int UserID { get; set; }
    public string Description { get; set; }
}

and then a controller:

public class CarsController: Controller
{
    public ActionResult Add()
    {
        var model = new CarViewModel
        {
            // don't ask me, those are the values you hardcoded in your view
            ID = 1,
            UserID = 44,
        };
        return View(model);
    }   

    [HttpPost]
    public PartialViewResult Add(CarViewModel model)
    {
        ...
    }
}

and a corresponding strongly typed view to your view model:

@model CarViewModel
@using (Html.BeginForm())
{
    @Html.HiddenFor(x => x.ID)
    @Html.HiddenFor(x => x.UserID)
    @Html.TextAreaFor(x => x.Description)
    <button type="submit">Add</button>
}

Solution 3

My question is can I post data to NewModel from my form? 

The short answer is yes you can post the form to any controller action on any controller related to any Model in your application.

For example, for your form to post to the "Add" action on the NewModel controller:

@using (Html.BeginForm("Add", "NewModel"))
    {
        @Html.Hidden("ID", "1")
        @Html.Hidden("UserID", "44")
        @Html.TextArea("Description")
    }

Since your view is strongly typed to your Car model, You can either change this and send a ViewModel to your view whose type matches the model your updating (as Darin demonstrated), or you'll need to map the post data from Car onto NewModel in your controller:

On the CarController's Add action (Post) :

[HttpPost]
public PartialViewResult Add(Car model)
{
    //now map attribute values from the Car model onto 
    //corresponding attributes of an instance of NewModel
    NewModel new = new NewModel();
    new.ID = model.ID;
    new.UserID = model.UserID;
    new.Desc = model.Description;
    //etc...

    //update your model/db
    _db.Add(new);

    //Redirect however you wish...
}

Also, check out AutoMapper, an object-to-object mapper which automates mapping of ViewModels onto Models and vice versa.

Solution 4

Your view is set up to use a model of type Project.Car however your action method takes a model of type ViewModels.NewModel, but also your model class posted is of type Add?

Change them all to match (assuming Add is correct):

View:

@model Add

Controller:

[HttpPost]
public PartialViewResult Add(Add model)
Share:
19,510

Related videos on Youtube

Alex
Author by

Alex

Updated on September 15, 2022

Comments

  • Alex
    Alex over 1 year

    If I have a view that has a model, lets say Car..

    @model Project.Car
    

    inside that view I want to create a form that sends data to a new model

        @using (Html.BeginForm("Add", "Controller"))
        {
            @Html.Hidden("ID", "1")
            @Html.Hidden("UserID", "44")
            @Html.TextArea("Description")
        }
    

    I've noticed that if my action is defined with my ViewModel it does not work (model is always null):

        [HttpPost]
        public PartialViewResult Add(ViewModels.NewModel model)
    

    However, if I use a FormCollection it works:

        [HttpPost]
        public PartialViewResult Add(FormCollection formCollection)
    

    Here is the ViewModel:

    public class NewModel
    {
        public int ID { get; set; }
        public int UserID { get; set; }
        public string Description { get; set; }
    }
    

    My question is can I post data to NewModel from my form? The View that it sits on is correct to be tied to Project.Car. Its a small form on the page that needs to post a different set of data that has nothing to do with Project.Car.

  • Alex
    Alex about 11 years
    My apologies, I posted this while working on a feature quickly and had to change the names of the actions and controllers for sample purposes. I have corrected the issue.
  • Ken
    Ken over 3 years
    My friend I wish I could give you a big slab of ribs for this answer - it is awesome.I made it Generic - because your code here is awesome.(see if this fits) public class ModelWrapper<T> : System.Web.Mvc.IViewDataContainer where T : class { public System.Web.Mvc.ViewDataDictionary ViewData { get; set; } public ModelWrapper(T value){this.ViewData = new System.Web.Mvc.ViewDataDictionary<T>(value);} }