【问题标题】:Knockout Promise Binding淘汰承诺绑定
【发布时间】:2017-09-24 10:55:42
【问题描述】:

是否存在与 Promises 一起使用的良好绑定实现?我尝试的一切似乎最终都会给我一个错误,即无法将绑定多次应用于同一个元素(我理解,并试图找到解决方法)。我找到了this,但它相当陈旧,而且似乎只适用于不控制后代绑定的绑定。

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

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

编辑:这是我正在谈论的打字问题。请记住,我正在使用一种方法将 observables 隐藏在 getter/setter 后面,以便我的属性看起来像常规的 javascript 属性。我的想法是使用装饰器将返回承诺的 getter 转换为返回值的 getter

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

这样做看起来没问题,只是 TypeScript 会抱怨返回类型。我可以将它投射到任何地方,但这就是不对的。或者,我可以将 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,这适用于某些情况,但在嵌套 promise 绑定时似乎会造成一些破坏。

编辑 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 是解析为布尔值的 Promise。

【问题讨论】:

  • 为什么要多次应用绑定?您可以异步更新现有的 observables,只要您预先创建它们并且不要用新实例替换它们。
  • 事后更新 observables 是我列出的一个选项,但这带来了打字问题,可能会给后来进来的人造成混淆。
  • 你想要完成什么?
  • 使用 observable 来保存你的视图模型是可以的。查看stackoverflow.com/a/27453338/1287183stackoverflow.com/a/12316626/1287183
  • 我不太了解您提到的打字问题。为什么它必须返回一个 promise 而不是简单地更新强类型的 observables?你能发布一些示例代码吗?

标签: typescript knockout.js data-binding promise


【解决方案1】:

我想我找到了解决办法。关键是“ko.applyBindingsToNode”的返回值,它是一个具有单个属性“shouldBindDescendants”的对象。 applyBindingsToNode 调用的返回值在打字稿声明中列为“任何”,所以在我好奇并记录下来之前,我不知道其中有什么。无论如何,这里是绑定处理程序的当前化身(至少就我写这篇文章而言)。

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;

【讨论】:

    猜你喜欢
    • 2013-02-04
    • 2013-10-31
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多