简体   繁体   中英

View Model posting null properties back to controller

On my Index page I have a search form that allows users to pull information based on 3 search criteria. When the the search results return from the controller, they are returned as a List object that is a property of the view model and displayed in a table below the search form.

The user then has the option of selecting which records they would like to alter. They will do this by selecting a check box next to the specified records. Then, using a form that appears below the search box, will enter the new values and hit an "Update" button to save the changes. (we do this in order to allow the user to make mass updates without having to alter the table row by row)

Here is a screen shot to illustrate what I mean. This is what the page will look like after the user has hit the "Search" button:

在此输入图像描述

When the user hit's "Search" the form is instructed to post back to the Index method of the controller. When the user hits "Clone Selected Items" the form is instructed to post back to an "Update" method in the same controller. However, the problem is the view model object that gets posted back to the "Update" method is completely null except for the values placed in the New Territory , New Description , and New Effective Date text boxes.

I'm relatively new to ASP MVC so any help/suggestions would be appreciated. Not sure why the View Model will post back from the controller with the same values it was sent there while using the "Search" button but will not do so using the "Clone Selected Items" one.

View Model (this is what gets posted back to the controller)

public class ZipCodeIndex
{
    public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
    [DisplayName("Zip Code")]
    public string searchZip { get; set; }
    [DisplayName("Effective on this date")]
    public string searchDate { get; set; }
    [DisplayName("State")]
    public string searchState { get; set; }
    [DisplayName("New Territory")]
    public string newTerritory { get; set; }
    [DisplayName("New Description")]
    public string newDescription { get; set; }
    [DisplayName("New Effective Date")]
    public string newEffectiveDate { get; set; }

    public ZipCodeIndex() 
    {
        zipCodeTerritory = new List<ZipCodeTerritory>();
    }
}

Model (This populates the List object in the View Model)

[MetadataType(typeof(ZipCodeTerritoryMetaData))]
public partial class ZipCodeTerritory
{
    public bool Update { get; set; }
}

public partial class ZipCodeTerritory
{
    public string ChannelCode { get; set; }
    public string DrmTerrDesc { get; set; }
    public string IndDistrnId { get; set; }
    public string StateCode { get; set; }
    public string ZipCode { get; set; }
    public System.DateTime EndDate { get; set; }
    public System.DateTime EffectiveDate { get; set; }
    public string LastUpdateId { get; set; }
    public Nullable<System.DateTime> LastUpdateDate { get; set; }
    public int Id { get; set; }
}

View

@model Monet.ViewModel.ZipCodeIndex

    @using(Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post))
    {
        <div id="searchBox" class="boxMe">
            <div id="zipBox">
                @Html.Raw("Zip Code")
                @Html.TextAreaFor(model => model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
            </div>
            <div id="dateBox">
                @Html.LabelFor(model => model.searchDate)
                @Html.TextBoxFor(model => model.searchDate, new { style="width: 80px;"})
            </div>
            <div id="stateBox">
                @Html.LabelFor(model => model.searchState)
                @Html.TextBoxFor(model => model.searchState, new { style = "width: 25px;" })
                <button type="submit">Search</button>
            </div>
        </div>
        <div id="errorStatus">
            @ViewBag.ErrorMessage            
        </div>
        <div style="clear: both;"></div>
    }

<br/>
@Html.ActionLink("Create New", "Create")
<br/>

@if (Model != null)
{
    using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
    {
        <div id="cloneBox">
            @Html.LabelFor(model => model.newTerritory)
            @Html.TextBoxFor(model => model.newTerritory, new { style="width: 30px;padding-left:10px;"})
            @Html.LabelFor(model => model.newDescription)
            @Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;" })  
            @Html.LabelFor(model => model.newEffectiveDate)     
            @Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })   
            <button type="submit">Clone Selected Items</button>                      
        </div>
    }    

    <table id="thetable" class="tablesorter" >
        <thead>
            <th></th>
            <th>Channel</th>
            <th>Territory</th>
            <th>Description</th>
            <th>State</th>
            <th>Zip</th>
            <th>Effective</th>
            <th>End Date</th>
            <th>Last Update By</th>
            <th>Last Update Date</th>
            <th></th>
        </thead>
        <tbody>
            @foreach (var item in Model.zipCodeTerritory)
            {
                <tr>
                    <td>@Html.CheckBoxFor(model => item.Update)</td>
                    <td>
                        @Html.DisplayFor(model => item.ChannelCode)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.IndDistrnId)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.DrmTerrDesc)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.StateCode)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.ZipCode)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.EffectiveDate)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.EndDate)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.LastUpdateId)
                    </td>
                    <td>
                        @Html.DisplayFor(model => item.LastUpdateDate)
                    </td>
                    <td>
                        @Html.ActionLink("Edit", "Edit", new { id = item.Id })
                    </td>
                </tr>
            }
        </tbody>
    </table>    
}

