1

我正在构建一个侧边栏来过滤主视图,例如John Lewis的侧边栏。我有代码工作,但它并不漂亮。

我知道在类似的行上有几个 SO 问题,但我无法完全理解我自己的用例。

我需要从服务器(例如通过 JSON)获取复选框的名称,以便在我的 ShopView 上动态创建 observableArrays。

它是这样的:

var data = {
    'gender' : [ ],
    'color' : [ ]
};

var filterMapping = {
    create: function( obj ) {
        return ko.observableArray( obj.data );
    }
}

var ShopView = new function() {

    var self = this;

    ko.mapping.fromJS( { filters: data }, filterMapping, self );

    // this is the bit I don't like
    this.filterChange = ko.computed(function () {
        for( var key in self.filters )  {
            var obj = self.filters[key]; 
             if( ko.isObservable(obj)){
                obj();                 
             }             
        }
    });

    this.filterChange.subscribe( function( ) {
        //make AJAX request for products using filter state
    });

}

我的 HTML 看起来和你期望的一样:

性别

    <ul>
        <li><input type="checkbox" value="male" data-bind="checked: filters.gender" />Male</li>
        <li><input type="checkbox" value="female" data-bind="checked: filters.gender" />Female</li>
    </ul>

正如我所说,它有效,但它并不好。在理想的世界中,我可以订阅 this.filters,例如

this.filters.subscribe( function() { 
    //make AJAX request for products using filter state
});

注意我不是想在客户端进行过滤——只是在动态绑定的复选框发生变化时更新我的​​视图模型。

有任何想法吗?谢谢!

4

1 回答 1

1

首先,映射插件应该被视为代码重复的辅助工具。我认为将映射插件本身视为解决方案并不是一个好主意。至少不是直接的。它还掩盖了您在 SO 上发布代码时发生的情况,因为我们看不到您正在使用的模型。只是一个想法。

现在,如果您想从服务器获取动态过滤器,并使用它们来过滤项目列表(就像您在商店中一样),我会这样做(这里是小提琴):

var FilterOption = function(name) {
    this.name = name;
    this.value = ko.observable(false);
};

var Filter = function(data) {
    var self = this;
    self.name = data.name;
    options = ko.utils.arrayMap(data.options, function(o) {
        return new FilterOption(o);
    });
    self.options = ko.observableArray(options);
    self.filteredOptions = ko.computed(function() {
        var options = []
        ko.utils.arrayForEach(self.options(), function(o) {
            if (o.value()) options.push(o.name);
        });
        //If no options, false represents no filtering for this type
        return options.length ? options : false;
    });
};

var ViewModel = function(data) {
    var self = this;
    self.items = ko.observableArray(data.items);
    filters = ko.utils.arrayMap(data.filters, function(i) {
        return new Filter(i);
    });
    self.filters = ko.observableArray(filters);
    self.filteredItems = ko.computed(function() {
        //Get the filters that are actually active
        var filters = ko.utils.arrayFilter(self.filters(), function(o) {
            return o.filteredOptions();
        });
        //Remove items that don't pass all active filter
        return ko.utils.arrayFilter(self.items(), function(item) {
            var result = true;
            ko.utils.arrayForEach(filters, function(filter) {
                var val = item[filter.name.toLowerCase()];
                result = filter.filteredOptions().indexOf(val) > -1;
            });
            return result;
        });
    });
};

下一个明显的步骤是添加对具有多个属性或选项属性的项目的支持,但这应该为您提供基本概念。您有一个过滤器列表,每个过滤器都有任意数量的选项(可叠加),并且您使用计算项目数组来存储过滤项目的结果。


编辑:要使用 ajax 订阅获取项目,您可以将FilteredItemsprop 替换为获取所选过滤器的计算值,然后订阅它,如下所示:

var ViewModel = function(data) {
    var self = this;
    self.items = ko.observableArray(data.items);
    filters = ko.utils.arrayMap(data.filters, function(i) {
        return new Filter(i);
    });
    self.filters = ko.observableArray(filters);
    self.selectedFilters = ko.computed(function() {
        ko.utils.arrayFilter(self.filters(), function(o) {
            return o.filteredOptions();
        });
    });
    self.selectedFilters.subscribe(function() {
        //Ajax request that updates self.items()
    });
};
于 2013-01-23T20:19:46.180 回答