10

PagerJS 可以获取 URL 参数并将它们绑定到模型。例如,在 PagerJS 网站的这个例子中(见链接),当你点击链接时,它会导航到#/user?first=Philip&last=Fry数据绑定的子页面,显示“Philip Fry”:

<a class="btn" data-bind="page-href: {path: 'user', params: {first: 'Philip', last: 'Fry'}}">Send parameter to page</a>

<div data-bind="page: {id: 'user', params: ['first','last']}" class="well-small">
    <div>
        <span>First name:</span>
        <span data-bind="text: first"></span>
    </div>
    <div>
        <span>Last name:</span>
        <span data-bind="text: last"></span>
    </div>
</div>

这是一种单向绑定:如果 observable 发生变化,由于用户在页面上的操作,URL 将不会被更新。

使用 PagerJS 时保持 URL 参数与 observables 同步的推荐方法是什么?

我想将用户动态创建的搜索条件存储在 URL 参数中,这些条件是通过选择一堆 UI 控件生成的,这样他/她就可以与其他人共享 URL 或将其添加为书签,当然,所有这些都无需重新加载页面。

4

2 回答 2

5

免责声明:我对 pager.js 一无所知,但我希望我的一般淘汰赛经验仍然可以提供帮助。

查看示例,page绑定似乎使用来自 url 的初始值创建 observables。我的第一个直觉是扩展这个绑定并确保订阅每个这些值都会更新 URL。

让我们命名这个绑定twoway-page

ko.bindingHandlers["twoway-page"] = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // Call pager.js' page binding
    ko.bindingHandlers.page.init(element, valueAccessor, allBindings, viewModel, bindingContext);

    // ...
  }
}

并在示例的绑定上调用它:

<div data-bind="twoway-page: {
                  id: 'start', 
                  params: ['first','last']
                 }">

调用后page.init,页面绑定扩展了视图模型,将params数组中定义的可观察对象添加到viewModel对象中。这意味着我们可以订阅这些 observables 的变化。

下一个挑战是计算正确的哈希。我查看了page-href绑定如何计算其href属性。原来它用于pager.page.path()具有pathandparams属性的对象。例如:

var hash = pager.page.path({
  path: "user",
  params: {
    "first": "John",
    "last": "Doe"
  }
});

我试图在计算的 observable 中构造一个类似的对象。

// ... 
var options = valueAccessor();
var pathObj = ko.computed(function() {

    var result = {
        path: options.id,
        params: {}
    };

    options.params.forEach(function(param) {
        result.params[param] = viewModel[param]();
    });

    return result;
}).extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });

我找不到通过 pager.js 方法更新哈希的“干净”方法,但我确实注意到在内部,pagerjs 用于location.hash = "newhash"设置一个值(尽管似乎也有一个 history/html5 替代方案......) . 无论如何,我们可以订阅我们的 observable 来更新哈希:

// ...
pathObj.subscribe(function(newValue) {
    location.hash = pager.page.path(newValue);
});

现在,text我们将使用textInput绑定而不是示例中的绑定,以便我们可以更新值:

<div>
  <span>First name:</span>
  <input type="text" data-bind="textInput: first">
</div>

所以,总结一下:我最好的猜测是

  1. 扩展现有的 pager.js 绑定
  2. 在 URL 中创建对所有需要更新的 observable 的订阅
  3. 当值改变时自动更新哈希;使用rateLimit扩展来防止更新过载

使用位置哈希做一些事情有点难以在小提琴中展示,所以我记录了我的概念证明的 gif:

使用自定义 pagerjs 绑定实时更新哈希

完整的自定义绑定代码为:

ko.bindingHandlers["twoway-page"] = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        ko.bindingHandlers.page.init(element, valueAccessor, allBindings, viewModel, bindingContext)

        var options = valueAccessor();

        var pathObj = ko.computed(function() {

            var result = {
                path: options.id,
                params: {}
            };

            options.params.forEach(function(param) {
                result.params[param] = viewModel[param]();
            });

            return result;
        }).extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });

        pathObj.subscribe(function(newValue) {
            location.hash = pager.page.path(newValue);
        })

        return { controlsDescendantBindings: true }
    }
};
于 2016-06-02T20:40:25.980 回答
2

我无法添加评论,所以我必须添加新答案。 非常好的答案user3297291,它真的帮助了我很多。但是,在现实世界中会出现一些问题,例如:

  1. 深度导航:pages, subpages, subsubpages 等这些 options.id是不够的,我们需要完整的路径 page/subpage/subsubpage(没有#!/

  2. 在页面之间共享一些URL参数时,只要参数发生变化,每个页面都会触发一次导航,最后一个会显示。我在某些页面(productID 等)之间共享相同的 ID 参数。

对于问题 1,计算完整路径:

...var options = valueAccessor();
var parent = pager.getParentPage(bindingContext);
var fullroute = parent.getFullRoute()();
if(fullroute.length == 0) 
    var path = fullroute.join('/')+options.id;
else 
    var path = fullroute.join('/')+'/'+options.id;

result对象变成:

var result = {
    path: path, 
    params: {}
};

对于问题 2,我们必须告诉他们,只有当活动页面是他们自己的时,他们才能触发导航。

在 subscribe 方法中,我们计算 activePage 路径并比较它们:

pathObj.subscribe(function(newValue) {
    //calculate activePath
    var activeParent = pager.activePage$().parentPage.getFullRoute()();
    var activeId = pager.activePage$().getCurrentId();
    if(activeParent.length == 0)
        var activePath = activeParent .join('/')+activeId;
    else 
        var activePath = activeParent .join('/')+'/'+activeId;

    if( path == activePath ){
        location.hash = pager.page.path(newValue);
    }
})

另外,在循环抛出参数时要小心,如果您提供了默认值,它可以是数组或对象:

<div data-bind="page: {id: 'search', params: ['product','price']}" class="well">

需求:

options.params.forEach(function(param) {...

VS

<div data-bind="page: {id: 'search', params: {'product': null,'price': 0}}" class="well">

需要类似的东西:

Object.keys(options.params).forEach(function(key,index) { 
    result.params[key] = viewModel[key](); 
}

再次感谢 user3297291 的回答,它确实有所作为。

于 2017-02-16T11:14:54.650 回答