在 Knockout 视图模型中调用 jQuery 插件是一种有效模式吗?

Is calling jQuery plugins in a Knockout view model a valid pattern?

我经常创建自定义绑定处理程序来更改 DOM。但有时我会偶然发现我想将这种代码放入 subscribe 处理程序中的情况。

MyModel =
{
    this.name = ko.observable();

    this.name.subscribe(function()
    {
        // is it correct to do these kind of calls inside a model?
        $.ajax(
        {
            url: "... url that creates customer on server ...",
            success: function()
            {
                $(".container_element").noty(
                {
                    text: "Customer created!"
                }); 
            }
        });
    });
}

Noty 是一个 jQuery 弹出警报消息的插件,由于 Noty 实际上改变了 DOM,推荐的设计模式是什么?

如果您想在视图模型内部进行DOM操作,通常是一个危险信号[=46] =](有一些例外,最著名的是 the beforeRender & friends callbacks)。这是因为通常,使用 MVVM 风格的编程,你有一个 View lighty 依赖于 View Model(具有声明性绑定),并且没有依赖关系从您的视图模型到视图。这很好的两个主要原因:

  1. 您的 VM 未与视图紧密耦合,因此您可以轻松地创建依赖于同一视图模型的不同视图。
  2. 您的 VM 更容易进行单元测试,因为测试单元不需要 DOM 存在。

你已经提到了,你可能应该使用什么:custom binding handlers来自相关文档:

This is how to control how observables interact with DOM elements, and gives you a lot of flexibility to encapsulate sophisticated behaviors in an easy-to-reuse way.

我找不到 "Noty" 示例自定义绑定,因此您必须创建自己的(通常并不难)。您可以从 this jQuery UI custom binding 以及文档中获得一些灵感。

使用您的示例将其放入代码中,看起来这样:

ko.bindingHandlers.noty = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // Set up any initial state, event handlers, etc. here
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $el = $(element);
        if(!!value) { $($el).noty({text: value}); }
    }
};

// Mock $.ajax for this example
$.ajax = function(options) { options.success({}); }

MyModel = function()
{
    var self = this;

    this.name = ko.observable();
    this.myAlertMsg = ko.observable("");

    this.name.subscribe(function()
    {
        // is it correct to do these kind of calls inside a model?
        $.ajax(
        {
            url: "... url that creates customer on server ...",
            success: function()
            {
                self.myAlertMsg("Customer created!");
            }
        });
    });
}

ko.applyBindings(new MyModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.3.4/packaged/jquery.noty.packaged.min.js"></script>

<div data-bind="noty: myAlertMsg"></div>
<input data-bind="value: name" /> (blur input to finish editing name)

请注意,我们需要 var self = this 习惯用法才能轻松引用 success 回调中的视图模型。