How can I change the ViewContainerRef of a ng-template

廉价感情. 提交于 2020-03-06 09:43:32

问题


This is driving me nuts:

  • make sure you check the console in the stackblitz

Here is a STACKBLITZ that describes my problem best.

In a nutshell. I want "Stuff" inside of a CLOSED Expansion-Panel NOT to be checked by ChangeDetection (exlude from ChangeDetection).

cd-check-comp: Projected from Parent to Child is STAMPED OUT from Parent-View so it is checked when ever parent is checked. That is the expected behaviour, but NOT DESIRED.

Question:

How can I put the projected ng-template (in this example cdkPortal/TemplatePortal) into the same ViewContainer as cd-check-comp: Im in Childs View?

How can I change/ switch ViewContainerRef of an ng-template. I would like to "perform/code" the switch inside Child-Component.


Stackblitz Save:

@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));
  }
}

回答1:


I think you're trying to detach the wrong View.

In ViewEngine if a template is declared in one view but inserted into a different view, change detection would also run when its declaration point was checked.

I would detach embedded inserted view. To do so you can get hold of ViewContainerRef from the place where you're inserting view through portal.

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();
   }
   ...
 }

Forked Stackblitz



来源:https://stackoverflow.com/questions/60117068/how-can-i-change-the-viewcontainerref-of-a-ng-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!