npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@phx-erp/phx-frontend-plugin-demo

v0.1.0

Published

> [English version](README.md)

Readme

PHX Frontend Plugin Demo

English version

Beispielprojekt, das zeigt, wie ein PHX-ERP-Frontend-Plugin als Angular-Web-Component mit @phx-erp/shared und @phx-erp/shared-ui erstellt wird.

Neues Plugin starten? Nutzen Sie @phx-erp/create-phx-frontend-plugin, um ein Projekt aus dem offiziellen Template zu erzeugen. Dieses Repository ist eine vollständige Demo zum Erkunden, Klonen oder als Referenz.

Dieselbe Codebasis unterstützt zwei Arbeitsweisen:

| Modus | Zweck | Typischer Befehl | | --------------------- | ----------------------------------------------- | ----------------- | | Plugin host | Ausführung in PHX als Custom Element | yarn run plugin | | Standalone client | Lokale Entwicklung wie eine normale Angular-App | yarn client |


Inhaltsverzeichnis


Übersicht

Ein PHX-Frontend-Plugin ist eine Angular-Anwendung, die als Custom Element (Web Component) verpackt wird. PHX lädt es über ein Manifest, das auf die kompilierte main.js verweist und den Element-Tag-Namen deklariert.

┌─────────────────┐     manifest.json      ┌───────────────────┐
│   PHX host      │ ─────────────────────► │  main.js (+ deps) │
│  (ERP shell)    │     loads & registers  │  custom element   │
└────────┬────────┘                        └────────┬──────────┘
         │                                          │
         │  pluginServices (Apollo, notifications…) │
         │  hostInjector                            │
         └──────────────────────────────────────────┘

Was diese Demo enthält:

  • Custom-Element-Tag: frontend-plugin-demo
  • Beispiel-Routen: Hello World, Product View, Address List
  • GraphQL-Abfragen über den PHX-Host-Apollo (Production) oder einen lokalen Apollo-Client (Development)
  • PHX-Login-Weiterleitung und Callback-Route für die eigenständige Entwicklung ohne vorkonfigurierten API-Key
  • PrimeNG- und Tailwind-Styling im PHX-Stil

Voraussetzungen

  • Node.js (LTS empfohlen)
  • Yarn v4
  • Eine laufende PHX-Instanz (für Plugin-Host-Tests und GraphQL-Schema/Codegen)

Für die lokale eigenständige Entwicklung benötigen Sie außerdem eines der folgenden:


Schnellstart

Diese Schritte gelten für dieses Demo-Repository. Um ein eigenes Plugin von Grund auf zu erstellen, nutzen Sie die Scaffold-CLI — siehe Eigenes Plugin erstellen.

  1. Abhängigkeiten installieren:
 yarn install
  1. Lokale Development-Umgebung anlegen (optional, für den Standalone-Modus):
 cp src/environments/environment.example.ts src/environments/environment.development.ts

Passen Sie apiUrl, wsUrl und optional apiKey in dieser Datei an. Die Datei ist gitignored. 3. GraphQL-Typen generieren (erfordert PHX Admin API unter der in codegen.ts konfigurierten Schema-URL):

 yarn codegen
  1. In einem der Entwicklungsmodi unten starten.

Live-Demo

