This is a migrated thread and some comments may be shown as answers.

MVC Grid inline editor with separate datasource

5 Answers 594 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Erik
Top achievements
Rank 1
Erik asked on 15 Feb 2017, 03:28 PM

For the past few days I've been trying to get a grid working for a relatively complex model. I want to use a remote datasource for populating the initial grid, but for adding and editing I need to query other datasources for several columns, to get filtering and sorting in a dropdownlist.

I've encountered several problems, with things like binding the dropdowns and validating the editor. I'll post some of my code here, maybe someone here can provide a suggestion on what I'm doing wrong/forgetting.

Controller: 

// GET: Settings
        public ActionResult Index()
        {
            return View();
        }
 
        public async Task<ActionResult> GetSettings([DataSourceRequest] DataSourceRequest request)
        {
            DiscountSettingsViewModel viewModel = await _discountService.GetViewModel();
            var result = viewModel.Discounts.ToDataSourceResult(request);
            result.Total = viewModel.Discounts.Count;
 
            return Json(result, JsonRequestBehavior.AllowGet);
        }
 
public async Task<ActionResult> GetItemGroups([DataSourceRequest] DataSourceRequest request)
        {
            var queryParser = new DataSourceODataParser();
            List<ItemGroupViewModel> result = await _discountService.GetItemGroups(queryParser.Parse(request));
            return Json(new DataSourceResult {Data = result, Total = result.Count}, JsonRequestBehavior.AllowGet);
        }
 
        public async Task<ActionResult> GetItems([DataSourceRequest] DataSourceRequest request)
        {
            var queryParser = new DataSourceODataParser();
            List<ItemViewModel> result = await _discountService.GetItems(queryParser.Parse(request));
            return Json(new DataSourceResult {Data = result, Total = result.Count}, JsonRequestBehavior.AllowGet);
        }
 
        public async Task<ActionResult> GetResellers([DataSourceRequest] DataSourceRequest request)
        {
            var queryParser = new DataSourceODataParser();
            List<ResellerViewModel> result = await _discountService.GetResellers(queryParser.Parse(request));
            return Json(new DataSourceResult { Data = result, Total = result.Count }, JsonRequestBehavior.AllowGet);
        }
 
        public async Task<ActionResult> GetCustomers([DataSourceRequest] DataSourceRequest request)
        {
            var queryParser = new DataSourceODataParser();
            List<CustomerViewModel> result = await _discountService.GetCustomers(queryParser.Parse(request));
            return Json(new DataSourceResult { Data = result, Total = result.Count }, JsonRequestBehavior.AllowGet);
        }

 

ViewModels:

public class DiscountSettingsViewModel
    {
        public int SubscriptionId { get; set; }
        public List<DiscountViewModel> Discounts { get; set; }
    }
 
    public class DiscountViewModel
    {
        public int Id { get; set; }
        public ItemGroupViewModel ItemGroup { get; set; }
        public ItemViewModel Item { get; set; }
        public ResellerViewModel Reseller { get; set; }
        public CustomerViewModel Customer { get; set; }
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public bool IsExtra { get; set; }
        public double Discount { get; set; }
 
        public DiscountViewModel()
        {
            Id = 0;
            ItemGroup = new ItemGroupViewModel();
            Item = new ItemViewModel();
            Reseller = new ResellerViewModel();
            Customer = new CustomerViewModel();
            IsExtra = false;
            Discount = 0;
        }
    }
 
    public class ItemGroupViewModel
    {
        public string Code { get; set; }
        public string Description { get; set; }
 
 
        public ItemGroupViewModel()
        {
            Code = "";
            Description = "";
        }
    }
 
    public class ItemViewModel
    {
        public string Code { get; set; }
        public string Description { get; set; }
 
 
        public ItemViewModel()
        {
            Code = "";
            Description = "";
        }
    }
 
    public class ResellerViewModel
    {
        public Guid? ID { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public Guid? Classification1 { get; set; }
 
 
        public ResellerViewModel()
        {
            ID = Guid.Empty;
            Code = "";
            Name = "";
            Classification1 = Guid.Empty;
        }
    }
 
    public class CustomerViewModel
    {
        public Guid? ID { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public ResellerViewModel Reseller { get; set; }
 
 
        public CustomerViewModel()
        {
            ID = Guid.Empty;
            Code = "";
            Name = "";
            Reseller = new ResellerViewModel();
        }
    }

 

Main view:

<h2>Discount Settings</h2>
 
<script type="text/javascript">
    kendo.data.binders.widget.defferedValue = kendo.data.Binder.extend({
        init: function (widget, bindings, options) {
            kendo.data.Binder.fn.init.call(this, widget.element[0], bindings, options);
            this.widget = widget;
            this._change = $.proxy(this.change, this);
            this.widget.bind("change", this._change);
        },
        refresh: function () {
            if (!this._initChange)
            {
                var widget = this.widget;
                var value = this.bindings.defferedValue.get();
 
                if (value)
                {
                    if (widget.options.autoBind === false)
                    {
                        //Bind the widget with single item if deffered binding is used
                        widget.dataSource.data([value]);
                        widget.value(value[widget.options.dataValueField]);
                    } else
                    {
                        //set widget value directly
                        this.widget.value(value[widget.options.dataValueField]);
                    }
                }
            }
        },
        change: function () {
            this._initChange = true;
            this.bindings.defferedValue.set(this.widget.dataItem() || null);
            this._initChange = false;
        },
        destroy: function () {
            this.widget.unbind("change", this._change);
        }
    });
</script>
 
@(Html.Kendo().Grid<DiscountViewModel>()
      .Name("Discounts")
      .Columns(col =>
      {
          col.Bound(i => i.ItemGroup)
            .Title(Resource.ItemGroup)
            .ClientTemplate("#: data.ItemGroup ? data.ItemGroup.Code : '[none]' #")
            .EditorTemplateName("ItemGroupLookup")
            .Filterable(false)
            .Sortable(true);
          col.Bound(i => i.Item)
            .Title(Resource.Item)
            .ClientTemplate("#: data.Item ? data.Item.Code : '[none]' #")
            .EditorTemplateName("ItemLookup")
            .Filterable(false)
            .Sortable(true);
          col.Bound(i => i.Reseller)
            .Title(Resource.Reseller)
            .ClientTemplate("#: data.Reseller ? data.Reseller.Name : '[none]' #")
            .EditorTemplateName("ResellerLookup")
            .Filterable(false)
            .Sortable(true);
          col.Bound(i => i.Customer)
            .Title(Resource.Customer)
            .ClientTemplate("#: data.Customer ? data.Customer.Name : '[none]' #")
            .EditorTemplateName("CustomerLookup")
            .Filterable(false)
            .Sortable(true);
          col.Bound(i => i.StartDate).Title(Resource.StartDate).Format("{0:dd-MM-yyyy}");
          col.Bound(i => i.EndDate).Title(Resource.EndDate).Format("{0:dd-MM-yyyy}");
          col.Bound(i => i.IsExtra).Title(Resource.IsExtra);
          col.Bound(i => i.Discount).Title(Resource.Discount);
          col.Command(command =>
          {
              command.Edit().Text(Resource.Edit);
              command.Destroy().Text(Resource.Delete);
          }).Width(250);
      })
      .Sortable(s => s.SortMode(GridSortMode.MultipleColumn))
      .ToolBar(toolbar => toolbar.Create().Text(Resource.Add))
      .Editable(editable => editable.Mode(GridEditMode.InLine))
      .Pageable()
      .DataSource(source => source
        .Ajax()
        .PageSize(20)
        .Events(events => events.Error("error_handler"))
        .Model(m =>
        {
            m.Id(i => i.Id);
            m.Field(p => p.ItemGroup);
            m.Field(p => p.Item);
            m.Field(p => p.Reseller);
            m.Field(p => p.Customer);
            m.Field(p => p.StartDate);
            m.Field(p => p.EndDate);
            m.Field(p => p.IsExtra);
            m.Field(p => p.Discount);
        })
        .Create(update => update.Action("CreateSetting", "Settings"))
        .Read(read => read.Action("GetSettings", "Settings"))
        .Update(update => update.Action("UpdateSetting", "Settings"))
        .Destroy(update => update.Action("DestroySetting", "Settings"))
      )
      .NoRecords(Resource.NoSettings)
)

 

Example EditorTemplate:

@model NuCall.ViewModels.ItemGroupViewModel
 
@(Html.Kendo().DropDownListFor(m => m)
      .HtmlAttributes(new { data_skip = "true", data_bind = "deferredValue: ItemGroup" })
      .OptionLabel("--none--")
      .DataSource(source =>
      {
          source.Custom()
              .ServerFiltering(true)
              .ServerPaging(true)
              .PageSize(20)
              .Type("aspnetmvc-ajax")
              .Transport(transport => transport.Read("GetItemGroups", "Settings"))
              .Schema(schema => schema
                  .Data("Data")
                  .Total("Total")
                  .Errors("Errors"));
      })
      .MinLength(1)
      .AutoBind(false)
      .Filter(FilterType.StartsWith)
      .DataValueField("Code")
      .DataTextField("Code"))

5 Answers, 1 is accepted

Sort by
0
Konstantin Dikov
Telerik team
answered on 17 Feb 2017, 10:03 AM
Hi Erik,

You can refer to the following help article and online demo for examples on how to bind complex object to an custom editor:
Note that you can change the binding of the custom editors as per your requirements.

Finally, you should have in mind that once you bind a column to a complex object, filtering, sorting, grouping, etc. will not work for that column and they will have to be disabled.

Hope this helps.


Regards,
Konstantin Dikov
Telerik by Progress
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Erik
Top achievements
Rank 1
answered on 06 Mar 2017, 04:44 PM

Hi Konstantin,

Thank you for your response.

Unfortunately, I still don't have this working. I changed my code to bind the several dropdowns to the viewdata instead of an ajax datasource. However, when I try to add a new record, I see an error popping up in the console about one of the fields not being defined

Uncaught ReferenceError: ItemGroup is not defined
    at eval (eval at compile (kendo.all.js:194), <anonymous>:3:150)
    at init._rowsHtml (kendo.all.js:50193)
    at init._renderContent (kendo.all.js:50749)
    at init.refresh (kendo.all.js:50625)
    at init.proxy (jquery-2.2.4.js:497)
    at init.trigger (kendo.all.js:124)
    at init._process (kendo.all.js:6938)
    at init._change (kendo.all.js:6898)
    at init.proxy (jquery-2.2.4.js:497)
    at init.trigger (kendo.all.js:124)

 

No new line shows up after this. Also, when picking a value from the dropdown, a yellow warning symbol pops up,  not allowing further actions expect for picking different values in the dropdown. Have I perhaps missed a step in the modelbinding? If so, do yuo have any guides to help with that?

0
Erik
Top achievements
Rank 1
answered on 07 Mar 2017, 04:11 PM

Update: I now fixed the referenceerror by assigning default values as null, but am still getting the yellow validation popups. It's a k-tooltip-validation element under the table field, containing only a span with the k-icon and k-i-warning classes.They appear on my dropdowneditors when choosing any value other than null, and also on the boolean checkbox (default false) and numerictextbox for a double (default 0.0).

It appears that while these are present, I cannot actually update/add the new row.

0
Konstantin Dikov
Telerik team
answered on 08 Mar 2017, 09:58 AM
Hello Erik,

In order for us to be able to investigate the issue that you are facing, please try to create a sample, runnable project replicating it, because from the provided information it will be really difficult to guess what could be causing it. You can return dummy data within the controller just to demonstrate the problem.

I am looking forward to your reply.


Regards,
Konstantin Dikov
Telerik by Progress
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Erik
Top achievements
Rank 1
answered on 09 Mar 2017, 09:48 AM
While trying to create a (not) working example, I figured out the issue was caused by my attempt to implement a custom DateTime validator, that was invalidating all fields. Removing this fixed the issue. Thank you for your assistance, Konstantin.
Tags
Grid
Asked by
Erik
Top achievements
Rank 1
Answers by
Konstantin Dikov
Telerik team
Erik
Top achievements
Rank 1
Share this question
or