【问题标题】:Accessing html elements from knockout view models从淘汰视图模型访问 html 元素
【发布时间】:2015-06-19 21:58:58
【问题描述】:

我创建了一个包含引导模式对话框的敲除组件,我正在注册和加载(作为 AMD 模块)如下:

// Register dialogs as components
ko.components.register('create-user-dialog', {
    viewModel: { require: 'Features/Users/Index/CreateController' },
    template: { require: 'text!Features/Users/Index/CreateDialog.html' }
});

模板 HTML 包含如下标签:

<div class="modal fade" id="create-user-modal" tabindex="-1" role="dialog">
...
</div>

淘汰视图模型扩展了一个基类,看起来像这样(打字稿):

export = CreateController

class CreateController extends Controllers.BootstrapModalController {

    constructor() {
        super($('#create-user-modal'));
    }
}

class BootstrapModalController {
    public dialog: JQuery;

    constructor(dialog: JQuery) {
        // Save a reference to the modal element so we can 
        // show/hide the dialog from the controller
        this.dialog = dialog;
    }

    // Show the modal window
    showModal = () => {
        this.dialog.modal({
            show: true,
            backdrop: 'static'
        });
    }

    // Close the modal window
    closeModal = () => {
        this.dialog.modal('hide');
    }
}

最后,我在我的页面中使用组件如下:

<div data-bind="component: 'create-user-dialog'"></div>

这可行,但有点混乱,因为CreateController 在构造函数中做了一些我认为有点奇怪的事情——即使用一个 jquery 选择器,它与模板中的 html 紧密耦合。

在某种程度上,这是不可避免的,但我认为这可能会更好一些。如果可能的话,我希望 BootstrapModalController 基类能够自动确定与 Bootstrap 模态相对应的 DOM 元素......这样 showModalcloseModal 就可以工作,只要模板实际上包含引导模式对话框和后代不必在构造函数中显式传入 id 或 jquery 对象。

我认为它可能能够使用类似jQuery find 的方法在组件的HTML 模板中寻找具有CSS 类modal 的元素。但是,为了做到这一点,我需要能够获取 Knockout 绑定组件的视图模型所针对的 rootNode(即&lt;div data-bind="component: 'create-user-dialog'"&gt;&lt;/div&gt; 元素)。

有没有办法从该组件的 javascript 视图模型中计算出该组件的 html rootNode?

【问题讨论】:

  • 处理视图和视图模型之间的绑定是淘汰赛中 bindingHandlers 的职责。我的建议是您的 ViewModel 可以有一个名为 isVisible 或类似名称的属性,并且您为模式创建一个 bindingHandler,绑定到该属性。 bindingHandler 提供了您绑定到(来自 ViewModel)的(潜在可观察的)值以及应用了 bindingHandler 的元素。只有 bindingHandler 知道引导 DOM 操作的 API,而不是让 ViewModel 拥有这些知识。
  • 谢谢罗伯特。我想我找到了一个解决方案,就像你所说的那样。所以基本上,如果你想在 viewmodel 和 dom 之间(在任一方向)推送东西,绑定处理程序就是答案。

标签: javascript html mvvm knockout.js typescript


【解决方案1】:

您可以使用自定义绑定,如http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html 文章中所述,以便使用 Knockout 包装 HTML/JS 元素,例如jquery 按钮绑定:

ko.bindingHandlers.jqButton = {
    init: function(element, valueAccessor) {
        var options = valueAccessor() || {};
        $(element).button(options);
    }
};

用法:

data-bind="jqButton: { ...some options... }"

在自定义绑定中,您可以访问应用绑定的元素。

【讨论】:

  • 感谢您的回答......虽然这不是我想要的(我想要的是相反的 - 将对 DOM 元素的引用注入到视图模型中......而不是将视图模型的东西推到DOM。
【解决方案2】:

好的,所以答案似乎是使用绑定处理程序。我找到了this snippet,这似乎可以满足我的要求:

ko.bindingHandlers.element = {
    init: function(element, valueAccessor) {
      var value = valueAccessor();
      value(element);
    }
};

作者演示的:

<canvas width="100" height="60" data-bind="element: yourObservable"></canvas>

一切似乎都相当轻松......唯一的事情是,在我的情况下,我必须从元素创建一个 JQuery 对象,如下所示:

ko.bindingHandlers.element = {
    init: function(element, valueAccessor) {
      var value = valueAccessor();
      value($(element));
    }
};

感谢罗伯特的提示!

【讨论】:

  • 您还可以通过“$element”上下文属性将当前元素传递给模型:data-bind="someBinding: { someParameter: $element}"。我不确定这是否适用于组件。
  • 我更喜欢自定义绑定,因为标记应该取决于模型,而不是其他。将元素传递给模型破坏了 MVVM 概念 IMO ;)。
  • 感谢您的认可,但这不是我的建议。 :) 我同意@TSV 的观点,即您应该避免将 DOM 元素推入 ViewModel。 BindingHandler 用于处理 DOM 和 ViewModel 之间的绑定。这将使依赖关系保持在 View 对 ViewModel 的依赖,而不是 ViewModel 对 View (DOM) 的依赖。鉴于您将 DOM 元素推送到 ViewModel 上的属性中的方法,您的 ViewModel 会获得对 View (DOM) 的依赖,这使得测试变得更加困难。
  • 确实如此,但是视图模型中的方法是 showModal 和 closeModal...鉴于这些方法的性质,我认为对视图的一些依赖是不可避免的。我想不是所有用例都可以使用 jasmine 和单元测试之类的东西轻松测试......这可能是量角器和回归测试是必要的情况之一。
  • @JamesCrosswell 您可以轻松地让 showModal 和 closeModal 方法设置一个可观察的 bool 属性isShowingModal。这样 bindingHandler 就可以绑定到该 bool 属性并根据该值显示/隐藏模式。如果您愿意,我可以写一个示例来说明我的意思。请告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多