DotVVM:在GridViewTemplateColumn ContentTemplate中使用自定义绑定

时间:2018-06-18 13:12:15

标签: gridview knockout.js data-binding dotvvm contenttemplate

我为KO制作了一个jQuery AutoComplete绑定。它允许按术语执行服务器端搜索建议。它还允许使用ID从服务器检索单个值来填充文本框或非输入html元素。到目前为止它工作正常。

当我将相同的绑定放入ContentTemplate时,在第一次呈现GridView期间,所有绑定都正常工作,从服务器检索项中每个id的数据,并将正确的Name注入到span中。

如果我尝试移动到网格的第二页,则从服务器检索主数据,我为每个行项获取新的ReviewObjectId-s,但未请求服务器(网络选项卡中没有请求) Chrome调试器),而且根本没有初始化绑定,名称显示与上一页完全相同。出现相同的行为,直到我转到寻呼机的最后一页或者直到在寻呼机中呈现更多的寻呼号码。有时单击下一页可以完成工作

过滤DataSource以显示每行的相同名称(每个项目都相同) target ReviewObjectId)经常显示相同的结果。

自定义绑定看起来像这样

<span data-bind="autoComplete:{apiOptions:{find:'/find-organization',get:'/get-organization', textItem:'name',valueItem:'id'},selectedValue: ReviewObjectId}"></span>

“find”是autocomplete api url,它会生成一系列建议。

“get”是一个填充api url,它通过Id(ReviewObjectId)提供实体。

TextItem和ValueItem用于将收到的JSON映射到viewModel。

我在GridView,DataPager和filter中使用与DataSource相同的GridViewDataSet,数据源总是根据页面和过滤器值正确过滤。

我做错了什么?我应该去哪里?

UPD:绑定:

ko.bindingHandlers.autoComplete = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        //initialize autocomplete with some optional options
        var options = valueAccessor().apiOptions;
        if (options && options.find) {
            var value = valueAccessor().selectedValue;
            var data = options.data || { data: {} };
            var searchOptions = { url: options.find, textItem: options.textItem, valueItem: options.valueItem };

            //init text on first load
            if (value() && options.get) {
                var fillOptions = { url: options.get, textItem: options.textItem, valueItem: options.valueItem };
                var fillData = value();
                var fillResult = function (data) {
                    if (data) {
                        if ($(element).is('input')) {
                            $(element).val(data);
                        } else {
                            $(element).html(data);
                        }
                    }
                };

                var promise = new Promise(function(resolve, reject) {
                    fetcher.search(fillOptions, fillData, fillResult);
                });
                if ($(element).is('input')) {
                    promise.then(fetcher.initAutoComplete(element, options, searchOptions, data, value));
                }
            }
            else {
                if ($(element).is('input')) {
                    fetcher.initAutoComplete(element, options, searchOptions, data, value);
                }
            }
        }
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        //var value = ko.utils.unwrapObservable(valueAccessor()) || null;
    }
};

var fetcher = {
    initAutoComplete: function (element, options, searchOptions, data, valueAccessor) {
        $(element).autocomplete({
            delay: options.delay || 300,
            minLength: options.minLength || 3,
            classes: {
                "ui-autocomplete": "dropdown-menu"
            },
            source: function (request, response) {
                //loading values from cache
                if (request.term) {
                    var cacheKey = searchOptions.url + '-' + request.term;
                    if (cacheKey in this.cache) {
                        response(this.cache[cacheKey]);
                        return;
                    }
                }
                //querying server, contract data MUST contain Term
                data.Term = request.term;
                this.search(searchOptions, data, response);
            }.bind(this),
            select: function (e, i) {
                valueAccessor(i.item.val);
                $(element).val(i.item.label);
            }
        });
    },
    search: function(options, data, response) {
        $.ajax({
            url: options.url,
            data: JSON.stringify(data),
            dataType: "json",
            type: "POST",
            contentType: "application/json; charset=utf-8",
            success: function(responseData) {
                var textItemName = options.textItem || "value";
                var valueItemName = options.valueItem || "key";

                //cache results if exist
                var cacheKey = '';
                if (Array.isArray(responseData)) { //search by term
                    var result = $.map(responseData,
                        function (item) {
                            return {
                                label: item[textItemName],
                                val: item[valueItemName]
                            }
                        });
                    if (result.length > 0) {
                        cacheKey = options.url + '-' + data.Term;
                        this.cache[cacheKey] = result;
                    }

                } else { //init by bound value
                    if (responseData[textItemName] && responseData[textItemName].length > 0) {
                        cacheKey = options.url + '-' + responseData[valueItemName];
                        this.cache[cacheKey] = responseData[textItemName];
                    }
                }
                //send result to response
                response(this.cache[cacheKey]);
            }.bind(this),
            error: function (responseData) {
                console.log("error");
            },
            failure: function (responseData) {
                console.log("failure");
            }
        });
    },
    cache: {}
}

1 个答案:

答案 0 :(得分:1)

我认为你忽略了绑定处理程序中的update事件。更改页面时,仅重新获取视图模型并更新绑定,不会重新获取页面。看看knockout.js文档如何使用update函数http://knockoutjs.com/documentation/custom-bindings.html

ko.bindingHandlers.yourBindingName = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // This will be called when the binding is first applied to an element
        // Set up any initial state, event handlers, etc. here
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever any observables/computeds that are accessed change
        // Update the DOM element based on the supplied values here.
    }
};

Knockout基本上会监视绑定初始化时触及的可观察对象的任何更改,然后在任何这些更改时调用update函数。