开发者

Filter users by one keyword in a nested observableArray

I am trying to filter my users observableArray which has a nested keywords observableArray

based on a keywords observableArray on my viewModel.

When I try to use ko.utils.arrayForEach I get a stack overflow exception. See the code below, also posted in this jsfiddle

function User(id, name, keywords){
    return {
        id: ko.observable(id),
        name: ko.observable(name),
        keywords: ko.observableArray(keywords),
        isVisible: ko.dependentObservable(functi开发者_高级运维on(){

        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"));

And the View code:

<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>


Your isVisible dependentObservable has created a dependency on itself and is recursively trying to evaluate itself based on this line:

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

So, this creates a dependency on viewModel.users, because remove has to access the observableArray's underlying array to remove the user. At the point that the array is modified, subscribers are notified and one of the subscribers will be itself.

It is generally best to not change the state of any observables in a dependentObservable. you can manually subscribe to changes to a dependentObservable and makes your changes there (provided the dependentObservable does not depend on what you are changing).

However, in this case, I would probably instead create a dependentObservable at the viewModel level called something like filteredUsers. Then, return a version of the users array that is filtered.

It might look like this:

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);

You also should not need the dirty flag, as dependentObservables will be re-triggered when any observables that they access have changed. So, since it accesses selectedKeyword, it will get re-evaluated whenever selectedKeyword changes.

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

I hope that I properly understood your scenario.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