Telerik MVC Grid: How to use DropDownList in a column?

advertisements

I have a Telerik MVC grid, in an MVC 3 application with Razor, which is being Ajax-bound. I am now trying to add a drop list column to it, so that users can use it in edit mode, but can't figure out how. The grid displays a list of Products, and I want the drop list to contain a collection of ProductCategories, to which the Product can be associated. I've been at this for hours now and I'm out of ideas. I really hope someone here can help :)

I have been referencing a Telerik demo, which is located here.

I think the part that is hanging me up is in a help view that the demo uses. In the demo, this is called "ClientEmployee(Editor)." In my case, I've placed the helper in a file called "ProductCategoryDropList.cshtml". In this helper, I'm having a tough time getting the DropDownList to bind correctly. I think this might be because I'm not setting up the BindTo() method with the right data somehow. I've marked this point of confusion in the sample DropDownList Helper code, below, with "SomeCollectionReference," as the first parameter in a "new SelectList()" constructor call. When I try to put "Model" in that spot, I get a NullReferecne exception. When I try to access ViewBag data containing the list, I get a message similar to "the SelectList doesn't have a ProductCategoryID column," or something like that. So, I'm not sure what else to try.

I'm not sure how clear this description of my problem is, but in an effort to be complete, I've included the code that I think is relevant below.

Controller:

public ActionResult Index()
{
    ViewBag.ProductCategories = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductcategoryName");
    var products = _productService.GetProducts().ToList();
    var presentationModel = _mapper.MapAsList(products);
    return View(presentationModel);
}

//
// GET: /Product/
[GridAction]
public ViewResult _Index()
{
    ViewBag.ProductCategories = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductcategoryName");
    return View(new GridModel<ProductPresentationModel>
                    {
                        Data = _mapper.MapAsList(_productService.GetProducts().ToList())
                    });
}

View:

This is a bit long, but I've tried to simplify it by placing "// <--- DropList Here" next to the column that I am trying to work with.

@model IEnumerable<Models.PresentationModels.ProductPresentationModel>

@(Html.Telerik().Grid(Model).HtmlAttributes(new { style = "width: 100%;" })
        // Give the Grid an HTML id attribute value
        .Name("ProductGrid")
        // Establish the promiry key, to be used for Insert, Update, and Delete commands
        .DataKeys(dataKeys => dataKeys.Add(p => p.ProductID))
        // Add an Insert command to the Grid Toolbar
        .ToolBar(commands => commands.Insert().ButtonType(GridButtonType.ImageAndText))
        // Using Ajax Data Binding to bind data to the grid
        .DataBinding(dataBinding => dataBinding
                // Ajax Binding
                .Ajax()
                    .Select("_Index", "Product")
                    // Home.Insert inserts a new data record
                    .Insert("Create", "Product")
                    // Home.Update updates an existing data record
                    .Update("Edit", "Product")
                    // Home.Delete deletes an existing data record
                    .Delete("Delete", "Product")
        )
        .Columns(columns =>
        {
            columns.Bound(p => p.ProductName).Width(120);
            columns.Bound(p => p.ProductDescription).Width(150);
            columns.Bound(p => p.PricePerMonth).Width(120);
            columns.Bound(p => p.ProductImagePath).Width(150)
            columns.Bound(p => p.ProductActive).Width(120)
                .ClientTemplate("<input type='checkbox' disabled='disabled' name='Active' <#= ProductActive ? checked='checked' : '' #> />");
            columns.Bound(p => p.ProductCategoryName); // <--- DropList Here
            columns.Command(commands =>
            {
                commands.Edit().ButtonType(GridButtonType.Image);
                commands.Delete().ButtonType(GridButtonType.Image);
            });
        })
        .Editable(editing => editing.Mode(GridEditMode.PopUp))
        .ClientEvents(events => events.OnEdit("onEdit"))
        .Pageable()
        .Scrollable()
        .Sortable()
        .Filterable()
)

@section HeadContent {
    <script type="text/javascript">
        function onEdit(e) {
            $(e.form).find('#ProductCategoryName').data('tDropDownList').select(function (dataItem) {
                return dataItem.Text == e.dataItem['ProductCategoryName'];
            });
        }
    </script>
}

Model:

[DisplayName(@"Category Name")]
[UIHint("ProductCategoryDropList"), Required]
[StringLength(255, ErrorMessage = @"Product Category Name cannot be more than 255 characters in length")]
public string ProductCategoryName
{
    get
    {
        string name = string.Empty;

        if (_model.ProductCategory != null)
        {
            name = _model.ProductCategory.ProductCategoryName;
        }

        return name;
    }
    set
    {
        if (_model.ProductCategory != null)
        {
            _model.ProductCategory.ProductCategoryName = value;
        }
    }
}

DropList Helper:

@model Models.PresentationModels.ProductPresentationModel

@(Html.Telerik().DropDownList()
        .Name("ProductCategoryName")
            .BindTo(new SelectList(<SomeCollectionReference>, "ProductCategoryID", "ProductCategoryName"))
)

ProductMapper:

public List<ProductPresentationModel> MapAsList(List<Product> products)
{
    //var categoryList = new SelectList(_productCategoryService.GetProductCategories().ToList(), "ProductCategoryID", "ProductCategoryName"); 

    var presentationModels = products
            .Select(x => new ProductPresentationModel()
            {
                ProductID = x.ProductID,
                ProductCategoryID = ((x.ProductCategory != null) ? x.ProductCategory.ProductCategoryID : 0),
                ProductCategoryName = ((x.ProductCategory != null) ? x.ProductCategory.ProductCategoryName : String.Empty),
                ProductName = x.ProductName,
                ProductDescription = x.ProductDescription,
                PricePerMonth = x.PricePerMonth,
                ProductImagePath = x.ProductImagePath,
                ProductActive = x.ProductActive,
                ProductCategories = new SelectList(_productCategoryService.GetProductCategories().ToList(), "ProductCategoryID", "ProductCategoryName")//categoryList
            }).ToList();

    return presentationModels;
}


I managed to work this out, somewhat, but I still have a question. here's what I changed to get it working:

Created a ViewData object in the controller, like this ...

public ActionResult Index()
{
    // ViewData object here ...
    ViewData["ProductCategories"] = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductCategoryName");
    var products = _productService.GetProducts().ToList();
    var presentationModel = _mapper.MapAsList(products);
    return View(presentationModel);
}

//
// GET: /Product/
[GridAction]
public ViewResult _Index()
{
    // ViewData object here ...
    ViewData["ProductCategories"] = new SelectList(_productCategoryService.GetActiveProductCategories(), "ProductCategoryID", "ProductCategoryName");
    return View(new GridModel<ProductPresentationModel>
                    {
                        Data = _mapper.MapAsList(_productService.GetProducts().ToList())
                    });
}

Then, I used the ViewData object in the DropDownListHelper, like this ...

@using System.Collections
@model Models.PresentationModels.ProductPresentationModel

@(Html.Telerik().DropDownList()
        .Name("ProductCategoryName")
        .BindTo(new SelectList((IEnumerable)ViewData["ProductCategories"], "Value", "Text"))
);

My question now is ... do I have to use the ViewData object? I'd love to be able to use a property off of my model. But, for some reason my Model is always NULL inside the Helper file. And, if I try to place the DropDownList code inside the Grid creation code, the DropDownList doesn't work at all.

So, do I have any other options?