Controller

    public ViewResult Index(ZipCodeIndex search)
    {
        try
        {
            //If search criteria is null page is loading for the first time so send blank view
            if (String.IsNullOrWhiteSpace(search.searchZip) &&
                String.IsNullOrWhiteSpace(search.searchDate) &&
                String.IsNullOrWhiteSpace(search.searchState))
            {
                return View();
            }

            //Determine if necessary search criteria has been sent
            if (String.IsNullOrWhiteSpace(search.searchZip) && String.IsNullOrWhiteSpace(search.searchState))
            {
                ViewBag.ErrorMessage = "Either State or Zip Code Must be Specified";
                return View(search);
            }

            DateTime effectiveDate;

            //Convert date string to DateTime type
            if (String.IsNullOrWhiteSpace(search.searchDate))
            {
                effectiveDate = DateTime.MinValue;
            }
            else
            {
                effectiveDate = Convert.ToDateTime(search.searchDate);
            }

            //Conduct search by State Code/Date alone
            if (String.IsNullOrWhiteSpace(search.searchZip))
            {
                search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                           where z.StateCode.Equals(search.searchState) &&
                                                 z.EffectiveDate >= effectiveDate
                                           select z).ToList();
                return View(search);
            }

            //Zip codes have been requested to conduct zip search
            string[] zipArray;

            //Create array and remove white spaces
            zipArray = search.searchZip.Split(',').Distinct().ToArray();
            for (int i = 0; i < zipArray.Length; i++)
            {
                zipArray[i] = zipArray[i].Trim();
            }

            //Determine if state code is being used in search
            if (String.IsNullOrWhiteSpace(search.searchState))
            {
                foreach (var zip in zipArray)
                {
                    var item = from z in db.ZipCodeTerritory
                               where z.ZipCode.Equals(zip) &&
                                      z.EffectiveDate >= effectiveDate
                               select z;
                    search.zipCodeTerritory.AddRange(item);
                }
            }
            else
            {
                foreach (var zip in zipArray)
                {
                    var item = from z in db.ZipCodeTerritory
                               where z.ZipCode.Equals(zip) &&
                                      z.EffectiveDate >= effectiveDate &&
                                      z.StateCode.Equals(search.searchState)
                               select z;
                    search.zipCodeTerritory.AddRange(item);
                }
            }
        }
        catch (DbEntityValidationException dbEx)
        {
            ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
            SendEmail.ErrorMail(Common.ErrorCheck.CombineDbErrors(dbEx));
        }
        catch (Exception ex)
        {
            ViewBag.ErrorMessage = ErrorCheck.FriendlyError(ex);
            SendEmail.ErrorMail(ex);
        }

        return View(search);
    }


    [HttpPost]
    public ActionResult Update(ZipCodeIndex updateZip)
    {
        foreach (var zipCode in updateZip.zipCodeTerritory)
        {
            if (zipCode.Update)
            {
                try
                {
                    if (!string.IsNullOrEmpty(updateZip.newTerritory)) zipCode.IndDistrnId = updateZip.newTerritory;
                    if (!string.IsNullOrWhiteSpace(updateZip.newDescription)) zipCode.DrmTerrDesc = updateZip.newDescription;
                    if (!string.IsNullOrWhiteSpace(updateZip.newEffectiveDate)) zipCode.EffectiveDate = Convert.ToDateTime(updateZip.newEffectiveDate);

                    db.Entry(zipCode).State = EntityState.Modified;
                    db.SaveChanges();
                }
                catch (DbEntityValidationException dbEx)
                {
                    ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
                    SendEmail.ErrorMail(Common.ErrorCheck.CombineDbErrors(dbEx));
                }
                catch (Exception ex)
                {
                    ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
                    SendEmail.ErrorMail("Zip Code not updated: " + zipCode.ToString() + " |MESSAGE| " + ex.Message);
                }
            }
        }

        return RedirectToAction("Index", updateZip);
    }

EDIT

I've added the following hidden fields to the second form (that posts to the Update method). This will send back the search criteria however the List object is still empty.

@if (Model != null)
{
    using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
    {
        @Html.HiddenFor(model => model.searchZip)
        @Html.HiddenFor(model => model.searchDate)
        @Html.HiddenFor(model => model.searchState)
        @Html.HiddenFor(model => model.zipCodeTerritory)

        <div id="cloneBox">

Well if adding the hiddenfor did not work I would try to make it something like this.

@for (int i = 0; i < Model.OrdItems.Count; i++)
{

@Html.DisplayFor(model => model.zipCodeTerritory[i].channelCode)@Html.HiddenFor(model => model.zipCodeTerritory[i].channelCode)
@Html.DisplayFor(model => model.zipCodeTerritory[i].zipCode)@Html.HiddenFor(model => model.zipCodeTerritory[i].zipCode)

}

When we use Html Helper method Html.DisplayFor() , it just shows the data as a plain text. You can check it by View Page resource in your browser. What you have do is to use Html.HiddenFor helper method to bind what data you need after post. Or you can also bind data using JQuery.Ajax

The above post is an inefficient and incorrect way to fix your problem. ASP.NET MVC reserves and uses the word "model" as a keyword during the model binding process. You need to change the name "search" to "model" in your action result parameter. That should fix your problem with your search data not posting back.

它只是当您在控制器中返回操作结果中的视图时,创建该模型的对象然后仅将其传递给视图。

Go to RouteConfig.cs in App_start , then properly you can see url: {controller}/{action}/{id} in RegisterRoutes method. Browser will go to this route. Has your Edit Action an input argument calls id ? maybe the name is different.

I would suggest trying to use KnockoutJS, it would be much easier to post the actual data from the server back, just updated. If you want examples - let me know i'll be happy to give some.

if that's not an option - Pay attention to what's inside the form you created (BeginForm), because most of what you want isn't inside, so it won't be submitted to the controller on post.

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