kendo-labs / knockout-kendo

A project to create a robust set of Knockout.js bindings for the Kendo UI widgets.
http://kendo-labs.github.com/knockout-kendo/
273 stars 144 forks source link

When using the configuration of `filterable: { mode: "row" }` binding is unable to process. #225

Open edhedges opened 8 years ago

edhedges commented 8 years ago

I'm receiving an error that says the following:

Uncaught ReferenceError: Unable to process binding "value: function (){return value }" Message: value is not defined

I've followed this down to the filterable row dom element has a data-bind attribute with value: value and knockout doesn't know what to do with it since value is undefined.

Is this configuration not supported?

bmsmg commented 8 years ago

Can you setup a jsfiddle or equivalent so we can see your structure?

edhedges commented 8 years ago

Here's an example of the same error, but in this example the data is showing up in the grid and the filtering seems to work stil...

EXAMPLE

My code is a bit different and the data configuration looks like this:

    data: new kendo.data.DataSource({
        data: ko.observableArray([]),
        serverFiltering: myConfigObj.serverFiltering,
        transport: {
            read:  {
                url: dmGridConfig.url,
                type: "GET"
            }
        },
        type: myConfigObj.serverFiltering ? "aspnetmvc-ajax" : undefined,
        schema: typeof (myConfigObj.schema) === "object" ? myConfigObj.schema : undefined,
        pageSize: 25
    }) 

I also don't normally insert/update items in the data observable array (most of it comes from the server).

@bmsmg Sorry for taking so long (I was using the menu config, but now need to use the row option).

RaptorCZ commented 8 years ago

Well, so we have an error. Is there any fix/workaround? It is still unhandled exception and in complex scenario it breaks flow.

edhedges commented 8 years ago

I'm not sure what the problem is, but I noticed that it has something to do with the filter row and the data-bind="value: ..." html elements (the inputs).

RaptorCZ commented 8 years ago

Seems that there is some problem with internal viewmodel and binding in FilterCell widget.

        var FilterCell = Widget.extend({
            init: function (element, options) {
                element = $(element).addClass('k-filtercell');
                var wrapper = this.wrapper = $('<span/>').appendTo(element);
                var that = this, 
                dataSource, 
                viewModel, 
                passedOptions = options, 
                first, 
                type, 
                operators = that.operators = options.operators || {}, 
                input = that.input = $('<input/>').attr(kendo.attr('bind'), 'value: value').appendTo(wrapper);
                Widget.fn.init.call(that, element[0], options);

It creates a new 'value' binding, but Knockout tries to bind it to other viewmodel and there is no 'value'. Binding is created on filter input on the top of grid - filtering row.

So maybe problem is in scope in Kendo widget or in kendo-knockout widget?

edhedges commented 8 years ago

I believe this is truly a knockout-kendo bug. The kendo code uses data-bind syntax throughout some of their widgets, but knockout is tripping up on the filter data-binds.

RaptorCZ commented 8 years ago

So maybe some way how to disable descendant binding for grid with filter or anything?

ronniemacapobre commented 8 years ago

Encountered the same issue. Any possible workaround for this?

RaptorCZ commented 8 years ago

I did it this way:

Created global binding

    ko.bindingHandlers.stopBinding =
        {
            init: (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) =>
            {
                return { controlsDescendantBindings: true }
            }
        }

Then modified kendo-knockout.js

        //build the core logic for the init function
        binding.setup = function(element, options, context) {
            var widget, $element = $(element);

            //step 2: setup templates
            self.setupTemplates(widgetConfig.templates, options, element, context);

            //step 3: initialize widget
            widget = self.getWidget(widgetConfig, options, $element);

            // -----------------------------------------------------------------------
            // by Raptor - Temporary fix for row filter
            //           - 'stopBinding' binding handler must be available anywhere
            // -----------------------------------------------------------------------
            // Disable descendant binding for kendoGrid, if filter is active in 'row' mode
            if (widgetConfig.name === "kendoGrid") {
                if ((options.filterable) && (options.filterable.mode === "row")) {
                    $(element).find(".k-filter-row").attr("data-bind", "stopBinding: true");
                }
            }

            //step 4: add handlers for events that we need to react to for updating the model
            self.handleEvents(options, widgetConfig, element, widget, context);

            //step 5: set up computed observables to update the widget when observable model values change
            self.watchValues(widget, options, widgetConfig, element);

            //step 6: handle disposal, if there is a destroy method on the widget
            if (widget.destroy) {
                ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                    if (widget.element) {
                        if (typeof kendo.destroy === "function") {
                            kendo.destroy(widget.element);
                        } else {
                            widget.destroy();
                        }
                    }
                });
            }
        };

I don't like this way, because I had to hardcode grin widget name there, but I have no time to made it better way. This one works for now and that's it.