从ko.computed访问Html元素

时间:2013-04-30 09:41:37

标签: javascript html dom knockout.js

是否可以从ko.computed函数访问绑定元素?

像这样的伪代码(为简洁起见而简化):

<h1 id="id1" data-bind="visible: myComputed">
<h1 id="id2" data-bind="visible: myComputed">
<h1 id="id3" data-bind="visible: myComputed">

...

self.myComputed = ko.computed(function(){
        return <BOUND_ELEMNT>.id == 'id2';
    });

仅导致显示的第二个元素。

注意:我知道我可以为每个元素分别计算,但在我的情况下是不可能的。

编辑:

好的 - 我会给出一个更准确的例子。以下内容与我的内容类似:

<section id="id1" data-bind="visible: myComputed1">A lot of code</section>
<section id="id2" data-bind="visible: myComputed2">different lots of code</section>
<section id="id3" data-bind="visible: myComputed3">Other lots of code</section>

...

// This field's value changes over time (between 'id1', 'id2' and 'id3').
// Some complex logic changes this field,
// and as a result only one h1 is showing at a time.
self.myField = ko.observable();
self.myComputed1 = ko.computed(function(){
        return self.myField() == 'id1';
    });
self.myComputed2 = ko.computed(function(){
        return self.myField() == 'id2';
    });
self.myComputed3 = ko.computed(function(){
        return self.myField() == 'id3';
    });

这是对DRY原则的丑恶违反,我想找到一种方法来重构它。上面的伪代码可以解决它,但我愿意接受建议......

3 个答案:

答案 0 :(得分:3)

您已经创建了一个简短的简化示例,通常很棒。但是,感觉您已经引入了XY-problem。因此,这个答案可能有用也可能没用。

您尝试在 ViewModel 中引入对 View 的依赖关系。它应该是相反的方式!这样的事情会更有意义:

<h1 data-bind="visible: myComputed, attr { id: myId }"></h1>

请注意使用attr binding来设置ID。您的ViewModel应该相应地构建:

var activeHeaderId = 'id2';

var viewModel = function(id) {
    var self = this;

    self.myId = ko.observable(id);

    self.myComputed = ko.computed(function() {
        return self.myId() === activeHeaderId;
    });
}

答案 1 :(得分:1)

注意:我将留下我的另一个答案作为问题第一点的答案,也许它会帮助其他用户绊倒这个问题。

更新中的问题是:

  

这是对DRY原则的丑恶违反,我想找到一种方法来重构它。

OP在评论中指出,关注给定示例的答案是首选。可以轻松地重构示例代码,使其不再违反DRY。 (PS。我仍然认为想要这种结构背后的“为什么”非常重要,但这并不妨碍我在给定样本的背景下回答这个问题。)


方法1 - 视图中的一个h1 - ViewModel中的一个项目

使用以下视图:

<h1 data-bind="attr: {id: myField}">

使用此ViewModel:

self.myField = ko.observable();

非常干,在功能上几乎相同(除了其他2个h1元素不仅是不可见的,它们甚至不在DOM中。)


方法2 - 视图中的多个h1 - ViewModel中的多个项目

将视图重构为列表结构(请参阅foreach binding中的注释4):

<!-- ko foreach: items -->
<h1 data-bind="attr: {id: myId}, 
               text: itemName, 
               visible: $root.currentItem().myId() === myId()">
</h1>
<!-- /ko -->

使用以下ViewModel:

var item = function(nr) {
    this.itemName = ko.observable("Item number " + nr);
    this.myId = ko.observable("id" + nr);
}

var viewModel = function() {
    this.items = ko.observableArray([new item(1), new item(2), new item(3)]);
    this.currentItem = ko.observable();
}

请参阅this fiddle了解演示。


方法3 - 视图中的一个h1 - ViewModel中的多个项目

使用此方法,您可以使用类似于方法2中的设置的列表,但只渲染currentitem。视图使用with binding

<!-- ko with: currentItem -->
<h1 data-bind="attr: {id: myId}, text: itemName"></h1>
<!-- /ko -->

ViewModel与方法2中的相同。有关演示,请参阅this fiddle

答案 2 :(得分:1)

创建一个自定义绑定处理程序,使用您的observable来触发可见性。像这样:

ko.bindingHandlers.idVisible = {
    update: function(element, valueAccessor) {
        var idUnwrapped = ko.utils.unwrapObservable(valueAccessor());
        if(idUnwrapped == $(element).attr('id'))
        {
            $(element).show();
        }
        else
        {
            $(element).hide();
        }
    }
};

更改您的HTML:

<h1 id="id1" data-bind="idVisible: headerId">Header 1</h1>
<h1 id="id2" data-bind="idVisible: headerId">Header 2</h1>
<h1 id="id3" data-bind="idVisible: headerId">Header 3</h1>

示例视图模型:

function ViewModel() {
    var self = this;

    self.headerId = ko.observable('id1');
}
var vm = new ViewModel();
ko.applyBindings(vm);

这是一个带有演示的jsFiddle,它会在两秒后更改headerId:http://jsfiddle.net/psteele/cq9GU/