tags:

views:

1194

answers:

3

Hi,

I am trying to implement my Edit action methods with strongly typed view which receives a custom-shaped ViewModel class. In other words, I want a strongly typed ViewModel which contains the Linq entity that should be edited plus a few other objects that should be displayed in the View.

I can see the view when calling the GET Edit action method, but the strongly typed POST action method receives only a ViewModel class with null parameters and I can't figure out how to retrieve the POST parameters.

The View Model looks as follows:

//my custom-shaped ViewModel
public class CustomersFormViewModel
{
    public SelectList AccountTypesDropDownBox;
    public SelectList CountriesDropDownBox;
    public Customer Customer;
}

The action method looks as follows:

//
// GET: /CustomersController/Edit   

public ActionResult Edit(int ID)
{
   var model = new CustomersFormViewModel
                    {
                        Customer = repository.Load(ID.Value),
                        CountriesDropDownBox = GetCountries(),
                        AccountTypesDropDownBox = GetAccountTypes()
                    };
    return View(model);
}

//
// POST: /CustomersController/Edit  

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(CustomersFormViewModel model)
{
    //THE NEXT LINE THROWS!!!
    Debug.Assert(Model.Customer!=null);
    return View(model);
}

And this is my Edit view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/CustAdminMaster.master"
    Inherits="System.Web.Mvc.ViewPage<Zeiterfassung.Controllers.CustomersController+CustomersFormViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    NewEdit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>
        NewEdit</h2>
    <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
    <% using (Html.BeginForm())
       {%>
    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="FirstName">FirstName:</label>
            <%= Html.TextBox("FirstName",Model.Customer.FirstName) %>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
    <% } %>
    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>

I've also tried a POST action method with formValues parameters, but the the viewmodel still did not contain the posted parameters:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int ID, FormCollection formValues)
{
      CustomersFormViewModel model = new CustomersFormViewModel();            
      UpdateModel(model);
      //THE NEXT LINE STILL THROWS
      Debug.Assert(model.Customer!=null);
      return View("NewEdit",model);
}

The only way I have found so far is to write my own code that grab the posted parameters from the FormCollection and updates my custom ViewModel accordingly. But this approach seems a bit primitive. Is there really no better way to do this?

EDIT: I've just tried a different syntax in the view as tvanfosson suggested, but the problem remains unchanged:

<label for="Customer.FirstName">FirstName:</label>
    <%= Html.TextBox("Customer.FirstName") %>
    <%= Html.ValidationMessage("Customer.FirstName", "*") %>
+2  A: 

You need to name your fields in accordance with the prefixes in the model. In addition, you will need to modify your view model to use properties instead of fields. The default model binder only looks at the public properties on the model when binding.

    <label for="Customer.FirstName">FirstName:</label>
    <%= Html.TextBox("Customer.FirstName") %>
    <%= Html.ValidationMessage("Customer.FirstName", "*") %>

That way it the model binder knows how to associate the form parameter with the appropriate component and associated property of your model.

tvanfosson
Thanks for your reply! "Customer.FirstName" does not work, since Customer is part of my model, but not the model. I assume you meant Model.Customer.FirstName and I have tried this (see my see my post edit), but the problem remained unchanged.
Adrian Grigore
Customer is a property of your model so using "Customer.FirstName" is appropriate. It will use reflection to find the "Customer" property on your Model, then the "FirstName" property on it. You also don't need to supply the value it will be retrieved from the model if the matching property is found
tvanfosson
I've just pasted the exact lines from your post into my view. The problem remained unchanged. Also, I've tried setting the FirstName to "test" in the GET action method. The input field was empty when looking at the view in a browser, so something must be wrong with the syntax you suggested
Adrian Grigore
I use exactly this method in one of my views. I have view model that includes a Participant, a Self contact, and an EmergencyContact. I use Participant.UserName for the UserName property on the Participant element of the Model so I know it works.
tvanfosson
Note: on Create() I do use the model as the input parameter. On Update, I pass the id as the parameter and use UpdateModel rather than model binding. I use LINQ so I have to get the corresponding model from the DB anyway and it's easier using UpdateModel than updating from a model parameter.
tvanfosson
I think I see the problem. You need to implement your view model using properties, not fields. The model binder only looks at the public properties on the model object.
tvanfosson
I'm sorry to say so, but I've just converted the ViewModel fields to properties and used the View syntax you suggested, but the problem still persists.
Adrian Grigore
Another difference is that my model is actually in the Model namespace not an inner class of my controller.
tvanfosson
A: 

Try

UpdateModel(model, "Customer");

And also take a look here :

http://stackoverflow.com/questions/347524/updatemodel-prefix-asp-net-mvc

If it still doesn't work, set a breakpoint in after UpdateModel. If it didn't do the job, try in quickWatch(ctrl+alt+q) with different combinations of calling UpdateModel.

If it still didn't work , hook up the source and debug a bit :). You'll learn something new and then you'll tell us about it :). Then everybody's happy.

Good Luck.

sirrocco
+1  A: 

Its been a while but there really hasn't been an answer on this one.

Was tampering with the same problem until I catched the auto property on my viewmodel having a private set.

Looking to your viewmodel you are missing the auto properties get and setters all together!

So change your viewmodel accordingly:

public class CustomersFormViewModel

    {
        public SelectList AccountTypesDropDownBox {get; set;}
        public SelectList CountriesDropDownBox {get; set;}
        public Customer Customer {get; set;}
    }
Ithai
You are right, however, tvanfosson was about 8 months earlier ;-)
Adrian Grigore