如何避免一次又一次地对元素应用绑定?

时间:2012-10-14 21:22:41

标签: web-applications knockout.js knockout-2.0

我有以下视图模型,用于编辑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应用的绑定,或者如何仅应用绑定一次,并更新我的视图模型,而不是为每次编辑重新创建它。我更喜欢第一种方法。视图模型不应具有单例特征。

2 个答案:

答案 0 :(得分:3)

通常,您可能需要执行类似创建observable以保存当前所选记录的操作,然后使用withtemplate绑定对其进行绑定。

所以,你会有一个像:

这样的编辑器
<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,你可以在这里找到:

https://github.com/rniemeyer/knockout-classBindingProvider