Um diese Demo in Ihrer eigenen Instanz zu nutzen, fügen Sie unter Admin → Custom Elements (https://your.phx.instance/admin/customElements) ein Custom Element mit folgender Manifest-URL hinzu:

https://unpkg.com/@phx-erp/phx-frontend-plugin-demo/manifest.json

Das path-Feld des Manifests verweist auf die Paketwurzel; unpkg liefert main.js über den "."-Export. Für die lokale Entwicklung mit yarn run plugin registrieren Sie stattdessen http://localhost:3223/local.manifest.json unter Admin → Custom Elements.


Entwicklungsmodi

Standalone client (üblicher Angular-Workflow)

Startet die App mit der Development-Build-Konfiguration auf Port 4201 — Routing, PHX-Login-Weiterleitung und ein lokaler Apollo-Client funktionieren ohne Einbettung des Plugins in PHX.

yarn client
# equivalent: ng serve --port 4201 --watch --configuration development

Öffnen Sie http://localhost:4201/.

Plugin-Host-Modus (innerhalb von PHX)

Baut mit der Production-Konfiguration (ohne Output Hashing) und stellt die Ausgabe mit CORS- und No-Cache-Headern bereit, damit PHX während der Entwicklung aktuelle Bundles laden kann.

yarn run plugin

Dies führt yarn build und yarn serve parallel aus:

| Skript | Funktion | | ------------ | -------------------------------------------------------------------------------------------- | | yarn build | Production-Watch-Build | | yarn serve | Stellt dist/.../browser/ unter http://localhost:3223/ bereit |

Verweisen Sie Ihre PHX-Instanz auf das lokale Manifest:

http://localhost:3223/local.manifest.json

(public/local.manifest.json wird in die bereitgestellte Ausgabe kopiert.)

Hinweis: Verwenden Sie yarn run plugin, nicht yarn plugin — Yarn behandelt plugin als eingebauten Befehl.

Terminal Keeper ist in .vscode/sessions.json vorkonfiguriert, um sowohl yarn run plugin als auch yarn client zu starten.

Weitere nützliche Befehle

yarn start          # ng serve with development configuration (default port 4200)
yarn codegen        # Regenerate GraphQL types

Projektstruktur

phx-frontend-plugin-demo/
├── public/
│   └── local.manifest.json    # Lokales Dev-Manifest (in dist kopiert)
├── manifest.json              # Veröffentlichtes Manifest (npm/unpkg)
├── scripts/
│   └── serve-dist.mjs         # Static server for plugin-host dev
├── src/
│   ├── app/
│   │   ├── components/        # Demo pages (hello-world, product-view, …)
│   │   ├── login.ts           # Auth-Callback-Route — Weiterleitungsflow (Standalone-Entwicklung)
│   │   ├── services/
│   │   │   ├── apollo.service.ts
│   │   │   └── phoenix-host-bridge.service.ts
│   │   ├── app.config.ts      # providePhoenixPluginWithPrimeNG, providers
│   │   ├── app.routes.ts
│   │   ├── apollo.provider.ts # Local Apollo (development only)
│   │   └── auth-guard.ts
│   ├── graphql/               # GraphQL documents for codegen
│   ├── environments/
│   │   ├── environment.ts              # Production defaults (used in PHX)
│   │   ├── environment.example.ts      # Template for local dev
│   │   └── environment.development.ts  # Local overrides (gitignored)
│   └── main.ts                # Registers the custom element
├── codegen.ts
├── serve.json                 # Cache headers for serve-dist.mjs
└── tailwind.config.js

Eigenes Plugin erstellen

Scaffold-CLI (empfohlen)

Der schnellste Weg ist die offizielle Scaffold-CLI, veröffentlicht als @phx-erp/create-phx-frontend-plugin:

# Yarn Berry — einmalig, ohne globale Installation
yarn dlx @phx-erp/create-phx-frontend-plugin

# npm
npx @phx-erp/create-phx-frontend-plugin@latest

Dabei wird das offizielle Plugin-Template kopiert, Platzhalter ersetzt (Projektname, Custom-Element-Tag, API-URLs), environment.development.ts angelegt und optional yarn install ausgeführt. Nicht-interaktive Flags (--yes, --install, --api-url, --ui-url, --api-key usw.) sind auf der Paketseite dokumentiert.

Manuelles Setup (Schritt für Schritt)

Wenn Sie jeden Schritt nachvollziehen oder von Grund auf anpassen möchten, führen die folgenden Abschnitte durch die Erstellung eines Projekts, das dieser Demo ähnelt, mit Yarn v4, Angular 20, PrimeNG 20 und Tailwind CSS.

Ersetzen Sie Platzhalter wie *PROJECT-NAME*, *YOUR-TAG* und *YOUR-TOKEN* durch Ihre Werte.

1. Grundsetup

mkdir *PROJECT-NAME*
cd *PROJECT-NAME*
npx @angular/cli@20 new *PROJECT-NAME* --directory ./ --package-manager yarn --style scss --ssr false --routing --standalone
yarn add primeng@20 @primeng/themes@20 [email protected] postcss tailwindcss-primeui @angular/animations@20 @angular/elements@20
yarn add -D [email protected] @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node concurrently
ng add apollo-angular

2. Tailwind CSS

Erstellen Sie tailwind.config.js im Projektroot. Diese Konfiguration ist an PHX angelehnt:

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'selector',
  content: ['./src/**/*.{html,ts,scss}'],
  theme: {
    extend: {
      animation: {
        fadein: 'fadein 0.5s ease-in-out',
        fadeout: 'fadeout 0.5s ease-in-out',
        fadeinleft: 'fadeinleft 0.5s ease-in-out',
        fadeinright: 'fadeinright 0.5s ease-in-out',
        fadeintop: 'fadeintop 0.5s ease-in-out',
        fadeinbottom: 'fadeinbottom 0.5s ease-in-out',
      },
      keyframes: {
        fadein: { '0%': { opacity: 0 }, '100%': { opacity: 1 } },
        fadeout: { '0%': { opacity: 1 }, '100%': { opacity: 0 } },
        fadeinleft: { '0%': { opacity: 0, transform: 'translateX(-100%)' }, '100%': { opacity: 1, transform: 'translateX(0)' } },
        fadeinright: { '0%': { opacity: 0, transform: 'translateX(100%)' }, '100%': { opacity: 1, transform: 'translateX(0)' } },
        fadeintop: { '0%': { opacity: 0, transform: 'translateY(-100%)' }, '100%': { opacity: 1, transform: 'translateY(0)' } },
        fadeinbottom: { '0%': { opacity: 0, transform: 'translateY(100%)' }, '100%': { opacity: 1, transform: 'translateY(0)' } },
      },
    },
  },
  plugins: [require('tailwindcss-primeui')],
};

