我有以下视图模型,用于编辑HTML表单中的Person对象:
function PersonModel(person) {
var self = this;
self.id = ko.observable(person.Id);
self.firstName = ko.observable(person.FirstName);
self.surname = ko.observable(person.Surname);
self.email = ko.observable(person.Email);
self.cell = ko.observable(person.Cell);
self.save = function (data) {
savePerson(data);
};
}
当用户想要编辑Person时,我将其新的实例绑定到编辑表单,如下所示:
function editPerson(person) {
var url = "@Url.Action("EditJson", "Person")";
$.getJSON(url, function (data) {
$("#person-detail").css("display", "block");
ko.applyBindings(new PersonModel(data), $("#person-detail")[0]);
});
}
在表单中,我将click事件绑定到视图模型中的save方法:
<a href="#" data-bind="click: save">Update</a>
我现在遇到在编辑单人记录时运行多个数据库更新的问题,我认为这是因为我在同一元素(编辑弹出窗口)上多次调用applyBindings
。我可以确认这一点,因为我在调用ApplyBindings时执行了很多数据库编辑。
现在,我需要知道如何删除applyBindings
应用的绑定,或者如何仅应用绑定一次,并更新我的视图模型,而不是为每次编辑重新创建它。我更喜欢第一种方法。视图模型不应具有单例特征。
答案 0 :(得分:3)
通常,您可能需要执行类似创建observable以保存当前所选记录的操作,然后使用with
或template
绑定对其进行绑定。
所以,你会有一个像:
这样的编辑器<div data-bind="with: currentItem">
...
</div>
然后,您将使用当前数据填充它:
this.currentItem(new PersonModel(data));
这样您只应用绑定一次,并且不会遇到多个事件处理程序等问题。with
绑定也只会在填充对象时呈现其内容,因此它不会显示currentItem
为空时的任何内容。
答案 1 :(得分:0)
Ryan的解决方案对大多数情况来说都是最好的。如果您真正处理可以在页面上多次实例化的JS和HTML代码模块,您可能需要查看ko.cleanNode()
或ko.removeNode()
。
这两种方法都由KO内部使用来处理内存泄漏和绑定清理。在最近的一个项目中,我有一个包含几个动态添加的产品行的表。这些产品中的每一个都必须是内联可编辑的。我没有为每一行复制表格html,而是选择设置一种KO模块模式,其中html动态加载到表格行中,应用绑定,并在完成编辑后删除绑定。
这是我继承的基本AMD模块:
/**
Prototype object for creating individual KnockoutJS modules
@module Shared
@class ko-module-prototype
@namespace
@static
**/
define(["knockout", 'jquery', 'Shared/js/helpers'], function (ko, $, helpers) {
var exports = {};
/**
KO viewModel
@property vm
@static
**/
exports.vm = {};
/**
KO bindings
@property bindings
@static
**/
exports.bindings = {};
/**
Optional binding namespace for avoiding bindings getting overwritten
@property bindingNamespace
@static
**/
exports.bindingNamespace = null;
/**
Whether bindings are registered with KO or not
@property bindingsRegistered
@static
**/
exports.bindingsRegistered = false;
/**
The HTML node wrapping the area we wish to apply KO bindings
@property $wrapperNode
@static
**/
exports.$wrapperNode = $("body");
/**
Optional html string to be loaded in as a template
@property template
@static
**/
exports.template = null;
/**
Injects html, registers bindings, and applies bindings
@method start
@static
**/
exports.start = function ($wrapperNode) {
// Update wrapper node
if ($wrapperNode) this.$wrapperNode = $wrapperNode;
this._openNodeId = "mod_" + helpers.uniqueId();
var $targetNode = $wrapperNode;
// Insert template html
// Wrap in a div
if (this.template) {
this.$wrapperNode.html($("<div></div>").attr("id", this._openNodeId).html(this.template));
$targetNode = $wrapperNode.find("#" + this._openNodeId);
}
// Register bindings
// Class binding provider has to be setup first...
if (!this.bindingsRegistered) {
this.bindingsRegistered = true;
var register = this.bindings;
if (this.bindingNamespace !== null) {
register = { };
register[this.bindingNamespace] = this.bindings;
}
ko.bindingProvider.instance.registerBindings(register);
}
ko.applyBindings(this.vm, $targetNode[0]);
};
/**
Removes html, and un-applies bindings
@method end
@static
**/
exports.end = function () {
var $openNode = $("#" + this._openNodeId)[0];
if (this.template !== null) {
ko.removeNode($openNode);
} else {
ko.cleanNode($openNode);
}
};
return exports;
});
它使用Ryan的KO classBindingProvider,你可以在这里找到: