Tengo un proyecto Angular utilizado por algunos clientes. Entonces, cada vez que construyo el proyecto, lo hago para un cliente, cambiando una propiedad en un archivo de configuración. Además, es posible que tenga una lógica ligeramente diferente en algunos componentes para diferentes clientes. Por ejemplo:
@Component({ selector: 'my-component', templateUrl: './component.template.html', styleUrl: './component.styles.scss' }) export class MyBaseComponent { getName() { return 'Base'; } /* Other logic here ... */ } @Component({ selector: 'my-component', templateUrl: './component.template.html', styleUrl: './component.styles.scss' }) export class MyClientComponent extends MyBaseComponent { getName() { return 'Client'; } } @Component({ selector: 'my-component', templateUrl: './component.template.html', styleUrl: './component.other-client.styles.scss' }) export class MyOtherClientComponent extends MyBaseComponent { getName() { return 'Other Client'; } }Ahora, lo que me gustaría es que esto sea totalmente transparente para otros módulos que importen estos componentes. Tenga en cuenta que los tres selectores tienen el mismo nombre. Traté de implementar el módulo de esta manera:
import { client } from '../environment'; import { MyBaseComponent, MyClientComponent, MyOtherClientComponent } from './components'; function chooseComponent() { switch(client) { case 'my-client': return MyClientComponent; case 'my-other-client': return MyOtherClientComponent; default: return MyBaseComponent; } } @NgModule({ declarations: [chooseComponent()], imports: [], providers: [], exports: [chooseComponent()], }) export class TestComponentModule {}Pero Angular no me deja hacer esto. ¿Saben cuál es la forma correcta de implementar esto? Claro, podría usar y si para cambiar la lógica. Pero eso es feo.
Además, si me gustaría cambiar una parte de la plantilla html, según el cliente. ¿Qué sería lo mejor para hacerlo? Imagínese, si el cliente es XI, muestre un botón, y si es YI, muestre una etiqueta. ¿Usar ngIf es la única forma?
¡Gracias!
Hay varias maneras de hacer esto. 1- podría crear un componente de contenedor y usar ngSwitch en la plantilla. En este caso, necesitaría usar un selector único para cada componente y usarlo en la plantilla del componente del contenedor: por ejemplo
Plantilla de contenedor
<ng-container [ngSwitch]="client"> <my-client-component *ngSwitchCase="'my-client'"></my-client-component> <my-other-client-component *ngSwitchCase="'my-other-client'"></my-other-client-component> </ng-container>2- Podría usar la representación dinámica y hacer la magia en el archivo mecanografiado de su componente de contenedor. Puede definir un método con el interruptor en la variable del cliente y dejar que angular represente el componente en la vista según el cliente. Para obtener más detalles sobre cómo renderizar un componente dinámico, consulte la documentación de renderización de componentes dinámicos angulares . En caso de que no desee cargar el otro componente del cliente en el navegador, puede colocar sus componentes en un módulo perezoso e importarlos con importación dinámica. Vea este buen artículo https://medium.com/@ckyidr9/lazy-load-feature-modules-without-routing-in-angular-9-ivy-220851cc7751
Si no me equivoco, puedes hacerlo de varias maneras:
// Have each component in 3 different modules. Have 3 route guards and use them to load the exact module. Something like this: import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate } from '@angular/router'; import { client } from '../environment'; @Injectable() export class MyClientGuard implements CanActivate { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): boolean { return client === 'my-client'; } } @Injectable() export class MyOtherClientGuard implements CanActivate { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): boolean { return client === 'my-other-client'; } } @Injectable() export class MyBaseComponentGuard implements CanActivate { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): boolean { return (client !== 'my-client' || client !== 'my-other-client'); } } const routes: Routes = [ { path: 'base-component', loadChildren: () => import('./base-component/base-component.module').then(m => m.BaseComponentModule), canActivate: [MyBaseComponentGuard] }, { path: 'other-client', loadChildren: () => import('./other-client/other-client.module').then(m => m.OtherClientModule), canActivate: [MyOtherClientGuard] }, { path: 'my-client', loadChildren: () => import('./my-client/my-client.module').then(m => m.MyClientModule), canActivate: [MyClientGuard] } ];La segunda manera de hacer esto es: Usando Resolvedores. Puede consultar esta documentación para obtener más detalles sobre los resolutores: https://angular.io/api/router/Resolve No lo he usado mucho, pero podría ayudarlo.
La tercera forma es tener todo en un solo módulo y un solo componente y basado en el valor de environment.client, usando ngIf / ngtemplate para mostrar las plantillas correctas en la página.
Una forma más es usar el mismo componente y definir algún @Input() clientValue: string como ese y, en función de eso, puede mostrar valores ocultos usando ng-container y ng-template.
No está directamente relacionado, pero podría echar un vistazo a esta aplicación que creé hace algún tiempo https://stackblitz.com/edit/angular-rxjs-in-memory-caching-dynamic?file=src%2Fapp%2Froot.component.ts
Esto simula un tablero con widgets donde los datos para el widget se recuperan del servidor, la API simula demoras aleatorias en cada llamada y almacena en caché las llamadas API en el navegador.
En términos de configuración, puede ver cómo puede definir qué widgets desea ver en la página, utiliza el cargador de componentes dinámicos para cargar los componentes. Esto fue construido en Angular 8, hay formas más elegantes de hacerlo con Ivy de Angular 9