PagerJS如何使用URL params完成“双向绑定”?

时间:2014-12-03 03:36:04

标签: knockout.js pagerjs

PagerJS可以获取URL参数并将它们绑定到模型。例如,在这个来自PagerJS网站(see link)的示例中,当您单击该链接时,它将导航到#/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参数与observable保持同步?

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

2 个答案:

答案 0 :(得分:5)

免责声明:我对pager.js一无所知,但我希望我的一般淘汰赛经验仍然有用。

查看示例,page绑定似乎使用url中的初始值创建了observable。我的第一直觉是扩展此绑定,并确保订阅每个值都会更新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后,页面绑定扩展了viewmodel,将params数组中定义的observable添加到viewModel对象。这意味着我们可以订阅这些可观察量的变化。

下一个挑战是计算正确的哈希值。我查看了page-href绑定如何计算其href属性。事实证明它在具有pager.page.path()path属性的对象上使用params。 E.g:

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

我找不到&#34;清洁&#34;通过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
  3. 中更新的所有可观察对象的订阅
  4. 值更改时自动更新哈希;使用rateLimit扩展名来防止更新过载
  5. 使用位置哈希做一些东西有点难以在小提琴中显示,所以我记录了我的概念证明的gif:

    live updating the hash using a custom pagerjs binding

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

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

答案 1 :(得分:2)

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

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

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

  3. 对于问题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);
        }
    })
    

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

    <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的回答,它确实有所不同。