使用 Can.js 创建分页对象

Creating a Pagination object with Can.js

我正在创建一个分页可观察对象。我用一个集合实例化它,它有一个派生的 paginatedList,稍后我更新它的主列表(假设我已经过滤了数据)。显然,我希望派生列表能够识别并重新呈现视图,但事实并非如此。有些东西一定不能被束缚吧。 Here's a fiddle.

var Pagination = can.Map.extend({

    pageNumber: 1,

    pageSize: 10,

    isFirstPage: can.compute(function() {
        return this.attr('pageNumber') === 1;
    }),

    isLastPage: can.compute(function() {
        return this.attr('pageNumber') === this.attr('lastPage');
    }),

    lastPage: can.compute(function() {
        return Math.ceil(this.attr('list').attr('length') / this.attr('pageSize'));
    }),

    paginatedList: can.compute(function() {
        var pgn = this.attr('pageNumber'),
            pgs = this.attr('pageSize'),
            start = (pgn-1) * pgs,
            end = start + pgs;
        return this.attr('list').slice(start, end);
    }),

    prevPage: function() {
        if (!this.attr('isFirstPage'))
            this.attr('pageNumber', this.pageNumber-1);
    },

    nextPage: function() {
        if (!this.attr('isLastPage'))
            this.attr('pageNumber', this.pageNumber+1);
    },

    toPage: function(pageNumber) {
        this.attr('pageNumber', pageNumber);
    }
});

var paginated = new Pagination({ list: collection });

...模板看起来像...

<table>
    {{#paginated.paginatedList}}
        <tr>
            ....
        </tr>
    {{/paginated.paginatedList}}
</table>

然后,稍后,用户过滤列表...

paginated.attr('list').replace(collection.filter(function() {
    // return filtered collection
}))

但是视图没有重新呈现。什么没有绑定?

首先,尝试解释我认为在您的示例中发生的事情:

了解 can.compute 如何弄清楚如何 publish/emit 它发生了变化可能并不明显,但在您的示例中,计算 paginatedList 会发出它已经发生的事实当任何属性 pageNumberpageSizelist 更改时,您可以在使用 .attr() 访问器方法访问属性的计算中看到这一点。

can.List 中的 replace 方法替换列表 中的项目 而不是列表本身,因此 在这个例子中,.attr('list') 访问的 属性 实际上并没有改变(也没有 pageNumberpageSize), 它仍然是列表的同一个实例,只是有不同的项目 现在在里面,所以计算值不会更新,这反过来也不会更新模板。

作为对示例的 simple/naive 修复,您只需替换:

paginated.attr('list').replace(collection.filter(function() {
    return Math.random() < 0.5;
}));

paginated.attr('list', collection.filter(function() {
    return Math.random() < 0.5;
}));

...即替换整个列表,而不仅仅是列表中的项目。

这是一个 fiddle 的变化 http://jsfiddle.net/phx1jgq1/1/

当然这可能不是您要查找的内容,也许您实际上想要绑定到项目可能会更改的列表。 这里的问题是 this.attr('list').slice(start, end); 并没有真正绑定到列表的所有单个项目,而是类似于:

this.attr('list').filter(function(item, index){
    return index >= start && index < end;
});

...确实如此,因此您只需替换该行即可使其按预期工作。

这是一个 fiddle 的变化:http://jsfiddle.net/n2tygdud/1/

当然,如果过滤所有项目感觉不对,您可能想查看 define plugin,让您制定高级和特定行为can.Maps 像 getter、setter 和类型转换,并使用它来绑定到列表 add/remove 事件并相应地更新计算的 属性。