2017-04-26 188 views
0

是否存在与Promise一起使用的良好绑定实现?我所尝试的一切似乎最终都会给我一个关于无法将绑定多次应用于同一元素(我了解,并试图找到解决方法)的错误。我发现this,但它是相当古老的,似乎只能用于绑定,不会控制后代绑定出于同样的原因。敲除承诺绑定

我也尝试编写我自己的实现,试图删除孩子,并且只有在承诺解决后重新绑定/绑定它们,但得到了相同的结果。

作为替代方案,我可能会创建一个异步计算的observable来绑定,但随后键入(我使用TypeScript)变得有点模糊,因为我会返回一个承诺,但是从可观察的将是别的东西。我可以将它输入为(有效)“Promise | T”,但这可能会令人困惑,因为它实际上只会是“T”。

编辑:这是我说的打字问题。请记住,我正在使用一种方法来隐藏getters/setter后面的observables,以便我的属性看起来像普通的javascript属性。我的想法是使用一个装饰来八九不离十把它返回一个承诺成返回值

export class Foo { 
    @promise get bar(): int { 
    return new Promise<int>((resolve, reject) => { 
     setTimeout(() => { resolve(1) }, 100); 
    }); 
    } 
} 

这样做看起来没事,但打字稿会抱怨返回类型的吸气剂吸气。我可以将它投射到任何地方,但这是不对的。或者,我可以将getter返回值转换为Promise | T,但是这样会误导实际的回报类型,因为它始终是T.

在绑定的方式中,我宁愿不重新绑定所有内容,但似乎在例如“if”绑定这实际上是我正在尝试使用的),但并没有真正的解决方法。

编辑2:以防万一,这是我目前的“承诺”装订处理器的化身:

import * as ko from "knockout"; 

ko.bindingHandlers["promise"] = { 
    init(element: HTMLElement, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
    let first = true; 

    function apply(bindingName: string, val: any) { 
     ko.applyBindingsToNode(element, { [bindingName]: val }, bindingContext); 
    } 
    ko.computed(() => { 
     let bindings = ko.unwrap(valueAccessor()); 

     if(bindings) { 
     ko.tasks.schedule(() => { 
      for(let bindingName in bindings) { 
      let promise = bindings[bindingName] as Promise<any>; 

      if(promise && promise.then) { 
       promise.then(val => { 
       apply(bindingName, val); 
       first = false; 
       }); 
      } else { 
       apply(bindingName, bindings[bindingName]); 
       first = false; 
      } 
      } 
     }); 
     } 

    }, null, { disposeWhenNodeIsRemoved: element })(); 

    return { 
     controlsDescendantBindings: false 
    }; 
    } 
} 


ko.virtualElements.allowedBindings["promise"] = true; 

我已经试过“controlsDescendantBindings”设置为false,这适用于一些事情,但似乎引起当承诺绑定嵌套时,会造成一些破坏。

编辑3:至于我想要做的,这是沿着这条线(注:使用Bootstrap)。

<ul class="nav navbar-nav"> 
    <!-- ko promise: { if: canAccessFooBar } --> 
    <li class="dropdown"> 
    <a class="dropdown-toggle" data-toggle="dropdown">Foobar</a> 
    <ul class="dropdown-menu> 
     <!-- ko promise: { if: canAccessFoo } --> 
     <li> 
     <a href="/foo">Foo</a> 
     </li> 
     <!-- /ko --> 
     <!-- ko promise: { if: canAccessBar } --> 
     <li> 
     <a href="/bar">Bar</a> 
     </li> 
     <!-- /ko --> 
    </ul> 
    </li> 
    <!-- /ko --> 
</ul> 

凡canAccessFoo,canAccessBar和canAccessFooBar是解析为布尔值的承诺。

+0

为什么你不止一次应用绑定?只要您事先创建它们并且不要用新实例替换它们,就可以异步更新现有的observables。 –

+0

在事实之后更新观察值是我列出的一个选项,但是这引起了打字问题,可能会导致后面的人混淆。 – Ixonal

+0

你想完成什么? –

回答

0

我想我找到了解决方案。关键是“ko.applyBindingsToNode”的返回值,它是具有单个属性“shouldBindDescendants”的对象。 applyBindingsToNode调用的返回值在typescript声明中被列为“any”,所以我不知道里面有什么,直到我好奇并记录下来。无论如何,这里是绑定处理程序的当前化身(至少就我写这个而言)起作用。

import * as ko from "knockout"; 

ko.bindingHandlers["promise"] = { 
    init(element: HTMLElement, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
    let bindings = ko.unwrap(valueAccessor()); 

    function apply(bindingName: string, val: any) { 
     let result = ko.applyBindingsToNode(element, { [bindingName]: val }, bindingContext); 
     if(result.shouldBindDescendants) ko.applyBindingsToDescendants(bindingContext, element); 
    } 

    if(bindings) { 
     for(let bindingName in bindings) { 
     let promise = bindings[bindingName] as Promise<any>; 

     if(promise && promise.then) { 
      promise.then(val => { 
      apply(bindingName, val); 
      }); 
     } else { 
      apply(bindingName, bindings[bindingName]); 
     } 
     } 
    } 

    return { 
     controlsDescendantBindings: true 
    } 
    } 
} 


ko.virtualElements.allowedBindings["promise"] = true;