Aufgrund eines bekannten Problems müssen diese Direktiven in den **styles jeder Komponente** (und in src/styles.scss für die lokale Entwicklung) ergänzt werden:

@tailwind base;
@tailwind components;
@tailwind utilities;

3. GraphQL Codegen

Erstellen Sie codegen.ts im Projektroot (passen Sie die Schema-URL an, falls Ihre PHX-Instanz woanders läuft):

import type { CodegenConfig } from '@graphql-codegen/cli';

const sharedConfig = {
  scalars: { DateTime: 'Date' },
  immutableTypes: false,
} as const;

const config: CodegenConfig = {
  overwrite: true,
  schema: 'http://localhost:3000/admin-api/schema.gql',
  documents: './src/graphql/*.ts',
  ignoreNoDocuments: true,
  generates: {
    './src/app/schema-types.ts': {
      plugins: ['typescript'],
      config: sharedConfig,
    },
    './src/app/generated.ts': {
      plugins: ['typescript-operations', 'typed-document-node'],
      config: {
        ...sharedConfig,
        importSchemaTypesFrom: './src/app/schema-types.ts',
      },
    },
  },
};

export default config;

In package.json ergänzen:

"codegen": "graphql-codegen"

Definieren Sie Queries und Mutations in src/graphql/ und führen Sie anschließend aus:

yarn codegen

Dies erzeugt src/app/schema-types.ts und src/app/generated.ts.

4. Environments

Verwenden Sie getrennte Environments, damit derselbe Build als PHX-Plugin (Production) oder als eigenständige Dev-App (Development) läuft.

**src/environments/environment.interface.ts**

export abstract class Environment {
  production: boolean = false;
  apiUrl: string | undefined;
  wsUrl: string | undefined;
  apiKey: string | undefined;
  serverUrl: string = '';
}

**src/environments/environment.ts** (Production — verwendet bei Einbettung in PHX)

import { Environment } from './environment.interface';

export const environment: Environment = {
  production: true,
  apiUrl: undefined,
  wsUrl: undefined,
  apiKey: undefined,
  serverUrl: '',
};

**src/environments/environment.development.ts** (lokale Entwicklung — in .gitignore aufnehmen)

import { Environment } from './environment.interface';

export const environment: Environment = {
  production: false,
  apiUrl: 'http://localhost:3000/admin-api',
  wsUrl: 'ws://localhost:3000/admin-api',
  apiKey: undefined, // or a PHX API user token; otherwise redirects to PHX login
  serverUrl: 'https://localhost:4200', // PHX instance used for standalone login redirect
};

Fügen Sie fileReplacements zur Development-Build-Konfiguration in angular.json hinzu:

"development": {
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.development.ts"
    }
  ]
}

Tipp: Legen Sie environment.example.ts im Repository ab und kopieren Sie es lokal nach environment.development.ts, wie diese Demo es tut.

5. PHX-Bibliotheken

@phx-erp/shared und @phx-erp/shared-ui werden in der öffentlichen npm-Registry veröffentlicht.

Installieren Sie die Bibliotheken:

yarn add @phx-erp/shared @phx-erp/shared-ui

6. Plugin-Setup

Application config

