Ir al contenido

Adapter de Angular

El adapter de Angular soporta Angular 14+ en tres formas: apps con NgModule, componentes standalone y Angular Elements. Solo una de ellas funciona dentro del Shadow DOM, que es el modo de aislamiento por defecto de Wu, así que empieza por registerStandalone().

Ventana de terminal
npm install wu-framework @angular/core @angular/platform-browser zone.js
src/main.ts
import 'zone.js';
import '@angular/compiler'; // modo JIT — no hace falta el plugin AOT
import { createApplication } from '@angular/platform-browser';
import { createComponent, provideZoneChangeDetection } from '@angular/core';
import { wuAngular } from 'wu-framework/adapters/angular';
import { AppComponent } from './app/app.component';
wuAngular.registerStandalone('dashboard', AppComponent, {
createApplication, // pásale las APIs de Angular — ver abajo
createComponent,
provideZoneChangeDetection,
});

registerStandalone(appName, RootComponent, options)

Sección titulada «registerStandalone(appName, RootComponent, options)»

El punto de entrada recomendado. Usa createApplication + createComponent con un hostElement explícito, lo que funciona en todas partes, incluido el Shadow DOM.

Opción Tipo Por defecto Notas
appConfig ApplicationConfig {} Configuración estándar de aplicación Angular; sus providers se fusionan.
createApplication function null De @angular/platform-browser. Muy recomendable.
createComponent function null De @angular/core.
provideZoneChangeDetection function null De @angular/core. Cuando está presente, el adapter añade provideZoneChangeDetection({ eventCoalescing: true }) a los providers.
standalone boolean true Plan B standalone.
standaloneContainer string '#root' Selector usado en modo standalone.
onMount / onUnmount function null Callbacks de ciclo de vida.
wuAngular.registerStandalone('dashboard', AppComponent, {
createApplication,
createComponent,
provideZoneChangeDetection,
appConfig: {
providers: [provideHttpClient(), provideRouter(routes)],
},
});

La vía NgModule. Arranca con platformBrowserDynamic().bootstrapModule().

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { wuAngular } from 'wu-framework/adapters/angular';
import { AppModule } from './app/app.module';
wuAngular.register('legacy-admin', AppModule, {
platformFactory: platformBrowserDynamic,
rootSelector: 'app-root',
providers: [],
});
Opción Tipo Por defecto Notas
platformFactory function null platformBrowserDynamic. Pásalo para evitar problemas de resolución del bundler.
providers Array [] Providers extra de arranque, entregados a platformFactory(providers).
rootSelector string 'app-root' Etiqueta creada dentro del contenedor para alojar la app. También recibe data-wu-angular-root="<appName>".
standalone boolean true Plan B standalone.
standaloneContainer string '#root' Selector usado en modo standalone.
onMount / onUnmount function null Reciben (container, moduleRef).

registerElement(appName, Component, options)

Sección titulada «registerElement(appName, Component, options)»

Envuelve un componente standalone como custom element mediante @angular/elements (que debe estar instalado) y luego deja que Wu coloque la etiqueta.

wuAngular.registerElement('mfe-angular', AppComponent, {
elementTag: 'mfe-angular-content',
});
Opción Tipo Por defecto
elementTag string `${appName}-element`
appConfig ApplicationConfig {}
standalone boolean true
standaloneContainer string '#root'
onMount / onUnmount function null

El elemento se define una sola vez (primero se comprueba customElements.get) y se reutiliza entre montajes.

await wu.update('dashboard', { theme: 'dark' }); // no hace nada

Ninguno de los tres puntos de entrada de Angular anuncia update ni hydrate. Para meter datos en una micro-app Angular en ejecución, usa el store o el bus de eventos, que es justo para lo que sirve createWuService().

Una factoría simple (no un @Injectable de Angular) que agrupa el bus de eventos y el store con seguimiento de suscripciones:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { createWuService } from 'wu-framework/adapters/angular';
@Component({ selector: 'app-root', standalone: true, template: '' })
export class AppComponent implements OnInit, OnDestroy {
private wu = createWuService();
orders: Order[] = [];
theme = 'light';
ngOnInit() {
this.wu.on('order:new', (e) => this.orders.push(e.data));
this.wu.onStateChange('theme.mode', (e) => (this.theme = e.value));
const user = this.wu.getState('user');
}
save(name: string) {
this.wu.setState('store.name', name);
this.wu.emit('settings:saved', { name });
}
ngOnDestroy() {
this.wu.destroy(); // elimina cada listener de on() / onStateChange()
}
}

Devuelve { emit, on, once, off, getState, setState, onStateChange, destroy, wu }.

Los componentes de Angular no se pueden crear de forma genérica en tiempo de ejecución, así que el adapter devuelve un objeto de configuración que ensamblas en un componente real en tu propio código:

import { Component, Input, Output, EventEmitter, ElementRef, ViewChild } from '@angular/core';
import { createWuSlotComponent } from 'wu-framework/adapters/angular';
const config = createWuSlotComponent();
@Component({
selector: 'wu-slot',
template: config.template,
styles: config.styles,
})
export class WuSlotComponent {
@Input() name!: string;
@Input() url!: string;
@Input() fallbackText?: string;
@Output() load = new EventEmitter();
// La salida se llama 'error' para el padre, pero la propiedad DEBE llamarse
// errorEvent: `error` guarda el mensaje que renderiza el template, así que no
// puede ser también el EventEmitter.
@Output('error') errorEvent = new EventEmitter();
error: string | null = null;
loading = true;
@ViewChild('container') container!: ElementRef;
// Implementa los métodos de ciclo de vida de config.methods
}

config provee { selector, template, styles, methods }. El template ya maneja los estados de carga y error y estampa data-wu-app / data-wu-url.

getWuSlotModuleConfig() devuelve el cableado del NgModule para shells que quieran declarar y exportar WuSlotComponent:

// { imports: ['CommonModule'], declarations: ['WuSlotComponent'], exports: ['WuSlotComponent'] }
import { createWuAIService } from 'wu-framework/adapters/angular';
@Component({ /* … */ })
export class AssistantComponent {
private ai = createWuAIService();
async ask(text: string) {
await this.ai.send(text);
}
}

Misma forma que los helpers de IA sin streaming de los otros adapters: envuelve wu.ai.send() con contabilidad local de mensajes. Consulta Visión general de IA.

vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: { port: 5008, cors: true },
esbuild: { target: 'es2022' },
});
// tsconfig.json — decoradores para esbuild
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false
}
}

Importa @angular/compiler en main.ts para ejecutar en modo JIT. cors: true es obligatorio para que el shell pueda pedir wu.json y el bundle de entrada desde otro origen.

import {
wuAngular, // export por defecto
register,
registerStandalone,
registerElement,
createWuService,
createWuSlotComponent,
getWuSlotModuleConfig,
createWuAIService,
getWuInstance,
waitForWu,
} from 'wu-framework/adapters/angular';

Desde el barrel, estos pasan a ser registerAngular y registerAngularStandalone.