DotVVM:在GridViewTemplateColumn ContentTemplate中使用自定义绑定
我为KO做了一个jQuery自动完成绑定。它允许在服务器端按术语搜索建议。它还允许用ID从服务器检索到的单个值填充文本框或非输入html元素 当我将相同的绑定放置到ContentTemplate中时,在GridView的第一次呈现期间,所有绑定都可以正常工作,条目中每个id的数据都从服务器中检索,正确的名称被注入到范围中 如果我试图移动到网格的第二个页面,则会从服务器检索主数据,我会为每个行项目获取新的ReviewObjectId-s,但不会请求服务器(chrome debugger的“网络”选项卡中没有请求),而且绑定根本没有初始化,名称显示与前一页完全相同。在我转到寻呼机中的最后一页或在寻呼机中呈现更多的页码之前,通常都会发生相同的行为。有时单击下一页可以完成此任务 筛选数据源以显示每行的相同名称(每个项具有相同的 target ReviewObjectId)通常显示相同的结果 自定义绑定如下所示DotVVM:在GridViewTemplateColumn ContentTemplate中使用自定义绑定,gridview,knockout.js,data-binding,dotvvm,contenttemplate,Gridview,Knockout.js,Data Binding,Dotvvm,Contenttemplate,我为KO做了一个jQuery自动完成绑定。它允许在服务器端按术语搜索建议。它还允许用ID从服务器检索到的单个值填充文本框或非输入html元素 当我将相同的绑定放置到ContentTemplate中时,在GridView的第一次呈现期间,所有绑定都可以正常工作,条目中每个id的数据都从服务器中检索,正确的名称被注入到范围中 如果我试图移动到网格的第二个页面,则会从服务器检索主数据,我会为每个行项目获取新的ReviewObjectId-s,但不会请求服务器(chrome debugger的“网络”选
<span data-bind="autoComplete:{apiOptions:{find:'/find-organization',get:'/get-organization', textItem:'name',valueItem:'id'},selectedValue: ReviewObjectId}"></span>
我认为您正在忽略绑定处理程序中的更新事件。更改页面时,仅会重新蚀刻视图模型并更新绑定,而不会重新蚀刻页面。查看knockout.js文档,了解如何使用
update
功能:
Knockout基本上会在绑定初始化时观察所接触的可观察对象的任何变化,然后在这些变化发生时调用
update
函数。实际上,autocomplete是在select function
中设置绑定属性的值,并更新文本,现在,它不应该对绑定属性的更改做出反应。重点是,当您更改GridView的页面时,其中的绑定只会更新,页面不会每次都重新蚀刻。更新甚至不会触发,我的意思是,在分页期间,仅当调用init时,才会调用更新。但是如果页面的大小与前一页不同,那么一切都正常。例如:页面大小为30。我们有6页,最后一页只有3项。1页绑定有效,2-3-4-5页的organization name字段值相同,且未输入绑定的初始化或更新函数,6页正常,绑定有效,值更改。开始分页回到5-绑定工作,4-3-2-1-都一样。可能是因为页面大小改变了,所以整个表都重新初始化了。但是当页面大小不变时,它只会更新。实际上你是对的。KO足够聪明,可以忽略空的更新函数。为更新做了一些重构&一切都很好。非常感谢。
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: {}
}
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.
}
};