Fügen Sie providePhoenixPluginWithPrimeNG in src/app/app.config.ts hinzu:

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { providePhoenixPluginWithPrimeNG } from '@phx-erp/shared-ui';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    // stripTrailingSegments: child route segments appended by PHX deep links
    // so APP_BASE_HREF still matches the host mount path.
    ...providePhoenixPluginWithPrimeNG({
      stripTrailingSegments: ['*YOUR*', '*ROUTES*', '*HERE*'],
    }),
    provideRouter(routes),
  ],
};

Routing: Übergeben Sie Ihre Routen-Pfadsegmente in stripTrailingSegments, oder leiten Sie sie automatisch ab:

stripTrailingSegments: routes.map((r) => r.path!).filter((p) => (p?.length ?? 0) > 0)

Host bridge service

Erstellen Sie src/app/services/phoenix-host-bridge.service.ts:

import { Injectable, Injector, signal } from '@angular/core';
import type { IPluginServices } from '@phx-erp/shared-ui';

@Injectable({ providedIn: 'root' })
export class PhoenixHostBridgeService {
  private readonly _hostInjector = signal<Injector | null>(null);
  private readonly _pluginServices = signal<IPluginServices | null>(null);

  hostInjector(): Injector | null {
    return this._hostInjector();
  }

  setHostInjector(injector: Injector): void {
    this._hostInjector.set(injector);
  }

  pluginServices(): IPluginServices | null {
    return this._pluginServices();
  }

  setPluginServices(services: IPluginServices): void {
    this._pluginServices.set(services);
  }
}

Die Host Bridge speichert von PHX bereitgestellte Services (Apollo Client, Notification Service usw.), damit Ihre Komponenten sie im Production-Modus nutzen können.

Root component

Ihre Root-Komponente empfängt pluginServices und hostInjector von PHX und leitet sie an die Bridge weiter:

import { Component, effect, inject, Injector, input } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { PhoenixHostBridgeService } from './services/phoenix-host-bridge.service';
import { IPluginServices, syncPhoenixHostInjector } from '@phx-erp/shared-ui';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `<router-outlet />`,
  styles: [`
@tailwind base;
@tailwind components;
@tailwind utilities;
`],
})
export class App {
  private readonly hostBridge = inject(PhoenixHostBridgeService);

  readonly pluginServices = input<IPluginServices>({});
  readonly hostInjector = input<Injector | undefined>(undefined);

  private readonly _syncHostInjector = syncPhoenixHostInjector(this.hostBridge, this.hostInjector);

  private readonly _syncPluginServices = effect(() => {
    this.hostBridge.setPluginServices(this.pluginServices());
  });
}

Bootstrap / custom element registration

Registrieren Sie in src/main.ts Ihr Plugin als Custom Element (ersetzen Sie *YOUR-TAG*):

import { appConfig } from './app/app.config';
import { App } from './app/app';
import { bootstrapPhoenixPluginCustomElement } from '@phx-erp/shared-ui';
import { environment } from './environments/environment';

bootstrapPhoenixPluginCustomElement(App, '*YOUR-TAG*', appConfig).then((app) => {
  if (!environment.production) {
    return app!.bootstrap(App);
  }
  return app;
});
  • bootstrapPhoenixPluginCustomElement — erstellt die Angular-Anwendung und registriert das Custom Element für PHX.
  • app.bootstrap(App) in Development — mountet zusätzlich die Root-Komponente, damit ng serve / der Standalone-Modus mit Routing funktioniert.

Der Custom-Element-Tag muss kleingeschrieben mit Bindestrichen sein (z. B. my-company-orders). Verwenden Sie denselben Tag in Ihrem Manifest.

7. In PHX registrieren

Manifest-Format

PHX lädt ein JSON-Manifest, das auf Ihr Entry-Skript verweist und den Custom-Element-Tag deklariert:

{
  "path": "https://example.com/path/to/main.js",
  "items": [
    {
      "tagName": "*YOUR-TAG*"
    }
  ]
}

| Feld | Beschreibung | | ----------------- | --------------------------------------------------------- | | path | Absolute URL zu main.js (und Basis für Chunk-Auflösung) | | items[].tagName | In main.ts registrierter Custom-Element-Tag |

Beispiel lokale Entwicklung (public/local.manifest.json in diesem Repo):

{
  "path": "http://localhost:3223/main.js",
  "items": [{ "tagName": "frontend-plugin-demo" }]
}

Veröffentlichtes Beispiel (Root-manifest.json, über npm/unpkg bereitgestellt):

