angularzone.jsngzone

Generic component where projected content is instantiated in outer zone


I have a webcomponent that for perf reasons needs to run outside angular.

I came up with the basic idea :

@Component({
  selector: 'run-outer-zone',
  template: ''
}) 
class OuterZoneComponent {
   constructor(private vcr: ViewContainerRef, private ngZone: NgZone) {
      this.ngZone.runOutsideAngular(() => {
          const compRef = vcr.createComponent(MyComponentWhichNeedsToRunOutsideAngular);
          compRef.data = // set the inputs
      })
   }
}

It works fine but that's not very generic and it's cumbersome if have multiple web components to handle.

Is it possible to build a custom generic component where the projected content is run in the outer zone ?


Solution

  • I ended up writing my own directive to run specific components outside the NgZone.

    It's hugely inspired by how the NgIf works.

    @Directive({
      selector: '[runOutside]',
      standalone: true,
    })
    export class RunOutsideDirective<T> {
      private isOutside: boolean;
    
      constructor(
        private _viewContainer: ViewContainerRef,
        private ngZone: NgZone,
        private templateRef: TemplateRef<T>
      ) {}
    
      ngOnInit() {
        this.updateView(true);
      }
    
      @Input()
      set runOutside(doRunOutside) {
        this.updateView(doRunOutside);
      }
    
      private updateView(doRunOutside: boolean) {
        if (doRunOutside === this.isOutside) {
          return;
        }
    
        this._viewContainer.clear();
        if (doRunOutside) {
          this.ngZone.runOutsideAngular(() => {
            this._viewContainer.createEmbeddedView(this.templateRef);
          });
        } else {
          this._viewContainer.createEmbeddedView(this.templateRef);
        }
      }
    }
    

    usage :

    <my-cmp *runOutside></my-cmp>