MVC Grid inline editor with separate datasource

6 posts, 0 answers
  1. Erik
    Erik avatar
    12 posts
    Member since:
    Sep 2016

    Posted 15 Feb 2017 Link to this post

    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"))
  2. Konstantin Dikov
    Admin
    Konstantin Dikov avatar
    2466 posts

    Posted 17 Feb 2017 Link to this post

    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.
  3. Erik
    Erik avatar
    12 posts
    Member since:
    Sep 2016

    Posted 06 Mar 2017 Link to this post

    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?

  4. Erik
    Erik avatar
    12 posts
    Member since:
    Sep 2016

    Posted 07 Mar 2017 in reply to Erik Link to this post

    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.

  5. Konstantin Dikov
    Admin
    Konstantin Dikov avatar
    2466 posts

    Posted 08 Mar 2017 Link to this post

    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.
  6. Erik
    Erik avatar
    12 posts
    Member since:
    Sep 2016

    Posted 09 Mar 2017 in reply to Konstantin Dikov Link to this post

    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.
Back to Top