{
  "path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
  "items": [{ "tagName": "frontend-plugin-demo" }]
}

Registrierungsschritte

  1. Hosten Sie main.js (und ggf. weitere Chunks) unter einer URL, die aus den Browsern Ihrer Nutzer erreichbar ist — dieselben Netzwerkregeln wie für Ihre PHX-Instanz.
  2. Veröffentlichen Sie ein Manifest-JSON unter einer stabilen URL.
  3. In PHX: Admin → Custom Elements → Manifest-URL hinzufügen und Mount-Pfad oder Tag wählen.
  4. Ab- und wieder anmelden. Das Plugin ist verfügbar unter https://your.phx.instance/customElements/*PATH*.

Für die lokale Plugin-Host-Entwicklung siehe Plugin lokal bereitstellen.


Weitere Themen

Plugin lokal bereitstellen

Statischen Server installieren:

yarn add -D serve

**scripts/serve-dist.mjs**

import { spawn } from 'node:child_process';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const port = process.env.PORT ?? '3223';

const serveBin = join(root, 'node_modules', '.bin', 'serve');
const child = spawn(serveBin, ['-l', port, '--cors', '--no-etag'], {
  stdio: 'inherit',
  shell: true,
  cwd: root,
});

child.on('exit', (code) => process.exit(code ?? 0));

**serve.json** (ersetzen Sie *PROJECT-NAME* durch Ihren Angular-Projektnamen aus angular.json)

{
  "public": "dist/*PROJECT-NAME*/browser",
  "headers": [
    {
      "source": "**/*.{js,mjs}",
      "headers": [
        { "key": "Cache-Control", "value": "no-store, no-cache, must-revalidate" }
      ]
    }
  ]
}

Die Bereitstellung aus dem Projektroot (nicht direkt aus dist/) stellt sicher, dass serve.json angewendet wird und verhindert veraltete gecachte main.js nach Rebuilds.

In package.json ergänzen:

"serve": "node ./scripts/serve-dist.mjs"

Eigener Port: PORT=8080 yarn serve.

Apollo, Auth Guard und Login

Im Production-Modus (eingebettet in PHX) übernimmt der Host die Authentifizierung. Nutzen Sie den Apollo Client aus IPluginServices über PhoenixHostBridgeService — ein separater Login-Flow ist nicht nötig.

Im Development-Modus (Standalone) stellen Sie einen eigenen Apollo Client bereit und leiten nicht authentifizierte Nutzer zur PHX-Login-Seite weiter. PHX leitet danach zurück auf eine lokale /login-Callback-Route, die den Token speichert und den Nutzer zur ursprünglichen Seite zurückbringt.

Apollo provider

Erstellen Sie src/app/apollo.provider.ts (vollständige Implementierung in diesem Repository). Er:

  • verbindet HTTP- und WebSocket-Links mit environment.apiUrl / environment.wsUrl
  • sendet Authorization: Bearer … aus environment.apiKey oder localStorage
  • speichert Tokens aus dem Response-Header phoenix-auth-token

Nur in Development registrieren, z. B. in app.config.ts:

...(environment.production ? [] : [...apolloProvider()])

Apollo service abstraction

Ein kleiner Service wählt im Production-Modus den Host-Apollo-Client und im Development-Modus den lokalen Client:

@Injectable({ providedIn: 'root' })
export class ApolloService {
  private readonly _apollo = signal<Apollo>(undefined!);

  constructor(private readonly injector: Injector) {
    if (environment.production) {
      this._apollo.set(injector.get(PhoenixHostBridgeService)?.pluginServices()?.apollo!);
    } else {
      this._apollo.set(
        injector.get(PhoenixHostBridgeService)?.pluginServices()?.apollo ?? injector.get(Apollo)
      );
    }
  }

  apollo = () => this._apollo();
}

Wenden Sie dasselbe Muster auf andere Host-Services an (z. B. Notifications), wenn Sie Standalone-Fallbacks benötigen.

Login-Callback-Route

Für die Entwicklung ohne voreingestellten API-Key benötigen Sie einen Weg, einen API-Token zu erhalten und zu speichern. Dieses Demo nutzt einen weiterleitungsbasierten Auth-Callback auf /login statt eines eingebetteten Login-Formulars — beide Ansätze sind jedoch möglich: Sie können auch ein eigenes Login-Formular bauen, die GraphQL-login-Mutation aufrufen und den Token selbst verwalten.

