Lazy loaded component does not update NGXS state

Ive started playing around with the new lazy loaded components in angular 9. I've got a stateful component using NGXS whose state is lazy loaded in a module near the component but once the component renders, the store is not updated with the new state.

Feature module

@NgModule({
  imports: [
    NgxsModule.forFeature([PurchaseState]),
    SharedModule, 
    FormsModule, 
    GridModule,
    PurchasesApiModule,
    SidePanelCoreModule
  ],
  declarations: [PurchaseDetailsPageComponent, PurchaseDetailsFormComponent, PurchaseDetailsAllotmentListComponent],
})
export class PurchaseDetailsModule {
  constructor() {
    console.log('PURCHASE CONSTRUCTED')
  }
}

Dynamic Component Loader

  async getComponent(key: string) {
    const lazyComp = await import('@pe/purchase-details').then(c => c.PurchaseDetailsPageComponent);
    const factory = this.componentFactoryResolver.resolveComponentFactory(lazyComp);
    this._component = this.content.createComponent(factory, null, this.injector);
  }

Things like the GridModule (which is only declared here) works alright, but im guessing the providers part of the module loading isnt being run?

Has anyone come across something like this?

I've tried including a module declaration for the state inside the component .ts file described in this article but no dice :(

1 answer

  • answered 2020-02-13 02:49 Evan Wallace

    So i was able to cobble together a solution after looking at how the angular router lazy loads modules.

    I manage a cache of configs in an app level service and dynamically load things using this guy.

    Happy to hear better solutions :)

    @Component({
      selector: 'app-lazy',
      templateUrl: './app-lazy.component.html',
      styleUrls: ['./app-lazy.component.scss'],
    })
    export class StatefulLazyComponent implements OnInit, OnDestroy {
    
      @ViewChild('content', { read: ViewContainerRef, static: true }) content: ViewContainerRef;
      _lazyLoadSub: Subscription;
      constructor(
        private componentFactoryResolver: ComponentFactoryResolver,
        private injector: Injector,
        private compiler: Compiler,
      ) {}
    
      ngOnInit() {
        const config  = {
          component: import('@pe/purchase-details').then(c => c.PurchaseDetailsPageComponent),
          module: import('@pe/purchase-details').then(m => m.PurchaseDetailsModule)
        };
    
        this._lazyLoadSub = combineLatest([
          this.getComponentObs(config.component),
          this.getModuleFactory(config.module)
          ]).subscribe(data => {
          const [compFactory, moduleFactory] = data;
          const module = moduleFactory.create(this.injector);
          const component = this.content.createComponent(compFactory, null, this.injector);
          // Module and component are loaded can now do stuff with them
        });
      }
    
      ngOnDestroy() {
        if(this._lazyLoadSub) {
          this._lazyLoadSub.unsubscribe();
        }
      }
    
      getComponentObs(promise: Promise<Type<any>>): Observable<ComponentFactory<any>> {
        return from(promise).pipe(mergeMap(comp => of(this.componentFactoryResolver.resolveComponentFactory<any>(comp))));
      }
    
      getModuleFactory(promise: Promise<Type<any>>): Observable<NgModuleFactory<any>> {
          return from(promise).pipe(mergeMap(t => from(this.compiler.compileModuleAsync(t))));
      }
    }