Crear un plugin
Configura el teu Entorn de Desenvolupament
Abans de començar, necessites tenir instal·lat Node.js i npm (el gestor de paquets de Node). Pots descarregar-los des de la pàgina oficial de Node.js.
Nota: Aquesta guia està basada en Angular 18 i cobreix totes les funcionalitats disponibles en aquesta versió. Tot i així, per a versions posteriors d'Angular, la major part de la informació continguda hauria de ser aplicable, tenint en compte les possibles variacions introduïdes en versions més recents. Es recomana revisar les notes de versió oficials per assegurar la compatibilitat completa amb la versió utilitzada.
Instal·la Angular CLI
Angular CLI és una eina que t'ajuda a crear i gestionar projectes Angular. Obre el teu terminal o línia de comandes i instal·la-ho amb la següent comanda:
npm install -g @angular/cli@18
Crea un Nou Projecte
Una vegada instal·lat Angular CLI, pots crear un nou projecte amb aquesta comanda:
ng new nom-del-teu-projecte --routing=false --style=css --view-encapsulation=ShadowDom
Ens farà la següent pregunta:
? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? (y/N)
Li contestarem que no, i esperarem que s'instal·lin totes les dependències.
Executa la teva Aplicació
Per veure la teva aplicació en acció, navega al directori del projecte i executa:
cd nom-del-teu-projecte
ng serve
Això iniciarà un servidor local. Obre el teu navegador i dirigeix-te a http://localhost:4200
per veure la teva nova aplicació Angular.
Afegir dependència al Shell
Per a afegir la dependència "@uxland/primary-shell" al teu projecte, executa la següent comanda a l'arrel del teu projecte:
npm install @uxland/primary-shell
Inicialitzar el Shell
En inicialitzar el projecte, el fitxer main.ts
el tenim d'aquesta manera:
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
Per a la inicialització del shell, substituirem el fitxer main.ts
que hem vist a dalt pel següent codi:
import { initializeShell } from "@uxland/primary-shell";
import "@uxland/primary-shell/dist/style.css"; // Afegim els estils globals del shell i el Design System
// Creem un contenidor HTML on injectarem el shell més endavant
const createAndAppendSandboxApp = () => {
const app = document.createElement("sandbox-app");
document.body.appendChild(app);
const sandbox = document.querySelector("sandbox-app");
return sandbox as HTMLElement;
}
// Inicialitzem el sandbox i l'aplicació
const initializeSandboxApp = (sandbox: HTMLElement) => {
try {
if (sandbox) {
initializeShell(sandbox);
}
}
catch (error) {
console.warn(error);
}
}
const sandbox = createAndAppendSandboxApp();
initializeSandboxApp(sandbox);
Elimina el contenidor "app-root" i afegeix els estils bàsics del shell a l' index.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular plugin demo</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
</body>
<custom-style>
<style>
body {
margin: 0;
overflow: hidden;
font-size: 14px;
background-color: #f5f6fa;
font-family: 'Roboto', sans-serif;
pointer-events: auto !important;
width: 100%;
height: 100%;
}
html {
font-size: 14px;
overflow: hidden;
font-family: 'Roboto', sans-serif;
width: 100%;
height: 100%;
}
</style>
</custom-style>
</html>
S'hauria de veure renderitzat el Shell de primària en el navegador.
Generar projecte de plugin
Els plugins d'Harmonix amb Angular seràn realment llibreries d'Angular. Utilitzarem l'Angular CLI per generar-la.
ng generate library my-plugin
Això hauria de generar una carpeta nova amb el codi my-plugin a la carpeta projects.
Eliminar fitxers de plantilla
S'han d'eliminar els fitxer de plantilla. Eliminem la carpeta lib
i el fitxer public-api
Declarar fitxer d'entrada del plugin
S'ha de declarar un fitxer on s'implementen les funcions necessàries per a declarar un plugin. Per conveni el fitxer s'anomena plugin.ts
. Llavors crearem el fitxer projects/my-plugin/src/plugin.ts
.
import "@angular/compiler";
import { PrimariaApi } from "@uxland/primary-shell";
export const initialize = (api: PrimariaApi) => {
console.log(`Plugin ${api.pluginInfo.pluginId} initialized`);
return Promise.resolve();
};
export const dispose = (api: PrimariaApi) => {
console.log(`Plugin ${api.pluginInfo.pluginId} disposed`);
return Promise.resolve();
};
És important afegir el import "@angular/compiler";
per poder compilar en temps d'execució en l'aplicació.
En el fitxer ng-package.json
s'ha de modificar l'entrada de la llibreria a entryFile i eliminar el camp dest
.
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "src/plugin.ts"
}
}
Importar plugins en el sandbox
Declarar la definició d'importació de plugins
Crearem un fitxer amb les definicions dels plugins amb els seus importadors.
Aquí li indicarem la id de plugin i el seu mètode de càrrega.
Per a això, crearem el fitxer plugins.ts
a la carpeta src de l'arrel:
import { PluginDefinition, Plugin } from "@uxland/primary-shell";
const importer: () => Promise<Plugin> = () => import("../projects/my-plugin/src/plugin") as any;
export const plugins: PluginDefinition[] = [{ pluginId: "angular-plugin", importer: importer}]
Això importarà el plugin via el fitxer d'entrada (projects/my-plugins/src/plugin
) creat prèviament.
Executar l'arrencada de plugins
Cridarem a la funció d'arrencada de plugins.
Ho farem cridant a la funció bootstrapPlugins en el main.ts
, passant com a paràmetre les definicions de importacions (src/plugins.ts
).
import { bootstrapPlugins, initializeShell } from "@uxland/primary-shell";
import { plugins } from "./plugins";
//...
const initializeSandboxApp = (sandbox: HTMLElement) => {
try {
if (sandbox) {
initializeShell(sandbox);
bootstrapPlugins(plugins); // Cridem a la funció d'inicialització de tots els plugins
}
}
catch (error) {
console.warn(error);
}
}
//...
Ara hauriem de veure el log de consola una vegada hagi carregat el plugin.
Injectar vistes
Ara que sabem que el plugin està inicialitzat correctament, crearem un component i l'injectarem a la regió principal utilitzant el regionManager
que ens proporciona l'api.
Crear vista:
Crear component
Crear un component amb el CLI d'Angular
ng generate component MainView --project my-plugin --view-encapsulation ShadowDom
Això generarà un component d'Angular, amb la vista encapsulada en un ShadowDom, com indica.
Convertir en vista
Es crearà una carpeta anomenada views
, i mourem el component main-view
a la carpeta views
.
Crear aplicació Angular:
A diferència d'altres frameworks com Lit, React... Angular requereix una instància d'aplicació, crearem la instància en el plugin.ts
import { createApplication } from "@angular/platform-browser"; // Afegir aquest import
import { PrimariaApi } from "@uxland/primary-shell";
export const initialize = (api: PrimariaApi) => {
createApplication().then(() => console.log("Angular application created")); // Afegir aquesta linea
console.log(`Plugin ${api.pluginInfo.pluginId} initialized`);
return Promise.resolve();
};
//...
Crear factoria de vista:
Crearem una factoria de vista, per poder registrar la vista principal. Crearem un fitxer factory.ts
a la carpeta de la vista main-view
on declararem la factoria:
import { ApplicationRef, NgZone, Type } from "@angular/core";
import { MainViewComponent } from "./main-view.component";
const viewAngularFactory = <C>(app: ApplicationRef, component: Type<C>): () => Promise<HTMLElement> => () => {
const host = document.createElement("host-component");
app.injector.get(NgZone).run(() => app.bootstrap(component, host));
return Promise.resolve(host);
}
export const viewFactory = (app:ApplicationRef) => viewAngularFactory(app, MainViewComponent);
Injectar la vista a la regio principal
Després, per un costat registrarem una vista a la regió principal i per altre costat farem el mateix a la regió de navegació.
Per a això, utilitzarem el regionManager
que ens proporciona l'api.
Utilitzarem el mètode registerMainView
del regionManager
passant-li la factoria de vista.
Canviarem també la funció dispose perquè elimini la vista quan es desactivi el plugin. Per a això accedirem a les regions del shell que ens dona l'api i utilitzarem la regió main. Com a segon argument li passarem la id de la vista que volem eliminar. Com que voldrem eliminar la vista registrada amb la funció registerMainView
, li passarem aquella mateixa id:
import { createApplication } from "@angular/platform-browser";
import { PrimariaApi } from "@uxland/primary-shell";
import { viewFactory } from "./views/main-view/factory";
export const initialize = (api: PrimariaApi) => {
createApplication().then((app) => {
api.regionManager.registerMainView({
id: "plugin-main-view", // Aquí declarem la id de la vista
factory: viewFactory(app),
}); //Registrem la vista a la regio main amb la factoria declarada
});
return Promise.resolve();
};
export const dispose = (api: PrimariaApi) => {
const main = api.regionManager.regions.shell.main;
api.regionManager.removeView(main, "plugin-main-view"); // Aquí utilitzem la id de la vista del main que volem eliminar
return Promise.resolve();
}
Altres exemples d'injecció de vistes: menú navegació
Per afegir una vista del plugin en el menú lateral de navegació utilitzarem el mètode registerView del regionManager
. En aquest cas, a la factoria li passarem una instància de la classe PrimariaNavItem
importada del shell (@uxland/primary-shell), a la mateixa vegada, li passarem un objecte de configuració que tindrá la propietat "icon" amb el literal de la icona a mostrar, "label" amb el títol que es mostrarà en el menú i "callbackFn" amb la callback que activarà la vista registrada en main al clicar l'ítem del menú:
//...
import { PrimariaApi, PrimariaNavItem } from "@uxland/primary-shell";
export const initialize = (api: PrimariaApi) => {
//...
const navigationMenu = api.regionManager.regions.shell.navigationMenu;
api.regionManager.registerView(navigationMenu,{
id: "plugin-navigation-menu",
factory: () => {
const menuItem = new PrimariaNavItem({
icon: "bolt",
label: "Angular plugin",
callbackFn: () => {
api.regionManager.activateMainView("plugin-main-view");
},
});
return Promise.resolve(menuItem);
},
});
//...
};
//...
Arribats a aquest punt, en el navegador veurem el següent:
I en clicar en el botó "Angular plugin", veurem el nostre plugin funcionant i mostrat a la regió principal:
Enhorabona, has implementat el teu primer plugin a Harmonix!
Pots veure un repositori d'exemple on s'implementa un plugin amb Angular:
https://stackblitz.com/~/github.com/uxland/harmonix-angular-plugin-demo