(七)Knockout 创建自定义绑定

创建自定义绑定

你可以创建自己的自定义绑定 – 没有必要非要使用内嵌的绑定(像click,value等)。你可以你封装复杂的逻辑或行为,自定义很容易使用和重用的绑定。例如,你可以在form表单里自定义像grid,tabset等这样的绑定。

注册您的绑定

添加子属性到ko.bindingHandlers来注册你的绑定: 

<script type="text/javascript">
    ko.bindingHandlers.yourBindName = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext){
            ///绑定时,设置任何初始状态,事件处理程序
        },
        update: function (element,valueAccessor,allBindingsAccessor,viewModel, bindingContext) {
            ///绑定之后应用于dom元素上,然后观察dom元素的变化,进行相应调用更新
        }
    };
</script>

然后就可以在任何DOM元素上使用了:

<div data-bind="yourBindingName: someValue"> </div>

你实际上没必要把init和update这两个callbacks都定义,你可以只定义其中的任意一个。

update回调

当管理的observable改变的时候,KO会调用你的update callback函数,然后传递以下参数:

element — 使用这个绑定的DOM元素
valueAccessor —JavaScript函数,通过valueAccessor()可以得到应用到这个绑定的model上的当前属性值。
allBindingsAccessor —JavaScript函数,通过allBindingsAccessor ()得到这个元素上所有model的属性值。
viewModel — 传递给ko.applyBindings使用的 view model参数,如果是模板内部的话,那这个参数就是传递给该模板的数据。
bindingContext保持在对象绑定上下文这种元件的结合。此对象包括特殊特性包括$parent,$parents, 和$root可用于访问数据 , 必然对这方面的祖先。

例如,你可能想通过 visible绑定来控制一个元素的可见性,但是你想让该元素在隐藏或者显示的时候加入动画效果。那你可以自定义自己的绑定来调用jQuery的slideUp/slideDown 函数:

 <script src="../../lib/jquery/jquery-1.9.1.js"></script>
 <script src="../../lib/knockout/knockout-3.4.0.js"></script> 


<div data-bind="slideVisible: giftWrap, slideDuration:600">You have selected the option</div>
<label><input type="checkbox" data-bind="checked: giftWrap" /> Gift wrap</label>
 
<script type="text/javascript">
    ko.bindingHandlers.slideVisible = {
        update: function (element, valueAccessor, allBindings) {
            // First get the latest data that we're bound to
            var value = valueAccessor();

            // Next, whether or not the supplied model property is observable, get its current value
            var valueUnwrapped = ko.unwrap(value);

            // Grab some more data from another binding property
            var duration = allBindings.get('slideDuration') || 400; // 400ms is default duration unless otherwise specified

            // Now manipulate the DOM element
            if (valueUnwrapped == true)
                $(element).slideDown(duration); // Make the element visible
            else
                $(element).slideUp(duration);   // Make the element invisible
        }
    };

    var viewModel = {
        giftWrap: ko.observable(true)
    };
    ko.applyBindings(viewModel);
</script>

init回调

Knockout在DOM元素使用自定义绑定的时候会调用你的init函数。init有两个重要的用途:

1.为Dom元素设置初始值

2.注册事件句柄,例如当用户点击或者编辑Dom元素的时候,你可以改变相关的observable值的状态。

KO会传递和update回调函数一样的参数。

继续上面的例子,你可以像让slideVisible在页面第一次显示的时候设置该元素的状态(但是不使用任何动画效果),而只是让动画在以后改变的时候再执行。你可以这样来做:

ko.bindingHandlers.slideVisible = {
    init: function(element, valueAccessor) {
        var value = ko.unwrap(valueAccessor()); // Get the current value of the current property we're bound to
        $(element).toggle(value); // jQuery will hide/show the element depending on whether "value" or true or false
    },
    update: function(element, valueAccessor, allBindings) {
        // Leave as before
    }
};

这就是说giftWrap的初始值声明的是false(例如giftWrap: ko.observable(false)),然后让初始值会让关联的DIV隐藏,之后用户点击checkbox的时候会让元素显示出来。

DOM事件之后更新observable值

你已经知道了如何使用update回调,当observable值改变的时候,你可以更新相关的DOM元素。但是其它形式的事件怎么做呢?比如当用户对某个DOM元素有某些action操作的时候,你想更新相关的observable值。

