在嵌套的observableArray中按一个关键字过滤用户

时间:2011-06-11 17:04:39

标签: javascript stack-overflow observablecollection knockout.js

我正在尝试过滤我的users observableArray,它有一个嵌套关键字observableArray
基于我的viewModel上的关键字observableArray。

当我尝试使用ko.utils.arrayForEach时,我收到了堆栈溢出异常。请参阅下面的代码,也发布在this jsfiddle

function User(id, name, keywords){
    return {
        id: ko.observable(id),
        name: ko.observable(name),
        keywords: ko.observableArray(keywords),
        isVisible: ko.dependentObservable(function(){

        var visible = false;            
        if (viewModel.selectedKeyword() || viewModel.keywordIsDirty()) {
            ko.utils.arrayForEach(keywords, function(keyword) {
                if (keyword === viewModel.selectedKeyword()){
                    visible = true;
                }
            });
            if (!visible) {
                viewModel.users.remove(this);
            }
        }
        return visible;
      })
    }
};

function Keyword(count, word){
    return{
        count: ko.observable(count),
        word: ko.observable(word)
    }
};

var viewModel = {
    users: ko.observableArray([]),
    keywords: ko.observableArray([]),
    selectedKeyword: ko.observable(),
    keywordIsDirty: ko.observable(false)
}

viewModel.selectedKeyword.subscribe(function () {
    if (!viewModel.keywordIsDirty()) {
        viewModel.keywordIsDirty(true);
    }
});

ko.applyBindings(viewModel);

for (var i = 0; i < 500; i++) {
    viewModel.users.push(
        new User(i, "Man " + i, ["Beer", "Women", "Food"])
    )
}

viewModel.keywords.push(new Keyword(1, "Beer"));
viewModel.keywords.push(new Keyword(2, "Women"));
viewModel.keywords.push(new Keyword(3, "Food"));
viewModel.keywords.push(new Keyword(4, "Cooking"));

查看代码:

<ul data-bind="template: { name: 'keyword-template', foreach: keywords }"></ul><br />
<ul data-bind="template: { name: 'user-template', foreach: users }"></ul>

<script id="keyword-template" type="text/html">
    <li>
        <label><input type="radio" value="${word}" name="keywordgroup" data-bind="checked: viewModel.selectedKeyword" /> ${ word }<label>
    </li>    
</script>

<script id="user-template" type="text/html">
    <li>
        <span data-bind="visible: isVisible">${ $data.name }</span>
    </li>    
</script>

1 个答案:

答案 0 :(得分:5)

您的isVisible dependentObservable已经创建了对自身的依赖,并递归尝试根据此行评估自己:

        if (!visible) {
            viewModel.users.remove(this);
        }

因此,这会创建对viewModel.users的依赖,因为remove必须访问observableArray的底层数组才能删除用户。在修改阵列时,会通知订户,其中一个订户就是自己。

通常最好不要更改dependentObservable中任何observable的状态。您可以手动订阅对dependentObservable的更改并在那里进行更改(前提是dependentObservable不依赖于您要更改的内容)。

但是,在这种情况下,我可能会在viewModel级别创建一个名为filteredUsers的dependentObservable。然后,返回已过滤的用户数组的版本。

可能看起来像这样:

viewModel.filteredUsers = ko.dependentObservable(function() {
    var selected = viewModel.selectedKeyword();
    //if nothing is selected, then return an empty array
    return !selected ? [] : ko.utils.arrayFilter(this.users(), function(user) {
        //otherwise, filter on keywords.  Stop on first match.
        return ko.utils.arrayFirst(user.keywords(), function(keyword) {
            return keyword === selected;
        }) != null; //doesn't have to be a boolean, but just trying to be clear in sample
    });
}, viewModel);

您也不应该需要脏标志,因为当他们访问的任何可观察对象发生更改时,将重新触发dependentObservables。因此,由于它访问selectedKeyword,只要selectedKeyword发生更改,它就会被重新评估。

http://jsfiddle.net/rniemeyer/mD8SK/

我希望我能正确理解你的情景。