Zen Pak
Zen Pak

Reputation: 578

Posted ViewModel return null properties

I am having issues with a view model that constantly return null properties after a post. Below is my code (it could be a syntax issue or two calling a class or property the same name as i saw in other posts but i could not see any such issue in code):

VIEW MODEL:

public class ProductItem
    {
        public int ProductID { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string LongDescription { get; set; }
        public int SupplierID { get; set; }
        public string Dimensions { get; set; }
        public double Price { get; set; }
        public bool On_Sale { get; set; }
        public double DiscountedPrice { get; set; }
        public string Thumbnail { get; set; }
        public string LargeImage { get; set; }
        public string LargeImage2 { get; set; }
        public string LargeImage3 { get; set; }
        public string CrossRef { get; set; }
        public byte Available { get; set; }
        public double Weight { get; set; }
        public byte Important { get; set; }
        public virtual ICollection<ProductCategory> ProductCategories { get; set; }

        // this is required on the page to allow products to be marked for deletion
        public bool IsForDelete { get; set; }
    }

    public class ProductListViewModel
    {
        public IEnumerable<ProductItem> ProductItems { get; set; }
        public IEnumerable<Category> CategoryItems { get; set; }
    }

CONTROLLER:

public ActionResult ProductList()
        {
            var productList = new ProductListViewModel();
            productList.ProductItems = productRepository.GetProductsWithDeleteOption().ToList();
            productList.CategoryItems = categoryRepository.GetCategories().ToList();
            return View(productList);
        }

        [HttpPost]
        public ActionResult ProductList(ProductListViewModel productViewModel, FormCollection formCollection, string submit)
        {            
            if (ModelState.IsValid)
            {
                // Check for submit action
                if (submit == "Change Sort")
                {
                    if (formCollection["Sortby"] == "ProductID")
                    {
                        OrderBy(productViewModel, formCollection, "m.ProductID");
                    }
                    else if (formCollection["Sortby"] == "Code")
                    {
                        OrderBy(productViewModel, formCollection, "m.Code");
                    }
                    else if (formCollection["Sortby"] == "Name")
                    {
                        OrderBy(productViewModel, formCollection, "m.Name");
                    }
                    else if (formCollection["Sortby"] == "Price")
                    {
                        OrderBy(productViewModel, formCollection, "m.Price");
                    }
                }
                else if (submit == "Delete all selected")
                {

                }
                else if (submit == "Update All")
                {

                }
                else if (submit == "Restrict Display")
                {

                }
            }
            return View(productViewModel);
        }

VIEW:

@model Admin.Models.ViewModels.ProductListViewModel

@{
    ViewBag.Title = "View Products";
}

@using (Html.BeginForm())
{
<h2>Product List as at @DateTime.Now.ToString("dd/MM/yyyy")</h2>
<table>
    <tr>
        <td>Sort by:</td>
        <td>
            <select name="Sortby">
                <option value="ProductID">ProductID</option>
                <option value="Code">Code</option>
                <option value="Name">Name</option>
                <option value="Price">Price</option>
            </select>
        </td>
        <td>
            <input type="radio" name="sortDirection" checked="checked" value="Asc" /> Ascending
            <input type="radio" name="sortDirection" value="Desc" /> Descending
        </td>
        <td>
            <input type="submit" name="submit" value="Change Sort" />
        </td>
    </tr>
    <tr>
        <td>Display only : (category)</td>
        <td>@Html.DropDownList("CategoryID", new SelectList(Model.CategoryItems, "CategoryID", "Name"), "All Categories")</td>
        <td colspan="2"><input type="submit" name="submit" value="Restrict Display" /></td>
    </tr>
    <tr>
        <td colspan="4"><br />Total Number of products: @Model.ProductItems.Count()</td>
    </tr>
</table>
<table>
    <tr>
        <th>
            Edit
        </th>
        <th>
            Code
        </th>
        <th>
            Name
        </th>
        <th>
            Price
        </th>
        <th>
            On_Sale
        </th>
        <th>
            DiscountedPrice
        </th>
        <th>
            Weight
        </th>
        <th>
            Delete
        </th>
        <th></th>
    </tr>

@for (var i = 0; i < Model.ProductItems.ToList().Count; i++)
{
    <tr>
        <td>
            @Html.HiddenFor(m => m.ProductItems.ToList()[i].ProductID)            
            @Html.ActionLink(Model.ProductItems.ToList()[i].ProductID.ToString(), "ProductEdit", new { id = Model.ProductItems.ToList()[i].ProductID })
        </td>
        <td>
            @Html.DisplayFor(m => m.ProductItems.ToList()[i].Code)
        </td>
        <td>
            @Html.DisplayFor(m => m.ProductItems.ToList()[i].Name)
        </td>
        <td>
            @Html.EditorFor(m => m.ProductItems.ToList()[i].Price)
        </td>
        <td>
            @Html.CheckBoxFor(m => m.ProductItems.ToList()[i].On_Sale, new { id = "On_Sale_" + Model.ProductItems.ToList()[i].ProductID })
        </td>
        <td>
            @Html.EditorFor(m => m.ProductItems.ToList()[i].DiscountedPrice)
        </td>
        <td>
            @Html.EditorFor(m => m.ProductItems.ToList()[i].Weight)
        </td>
        <td>
            @Html.CheckBoxFor(m => m.ProductItems.ToList()[i].IsForDelete, new { id = Model.ProductItems.ToList()[i].ProductID }) 
        </td>
        <td>
            @Html.ActionLink("Edit", "ProductEdit", new { id = Model.ProductItems.ToList()[i].ProductID }) |
            @Html.ActionLink("Details", "Details", new { id = Model.ProductItems.ToList()[i].ProductID }) |
            @Html.ActionLink("Delete", "Delete", new { id = Model.ProductItems.ToList()[i].ProductID })
        </td>
    </tr>
}

</table>
<p>
    <input name="submit" type="submit" value="Delete all selected" />
</p>
<p>
    <input name="submit" type="submit" value="Update All" />
</p>

<p>
    @Html.ActionLink("Add a new product", "ProductAdd")
</p>

}

In the post action, the productViewModel argument has the ProductItems and CategoryItems properties as null.

Upvotes: 0

Views: 2444

Answers (2)

Brendan Vogt
Brendan Vogt

Reputation: 26048

Yes it will be null on post back. I have a similar table in one of my projects and this is what I would have done given my situation.

You could change your view model to look like this then you don't have to do so many converting to lists in your view:

public class ProductListViewModel
{
     public List<ProductItem> ProductItems { get; set; }

     public List<Category> CategoryItems { get; set; }
}

Now in you view it could look something like this (this is just part of it, then rest you can just go and add):

@for (int i = 0; i < Model.ProductItems.Count(); i++)
{
     <tr>
          <td>
               @Html.DisplayFor(m => m.ProductItems[i].Name)
               @Html.HiddenFor(m => m.ProductItems[i].Name)
          </td>
     </tr>
     <tr>
          <td>
               @Html.CheckBoxFor(m => m.ProductItems[i].IsForDelete)
          </td>
     </tr>
}

Add the code and do some debugging to see how the values are returned on submit

I hope this helps.

Upvotes: 1

U.P
U.P

Reputation: 7442

Ok, so there are two problems.

  1. I don't understand why you want to post list of the CategoryItems You should only expect the selected category and not the list

  2. The problem with ProductItems is the name generated for <input> tags. Currently, the name being generated is name="[0].Price" whereas it should have been name="ProductItems[0].Price"

I changed the following code

@Html.EditorFor(m => m.ProductItems.ToList()[i].Price)

to

@Html.EditorFor(m => m.ProductItems[i].Price)

and it worked.

Note: I changed IEnumerable<ProductItem> ProductItems to List<ProductItem> ProductItems in ProductListViewModel

Upvotes: 3

Related Questions