Open tiberiuzuld opened 5 years ago
Hello, thanks for interesting suggestion!
Did you tried using Lazy Modules with Ivy Renderer API before? I think it can be more declaratively instead of using old View Engine.
Do you want to discuss that feature and implement that via PR?
Hello, Tried using the my implementation of Lazy Modules and they work with Ivy. Didn't try using the Ivy Renderer API, didn't had the time to look into it. We can discuss what ideas you have for improvement.
I'll close that issue because with Angular Ivy we can load components lazily. You can find out an example in readme or check out that tweet.
Thanks!
Hello @thekiba , With the release of Ivy I think that in the ngxd needs some changes regarding ngModuleFactory.
// https://github.com/IndigoSoft/ngxd/blob/master/projects/core/src/lib/directive/component.outlet.ts
// from
@Input() ngxComponentOutletNgModuleFactory: NgModuleFactory<any> | null;
// to
@Input() ngxComponentOutletNgModule: Type<any> | null;
...
// https://angular.io/api/core/createNgModuleRef
private createNgModuleRef() {
if (this.ngxComponentOutletNgModule) {
this._ngModuleRef = createNgModuleRef(this.ngxComponentOutletNgModule, this.injector);
}
}
So my code from original post will be a bit simpler:
(routeConfigs[0].loadChildren() as any).then(module => {
this.component = module.entry;
this.module = module;
});
I also tested your suggestion with component | async
and to have it load the component directly, but I have an issue if I have some services provided on the module and the entire module is defined as a lazy load route, in this case I will get an a NullInjectorError: No provider for ...
error. Yeah probably I should move them from module to providedin root
.
Interestingly in angular they still use compiler and NgModuleFactory, even tho they are deprecated. https://github.com/angular/angular/blob/master/packages/router/src/router_config_loader.ts#L71
If you think this changes would be useful I can work on a PR to make the changes.
Hello, Updated code for lazy load of standalone components in Angular v14:
// define your route on the root routes
// this is needed for angular to build your component in the final package, they detect dynamic imports only defined in routes.
const appRoutes: Routes = [
...
{
path: '**',
component: NotFoundComponent,
// redirectTo: 'login'
}, // lazy standalone components routes below, so user never reaches them
{
path: LazyComponents.MY_LAZY_COMPONENT,
loadComponent: () => import('./lazyComponent/lazy.component').then(c=> c.LazyComponent)
}
];
// enum to make things easy
export enum LazyComponents {
MY_LAZY_COMPONENT = 'my-random-path'
}
// app-lazy-load component to fetch the component
import {CommonModule} from '@angular/common';
import {Component, Input, OnInit, Type} from '@angular/core';
import {Router} from '@angular/router';
import {NgxdModule} from '@ngxd/core';
import {from, Observable, of} from 'rxjs';
import {LazyComponents} from '....';
const wrapInObservable = <T>(value: T | Observable<T> | Promise<T>): Observable<T> => {
if (value instanceof Observable) {
return value;
}
if (value instanceof Promise) {
return from(value);
}
return of(value);
};
@Component({
selector: 'app-lazy-load',
templateUrl: './lazy-load.component.html',
standalone: true,
imports: [CommonModule, NgxdModule]
})
export class LazyLoadComponent implements OnInit {
@Input() component: LazyComponents;
@Input() context: object;
component: Type<unknown>;
constructor(private router: Router) {}
ngOnInit() {
const routeConfig = this.router.config.find(config => config.path === this.module);
if (routeConfig && routeConfig.loadComponent && routeConfig.loadComponent instanceof Function) {
wrapInObservable(routeConfig.loadComponent()).subscribe(component => (this.component = component));
} else {
console.error('Lazy component not found.');
}
}
}
<ng-container *ngIf="component">
<ng-container *ngxComponentOutlet="component; context: context;"> </ng-container>
</ng-container>
// app component in which to load my lazy standalone component
import {Component} from '@angular/core';
import {LazyComponents} from '....';
@Component({
selector: 'app-component',
template: './app-component.component.html',
})
export class AppComponent {
LazyComponents = LazyComponents;
}
<app-lazy-load [context]="mycontext" [component]="LazyComponents.MY_LAZY_COMPONENT"></app-lazy-load>
Hey, we can now lazy load standalone components with ngxd. If the modules are converted to standalone components, there's no need for this workaround.
Hello, I managed to implement lazy load a feature module dynamically and load the the component in the DOM using this library for dynamic rendering of the component. Works in both JIT and AOT runtime. I think this feature will be great addition to the list of features this library supports. Steps:
@NgModule({ declarations: [LazyFeatureComponent], exports: [LazyFeatureComponent], entryComponents: [LazyFeatureComponent], imports: [ CommonModule] }) export class LazyModule { static entry = LazyFeatureComponent; // This is needed to determine the component we want to render }
That is all the setup needed to make a feature module lazy load at runtime when needed depending on data you have. The loading of the module is the same thing as what angular does on lazy routes here but the code is private and not accessible from Router. If the angular team makes the code public API in the future we can reuse they're
loadModuleFactory
method.Let me know if you have any further questions or need any help integrating this part in the library.
Thanks