2017-04-26 101 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;