结构指令的上下文未更新

Structural directive's context not updated

我创建了一个带上下文的结构指令。

@Directive({selector: '[myDir]'})
export class MyDir implements OnInit {
  constructor(private templateRef: TemplateRef<any>,
              private viewContainerRef: ViewContainerRef,
  ) { }

  public context = {$implicit: false}

  ngOnInit() {
    this.viewContainerRef.createEmbeddedView(this.templateRef, this.context)
  }
}

这就是我的使用方式。

<pre *myDir="let context">Context: {{ context | json}}</pre>

模板已正确安装到视图中,Context: false 按预期出现。

但是,如果我尝试更改上下文,它不会反映在视图中。根据 ngTemplateOutlet spec from the Angular repo 判断,它看起来应该在上下文更改时更新。 是我的代码有误,还是我误解了规范?

我也尝试过注入 CR 并手动触发变更检测,但没有用。在我的真实情况下,应该在监听鼠标事件的可观察订阅上触发更改,但这里有一个简单间隔的演示。

Observable
  .interval(500)
  .map((_, i) => i % 2 == 0)
  .takeUntil(this.destroy$)
  .subscribe(context => {
    this.context = {$implicit: context}
    console.log('Context is now', this.context.$implicit)
  })

尽管 context$ 确实 发生了变化,并且 显示在控制台中,但视图并未更新。 Here's a demo on StackBlitz.


我在销毁时添加了取消订阅,否则闪电战会在几次重新加载后变得疯狂。还向 AppComponent 添加了一个测试可观察对象,它只是半秒间隔的简单计数器,与指令无关。这只是对应用程序正常运行的健全性检查,并且整个应用程序的 CD 周期通常 运行 正常。我没有在任何地方使用 OnPush。

Javascript 是非常有趣的语言。

让我们考虑以下代码:

let view: any = {};
function detectChanges() {
  console.log(view.context);
}

function createEmbeddedView(context) {
  view.context = context;
}

let context = { x: false };
createEmbeddedView(context);

detectChanges(); // prints { x: false }

当我们创建 context 并将其传递给 createEmbeddedView 函数时。

现在让我们改变context。我们将采用两种选择:

1) 改变自己

context = { x: true };

detectChanges(); // prints { x: false }

2) 更改内部结构

context.x = true;

detectChanges(); // prints { x: true} Hooray!!!

正如我们所见,我们的输出在第一种情况下没有改变,而在第二种情况下有效。那是因为在 javascript 中我们传递了对象 by sharing.

这是一种特定的按值传递。如果我们更改参数本身,它不会对函数内部提供的项目产生任何影响。但是如果我们改变一些属性那么它会改变项目。

因此,对于您的情况,您可以更改上下文,例如:

this.context.$implicit = context;

https://stackblitz.com/edit/so-context-question-a4n5yx?file=app%2Fapp.component.ts