3

是否可以从 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 原则的严重违反,我想找到一种方法来重构它。上面的伪代码可能会解决它,但我愿意接受建议......

4

3 回答 3

3

您创建了一个简短的简化示例,这通常很棒。但是,感觉就像您引入了一个XY 问题。因此,这个答案可能有帮助,也可能没有帮助。

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

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

注意使用attr 绑定来设置 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;
    });
}
于 2013-04-30T10:01:11.127 回答
1

注意:我将我的其他答案作为问题的第一部分的答案,也许它会帮助其他用户偶然发现这个问题。

更新中的问题是:

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

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


方法 1 - h1View 中的一项 - ViewModel 中的一项

使用以下视图:

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

使用此视图模型:

self.myField = ko.observable();

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


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

将视图重构为列表结构(参见foreach 绑定中的注释 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();
}

请参阅此小提琴以进行演示。


方法 3 - h1View 中的一个 - ViewModel 中的多个项目

使用此方法,您可以使用方法 2 中的设置列表,但仅呈现当前项。视图利用with绑定

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

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

于 2013-04-30T13:19:58.897 回答
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/

于 2013-04-30T15:00:38.943 回答