如何更改 ng-template 的 ViewContainerRef
How can I change the ViewContainerRef of a ng-template
这让我抓狂:
- 确保检查 stackblitz 中的控制台
这是最能描述我的问题的STACKBLITZ。
简而言之。我希望 CLOSED 扩展面板 NOT 内的 "Stuff" 由 ChangeDetection 检查(从 ChangeDetection 中排除)。
cd-check-comp: Projected from Parent to Child
是 STAMPED OUT 来自 Parent
-View,所以只要检查 parent
就会检查它。这是预期的行为,但 不需要 。
问题:
如何将投影的 ng-template
(在此示例中为 cdkPortal/TemplatePortal)放入与 cd-check-comp: Im in Childs View
相同的 ViewContainer
?
如何更改/切换 ng-template
的 ViewContainerRef
。我想 "perform/code" 里面的开关 Child
-Component.
Stackblitz 保存:
@Component({
selector: "parent",
template: `
<button (click)="tick()">Trigger app.tick()</button>
<mat-expansion-panel #ep1>
<mat-expansion-panel-header>
<mat-panel-title>
Stuff inside should only be checked if open
</mat-panel-title>
</mat-expansion-panel-header>
<child [disableCD]="!ep1.expanded">
<ng-template cdkPortal>
<cd-check-comp name='Projected from Parent to Child'></cd-check-comp>
</ng-template>
</child>
</mat-expansion-panel>
<cd-check-comp name='Im in parents View.'></cd-check-comp>
<p> Main Goal: <b>cd-check-comp: Projected from Parent to Child</b> should not be "checked" when the Panel is closed for the first time.</p>
`,
})
export class Parent {
tick() { setTimeout(() => {}); }
}
@Component({
selector: 'child',
template: `
<ng-template [cdkPortalOutlet]="_portal"></ng-template>
<cd-check-comp name="Im in Childs View"></cd-check-comp>
`,
})
export class Child implements OnInit {
@ContentChild(CdkPortal, {static: true}) _lazyPortal: CdkPortal;
@Input() disableCD: boolean;
_opened: BehaviorSubject<boolean>;
_portal: TemplatePortal;
constructor(
private _changeDetectorRef: ChangeDetectorRef, private _vcr: ViewContainerRef
) {
}
ngOnInit() {
this._opened = new BehaviorSubject(this.disableCD);
}
ngDoCheck() {
console.log('Child checked')
}
ngOnChanges(sc: SimpleChanges) {
// return;
this.disableCD ? this._changeDetectorRef.detach() : this._changeDetectorRef.reattach();
if (this._opened) { this._opened.next(this.disableCD); }
}
ngAfterContentInit() {
if (this._lazyPortal) {
this._opened.pipe(
startWith(null!),
filter(() => this._opened.value && !this._portal),
take(1)
).subscribe(() => {
this._portal = this._lazyPortal;
});
}
}
}
@Component({
selector: "cd-check-comp",
template: "<p>cd-check-comp: <b>{{name ? name : instanceCounter}}</b></p>",
styles: [':host { display: block; border: 1px dashed black}']
})
export class CdCheckComp implements DoCheck {
static counter = 0;
@Input() name: string;
instanceCounter: number;
constructor(private _vcr: ViewContainerRef) {
this.instanceCounter = ++CdCheckComp.counter;
}
ngDoCheck() {
console.log("checked:" + (this.name ? this.name : this.instanceCounter));
}
}
我认为你试图分离错误 View
。
在 ViewEngine 中,如果模板在一个视图中声明但插入到另一个视图中,则更改检测也会 运行 在检查其声明点时。
我会分离嵌入的插入视图。为此,您可以通过 portal
.
从要插入视图的位置获取 ViewContainerRef
child.html
<ng-template #portalContainer [cdkPortalOutlet]="_portal"></ng-template>
^^^^^^^^^^^^^^^^
add this
child.ts
export class Child implements OnInit {
@ViewChild('portalContainer', { read: ViewContainerRef, static: true })
portalContainer: ViewContainerRef;
ngOnChanges(sc: SimpleChanges) {
if (this.portalContainer.length) {
const view = this.portalContainer.get(0)!;
this.disableCD ? view.detach() : view.reattach();
}
...
}
这让我抓狂:
- 确保检查 stackblitz 中的控制台
这是最能描述我的问题的STACKBLITZ。
简而言之。我希望 CLOSED 扩展面板 NOT 内的 "Stuff" 由 ChangeDetection 检查(从 ChangeDetection 中排除)。
cd-check-comp: Projected from Parent to Child
是 STAMPED OUT 来自 Parent
-View,所以只要检查 parent
就会检查它。这是预期的行为,但 不需要 。
问题:
如何将投影的 ng-template
(在此示例中为 cdkPortal/TemplatePortal)放入与 cd-check-comp: Im in Childs View
相同的 ViewContainer
?
如何更改/切换 ng-template
的 ViewContainerRef
。我想 "perform/code" 里面的开关 Child
-Component.
Stackblitz 保存:
@Component({
selector: "parent",
template: `
<button (click)="tick()">Trigger app.tick()</button>
<mat-expansion-panel #ep1>
<mat-expansion-panel-header>
<mat-panel-title>
Stuff inside should only be checked if open
</mat-panel-title>
</mat-expansion-panel-header>
<child [disableCD]="!ep1.expanded">
<ng-template cdkPortal>
<cd-check-comp name='Projected from Parent to Child'></cd-check-comp>
</ng-template>
</child>
</mat-expansion-panel>
<cd-check-comp name='Im in parents View.'></cd-check-comp>
<p> Main Goal: <b>cd-check-comp: Projected from Parent to Child</b> should not be "checked" when the Panel is closed for the first time.</p>
`,
})
export class Parent {
tick() { setTimeout(() => {}); }
}
@Component({
selector: 'child',
template: `
<ng-template [cdkPortalOutlet]="_portal"></ng-template>
<cd-check-comp name="Im in Childs View"></cd-check-comp>
`,
})
export class Child implements OnInit {
@ContentChild(CdkPortal, {static: true}) _lazyPortal: CdkPortal;
@Input() disableCD: boolean;
_opened: BehaviorSubject<boolean>;
_portal: TemplatePortal;
constructor(
private _changeDetectorRef: ChangeDetectorRef, private _vcr: ViewContainerRef
) {
}
ngOnInit() {
this._opened = new BehaviorSubject(this.disableCD);
}
ngDoCheck() {
console.log('Child checked')
}
ngOnChanges(sc: SimpleChanges) {
// return;
this.disableCD ? this._changeDetectorRef.detach() : this._changeDetectorRef.reattach();
if (this._opened) { this._opened.next(this.disableCD); }
}
ngAfterContentInit() {
if (this._lazyPortal) {
this._opened.pipe(
startWith(null!),
filter(() => this._opened.value && !this._portal),
take(1)
).subscribe(() => {
this._portal = this._lazyPortal;
});
}
}
}
@Component({
selector: "cd-check-comp",
template: "<p>cd-check-comp: <b>{{name ? name : instanceCounter}}</b></p>",
styles: [':host { display: block; border: 1px dashed black}']
})
export class CdCheckComp implements DoCheck {
static counter = 0;
@Input() name: string;
instanceCounter: number;
constructor(private _vcr: ViewContainerRef) {
this.instanceCounter = ++CdCheckComp.counter;
}
ngDoCheck() {
console.log("checked:" + (this.name ? this.name : this.instanceCounter));
}
}
我认为你试图分离错误 View
。
在 ViewEngine 中,如果模板在一个视图中声明但插入到另一个视图中,则更改检测也会 运行 在检查其声明点时。
我会分离嵌入的插入视图。为此,您可以通过 portal
.
ViewContainerRef
child.html
<ng-template #portalContainer [cdkPortalOutlet]="_portal"></ng-template>
^^^^^^^^^^^^^^^^
add this
child.ts
export class Child implements OnInit {
@ViewChild('portalContainer', { read: ViewContainerRef, static: true })
portalContainer: ViewContainerRef;
ngOnChanges(sc: SimpleChanges) {
if (this.portalContainer.length) {
const view = this.portalContainer.get(0)!;
this.disableCD ? view.detach() : view.reattach();
}
...
}