你可以使用init回调来注册一个事件句柄,这样可以改变相关的observable值,例如

<h2>DOM事件之后更新observable值</h2>
<p>Name: <input data-bind="hasFocus: editingName" /></p>
<div data-bind="visible: editingName">You're editing the name</div>
<button data-bind="enable: !editingName(), click:function() { editingName(true) }">Edit name</button>
 
<script type="text/javascript">
    ko.bindingHandlers.hasfocus = {
        init: function (element, valueAccessor) {
            $(element).focus(function () {
                var value = valueAccessor();
            });
            $(element).blur(function () {
                var value = valueAccessor();
                value(false);
            });
        },
        update: function (element, valueAccessor) {
            var value = valueAccessor();
            if (ko.utils.unwrapObservable(value)) {
                element.focus();
            }
            else {
                elemen.blur();
            }
        }
    };

    var viewModel = {
        editingName: ko.observable(true)
    };
    ko.applyBindings(viewModel);
</script>

当元素获得焦点和失去焦点

添加自定义清理逻辑

在一个典型的Knockout应用中,DOM元素会动态地添加和移除,例如使用模板绑定或者控制流绑定(if, ifnot, with, 和 foreach)1。当创建一个自定义绑定时,你可能希望在Knockout移除与你的自定义绑定相关联的元素时执行一些清理逻辑1。

注册一个元素移除时的回调函数

要注册一个在节点被移除时运行的函数,你可以调用ko.utils.domNodeDisposal.addDisposeCallback (node, callback)1。举个例子,假设你创建了一个自定义绑定来实例化一个小部件。当绑定的元素被移除时,你可能想要调用小部件的destroy方法:

ko.bindingHandlers.myWidget = {
    init: function(element, valueAccessor) {
        var options = ko.unwrap(valueAccessor()),
            $el = $(element);
 
        $el.myWidget(options);
 
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            // This will be called when the element is removed by Knockout or
            // if some other part of your code calls ko.removeNode(element)
            $el.myWidget("destroy");
        });
    }
};

设置计算观察值或者手动订阅自动清理

If you create a computed observable in a custom binding, rather than using a custom disposal callback, you can set the computed to dispose automatically when the node is removed. When constructing the computed observable, provide the node using the disposeWhenNodeIsRemoved option:

如果你在一个自定义绑定中创建了一个计算观察值,而不是使用一个自定义清理回调函数,你可以设置计算观察值在节点被移除时自动清理1。当构造计算观察值时,使用disposeWhenNodeIsRemoved选项提供节点:

ko.computed({
    read: function () {
        element.title = ko.unwrap(valueAccessor());
    },
    disposeWhenNodeIsRemoved: element
});

If a binding includes a manual subscription, this can be set to dispose automatically by calling its disposeWhenNodeIsRemoved method:
如果一个绑定包含了一个手动订阅,这也可以通过调用它的disposeWhenNodeIsRemoved方法来设置为自动清理:

var titleSubscription = someObservable.subscribe(function (val) {
    element.title = val;
});

titleSubscription.disposeWhenNodeIsRemoved(element);

覆盖外部数据的清理

When removing an element, Knockout runs logic to clean up any data associated with the element. As part of this logic, Knockout calls jQuery’s cleanData method if jQuery is loaded in your page. In advanced scenarios, you may want to prevent or customize how this data is removed in your application. Knockout exposes a function, ko.utils.domNodeDisposal.cleanExternalData(node), that can be overridden to support custom logic. For example, to prevent cleanData from being called, an empty function could be used to replace the standard cleanExternalData implementation:
当移除一个元素时,Knockout会运行一些逻辑来清理与该元素相关联的数据1。作为这个逻辑的一部分,如果jQuery加载到了你的页面上,Knockout会调用jQuery的cleanData方法1。在一些高级场景中,你可能想要阻止或者自定义这些数据如何在你的应用中被移除1。Knockout暴露了一个函数,ko.utils.domNodeDisposal.cleanExternalData (node),它可以被覆盖以支持自定义逻辑1。例如,为了阻止cleanData被调用,可以使用一个空函数来替换标准的cleanExternalData实现:

ko.utils.domNodeDisposal.cleanExternalData = function () {
    // Do nothing. Now any jQuery data associated with elements will
    // not be cleaned up when the elements are removed from the DOM.
};
posted @ 2019-01-28 16:23  【唐】三三  阅读(506)  评论(0编辑  收藏  举报