结构指令的上下文未更新
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
我创建了一个带上下文的结构指令。
@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