Фильтровать пользователей по одному ключевому слову во вложенном observableArray - PullRequest
3 голосов
/ 11 июня 2011

Я пытаюсь отфильтровать мой users observableArray с вложенными ключевыми словами observableArray
на основе ключевых слов observableArray в моей viewModel.

Когда я пытаюсь использовать 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 Ответ

5 голосов
/ 12 июня 2011

Ваш isVisiblependentObservable создал зависимость от себя и рекурсивно пытается оценить себя на основе этой строки:

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

Итак, это создает зависимость от viewModel.users, так как remove должен получить доступ к базовому массиву observableArray, чтобы удалить пользователя. В момент изменения массива подписчики уведомляются, и один из подписчиков будет сам.

Обычно лучше не менять состояние каких-либо наблюдаемых в зависимой наблюдаемой. вы можете вручную подписаться на изменения зависимого объекта и вносить в него изменения (при условии, что зависимый объект не зависит от того, что вы меняете).

Однако в этом случае я бы, вероятно, вместо этого создал бы зависимый объект наблюдения на уровне viewModel, который называется что-то вроде filteredUsers. Затем верните версию отфильтрованного массива пользователей.

Это может выглядеть так:

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

Вам также не нужно указывать грязный флаг, так как зависимые наблюдательные объекты будут повторно запускаться при изменении любых наблюдаемых объектов, к которым у них есть доступ. Таким образом, поскольку он обращается к selectedKeyword, он будет переоцениваться при каждом изменении selectedKeyword.

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

Надеюсь, я правильно понял ваш сценарий.

...