Bei der Weiterleitungsvariante leitet der Auth Guard nicht authentifizierte Nutzer zur PHX-Login-Seite (environment.serverUrl) weiter. Nach erfolgreichem Login leitet PHX zurück auf die /login-Route Ihres Plugins mit einem authToken-Query-Parameter und einem base64-kodierten redirectTo-Ziel. Die Callback-Komponente speichert den Token in localStorage und navigiert zurück zur ursprünglichen Seite:

@Component({
  selector: 'app-login',
  template: `
    <div class="flex flex-col items-center justify-center h-screen w-screen opacity-20">
      <div class="animate-fadein duration-1000 animate-infinite animate-alternate text-4xl">Signing in...</div>
    </div>
  `,
})
export class Login {
  constructor(private readonly route: ActivatedRoute) {
    this.route.queryParams.subscribe((params) => {
      const token = params['authToken'];
      if (token) {
        const redirectTo = decodeURIComponent(atob(params['redirectTo']));
        localStorage.setItem('api-key', token);
        window.history.replaceState({}, '', redirectTo);
        window.location.href = redirectTo;
      }
    });
  }
}

Die vollständige Implementierung finden Sie unter src/app/login.ts in diesem Repo.

Auth guard

Nicht authentifizierte Nutzer nur im Development-Modus zur PHX-Login-Seite weiterleiten. Der redirectTo-Query-Parameter verknüpft die Weiterleitung über die /login-Callback-Route Ihres Plugins:

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): boolean {
    const token = environment.apiKey ?? localStorage.getItem('api-key');
    if (!environment.production && !token) {
      const redirectTo = encodeURIComponent(
        btoa(`${window.location.protocol}//${window.location.host}/login?redirectTo=${encodeURIComponent(btoa(window.location.href))}`)
      );
      window.location.href = `${environment.serverUrl}/login?redirectTo=${redirectTo}`;
      return false;
    }
    return true;
  }
}

In app.routes.ts einbinden:

const canActivate = [environment.production ? () => true : AuthGuard];

export const routes: Routes = [
  { path: '', canActivate, component: HelloWorld },
  { path: 'product-view', canActivate, component: ProductView },
  { path: 'address-list', canActivate, component: AddressList },
  { path: 'login', component: Login },
];

package.json scripts

Empfohlene Skripte nach dieser Anleitung:

{
  "scripts": {
    "build": "ng build --watch --output-hashing none --configuration production",
    "serve": "node ./scripts/serve-dist.mjs",
    "plugin": "concurrently \"yarn build\" \"yarn serve\"",
    "client": "ng serve --port 4201 --watch --configuration development",
    "codegen": "graphql-codegen"
  }
}

| Skript | Beschreibung | | --------- | ---------------------------------------------------------------------- | | build | Production-Watch-Build für PHX | | serve | Stellt kompilierte Assets für PHX bereit (Standard-Port 3223) | | plugin | Führt build + serve zusammen aus — verwenden Sie yarn run plugin | | client | Eigenständiger Angular-Dev-Server auf Port 4201 | | codegen | Generiert GraphQL-TypeScript-Typen neu |


Fehlerbehebung

| Symptom | Zu prüfen | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | PHX zeigt eine alte Plugin-Version | Hard-Refresh; prüfen, ob serve.json Cache-Control: no-store für JS setzt; yarn run plugin neu starten | | 401 / GraphQL-Auth-Fehler im Standalone-Modus | apiKey in environment.development.ts setzen, oder sicherstellen, dass serverUrl auf Ihre PHX-Instanz zeigt, damit die Login-Weiterleitung funktioniert | | yarn add @phx-erp/shared schlägt fehl | Netzwerkzugriff auf die öffentliche npm-Registry prüfen; yarn install erneut ausführen | | Routing in PHX funktioniert nicht | Routen-Segmente zu stripTrailingSegments in providePhoenixPluginWithPrimeNG hinzufügen | | Tailwind-Klassen fehlen in einer Komponente | @tailwind-Direktiven in den styles der Komponente ergänzen | | yarn codegen schlägt fehl | PHX muss laufen und die Schema-URL in codegen.ts erreichbar sein | | Custom Element nicht gefunden | Tag im Manifest muss exakt mit customElements.define / bootstrapPhoenixPluginCustomElement übereinstimmen |


Support

Bei Integrationsfragen oder PHX-spezifischen APIs wenden Sie sich an Ihren PHX-Partner.