Search code examples
angularondestroy

Angular2 detach view before onDestroy?


Is it somehow possible in Angular2 to get notified BEFORE the component is already destroyed!? i.e. When it is ABOUT TO be destroyed.

I have a container component which holds a ViewContainerRef on one of its children which will be used to dynamically load views. If the container component itself gets destroyed (in my case due to a *ngFor directive in the parent component), i want to detach the view which is currently loaded in the ViewContainerRef and attach it on another container again.

Thing is: In the ngOnDestroy() life cycle hook the ViewContainerRef is already cleared, so all views are destroyed, nothing to detach anymore.


Solution

  • My solution for now (not really a workaround) is to implement the ngOnChanges() of the parent component which uses the ngFor directive. If the array which is iterated by ngFor has changed and its length is shorter than before i simply detach the views of all containers (@ViewChildren containers: QueryList) and map them to the containers (Map). I then re-insert the mapped views again by iterating the new containers list and loading the ViewRefs from the map, in the containers.changed listener.

    @Component({
     selector: 'container-collection',
     directives: [Container],
     template: `<container *ngFor="let i of views"></container>`
    })
    export class ContainerCollection {
     public views = [];
    
     @ViewChildren(Container) private containers: QueryList<Container>;
    
     private viewMap = new Map<Container, ViewRef>();
    
     public ngOnChanges(changes: {[key: string]: SimpleChange]}): void {
      if(changes['views']) {
       //detach all views and remember which container had which view
       this.viewMap = new Map<Container, ViewRef>();
    
       this.containers.forEach(container => {
        var view = container.viewContainer.detach();
        if(view)
         this.viewMap.set(container, view);
       });
      }
     }
    
     public ngAfterViewInit(): void {
      //insert the views again (into the appropriate containers)
      this.containers.changes.subscribe( () => {
       this.containers.forEach(container => {
        var view = this.viewMap.get(container);
        if(view)
         container.viewContainer.insert(view);
       });
      });
     }
    }
    
    @Component({
     selector: 'container',
     template: `<div #viewContainer></div>`
    })
    export class Container {
     @ViewChild('viewContainer') public viewContainer;
    }
    

    The code is just a draft and may contain syntax errors. But the idea (which is working for me) should be clear.

    It would be great of the Angular2 team to add a LifeCycle hook which gets called BEFORE a component gets actually destroyed. (e.g. ngBeforeDestroy() ) or so.