自定义元素的绑定上下文 - 它到底是什么,如何访问父VM?

7

我在文档中没有找到答案,所以我在这里提问。自定义元素的bind方法传递的绑定上下文是什么?它是否等同于路由当前活动的视图模型?至少,这是我目前发现的。

为什么不是元素的父级(在DOM术语中)VM呢?

使用此代码:

@customElement("myelem")
@inlineView("<template><content></content></template>")
export class MyElem{
    bind(ctx){
      console.log(ctx);
    }
}

// welcome.html
<myelem>
    <h3>inside myelem</h3>
    <myelem>
      <h4>inside inside ... </h4>
    </myelem>
  </myelem>

在控制台中的输出只是当前视图模型(Welcome)打印了两遍。
我希望第一个(外部)myelemWelcome,但是第二个(内部)出现时应该是MyElem
请解释一下我错在哪里,以及如何使内部自定义元素能够知道它的实际上下文(即上面的外部元素),而不使用像创建“秘密”属性这样的丑陋技巧来共享上下文(实际传递给它们的上下文)。
1个回答

16

就数据绑定而言,这两个元素都绑定到相同的绑定上下文中。考虑以下示例:

<div foo.bind="bar">
  <div foo.bind="bar"></div>
</div>

你会期望两个 <div> 元素具有相同的绑定上下文吧?这两个元素的 foo 属性应该绑定到同一个模型的 bar 属性。在这种情况下也是如此:

<myelem foo.bind="bar">
  <myelem foo.bind="bar"></myelem>
</myelem>

两个 <myelem> 实例都绑定到相同的绑定上下文/模型。

如果我理解问题正确,您希望以优雅的方式将内部 MyElem 类实例引用外部 MyElem 类实例。幸运的是,您正在使用 Aurelia,因此有一种非常好的方法来完成这个操作... 使用 inject 装饰器声明它作为一个依赖项:

import {inject, Parent} from 'aurelia-dependency-injection';
import {customElement} from 'aurelia-framework';

@customElement("myelem")
@inject(Parent.of(MyElem))
export class MyElem {
  constructor(parent) {
    this.parent = parent;
  }
  ...
}

然而,有一个重要的注意事项...

Aurelia 依赖注入容器的默认行为是在容器中未找到所需的项目实例时创建该实例。这意味着 @inject(Parent.of(MyElem)) 并不完全符合我们的要求。在没有父级 MyElem 实例的情况下,容器将为我们创建一个实例,而不是返回 null。通常,我们会使用 @inject(Optional.of(MyElem)) 来告诉容器,只有当它存在于容器中时才给我们实例。我不知道如何结合 Parent.of 和 Optional.of。我将在 aurelia 依赖注入仓库中创建一个问题,以便我们可以添加此功能。

与此同时,我们可以轻松地创建自己的 Resolver,将 Parent.of 和 Optional.of 的行为相结合:

import {resolver} from 'aurelia-dependency-injection';

@resolver()
export class OptionalParent {
  constructor(key) {
    this.key = key;
  }

  get(container) {
    if (container.parent && container.parent.hasResolver(this.key, false)) {
      return container.parent.get(this.key)
    }
    return null;
  }

  static of(key) {
    return new OptionalParent(key);
  }
}

所以我们的MyElem类的新版本看起来像这样:

import {inject} from 'aurelia-dependency-injection';
import {customElement} from 'aurelia-framework';
import {OptionalParent} from './optional-parent';

@customElement("myelem")
@inject(OptionalParent.of(MyElem))
export class MyElem {
  constructor(parent) {
    this.parent = parent;
  }
  ...
}

这是一个可运行的例子。请检查控制台,查看显示结果的日志信息:

https://gist.run/?id=1a84e0a466fb928aa075


太好了,谢谢!我的第一反应是“我应该更仔细地阅读 DI 文档”,但现在我发现 Parent.of 根本没有记录 ;) - migajek
我自己也遇到了这个问题。稍后我会将我的用例添加到该问题中。 - user677526
这在最新的Aurelia Plunker(0.8.2)中不起作用,错误涉及OptionalParent并显示super()需要为null或函数,而不是undefined - niieani
3
我已更新代码以适应2015年11月Aurelia版本中的依赖注入库更改。 - Jeremy Danyow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接