> For the complete documentation index, see [llms.txt](https://docs.devolutions.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.devolutions.net/powershell-universal/da/apps/components/custom-components/building-custom-components.md).

# Opbygning af brugerdefinerede JavaScript-komponenter

Byg brugerdefinerede JavaScript- og React-komponenter til PowerShell Universal-apps, herunder Webpack-bundling, komponentregistrering, props og fejlfinding.

Universal kan udvides, og De kan bygge brugerdefinerede JavaScript-komponenter og frameworks. Dette dokument gennemgår, hvordan De bygger brugerdefinerede komponenter, der integreres med appplatformen i Universal.

{% hint style="warning" %}
Dette er et avanceret emne og ikke påkrævet, hvis De blot vil bruge Universal Apps.
{% endhint %}

{% hint style="info" %}
Et komplet fungerende eksempel finder De i projektet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) på GitHub. Dette er en produktionsklar brugerdefineret komponent, der integrerer diagrambiblioteket Mermaid med PowerShell Universal.
{% endhint %}

## Oversigt

Opbygning af en brugerdefineret React-komponent til PowerShell Universal omfatter flere vigtige dele:

1. **Projektstruktur**: Et Node.js-projekt med Webpack til bundling af React-komponenter
2. **React-komponent (JSX)**: Den React-komponent, der gengiver Deres brugergrænseflade
3. **PowerShell-modul (PSM1)**: PowerShell-funktioner, der opretter komponentdefinitioner
4. **Modulmanifest (PSD1)**: standardmetadata for et PowerShell-modul
5. **Buildproces**: Webpack-konfiguration til at samle JavaScript-aktiver
6. **Komponentregistrering**: Registrering af Deres komponent i Universal Dashboard

### Sådan fungerer det

Integrationen mellem PowerShell og React fungerer sådan:

1. **PowerShell-siden**: Deres PowerShell-funktion returnerer en hashtable med komponentegenskaber
2. **Aktivregistrering**: Den bundtede JavaScript registreres hos PowerShell Universals AssetService
3. **Komponenttype**: Egenskaben `type` knytter PowerShell-hashtabellen til React-komponenten
4. **Overførsel af props**: Hashtable-egenskaber bliver automatisk til React-props
5. **Gengivelse**: Universal Dashboard indlæser JavaScript-bundtet og gengiver React-komponenten

```
PowerShell Function → Hashtable → Asset Service → React Component → DOM
```

### Eksemplet med ud-mermaid

I hele denne vejledning henviser vi til projektet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) som et eksempel fra den virkelige verden. Denne komponent omslutter diagrambiblioteket Mermaid.js til brug i PowerShell Universal og demonstrerer:

* Integration af tredjeparts-JavaScript-bibliotek
* Brug af React-hooks (useEffect, useRef)
* Overførsel af konfigurationsobjekt
* Professionel projektstruktur
* Byggeautomatisering

## Trin for trin

Dette følgende afsnit fører Dem trin for trin gennem de forskellige aspekter af at opbygge en Universal App-komponent.

### 1. Installation af afhængigheder

De skal installere følgende afhængigheder, før De opretter Deres komponent.

* [NodeJS](https://nodejs.org/en/) - Påkrævet til npm og til at køre byggeværktøjer

### 2. Opret et nyt projekt

Opret et nyt katalog til Deres komponentprojekt:

```powershell
New-Item -Path .\MyComponent -ItemType Directory
Set-Location .\MyComponent
```

Initialiser et nyt npm-projekt:

```powershell
npm init -y
```

Dette opretter en grundlæggende projektstruktur, herunder:

* `package.json` - Node.js-afhængigheder og build-scripts

### 3. Installer JavaScript-afhængigheder

Installer de påkrævede byggeværktøjer og React-afhængigheder:

```powershell
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli css-loader style-loader @babel/plugin-proposal-class-properties @babel/plugin-syntax-dynamic-import --legacy-peer-deps
```

Installer Universal Dashboard-pakken:

```powershell
npm install universal-dashboard --legacy-peer-deps
```

Projektet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) indeholder f.eks. pakken `mermaid` som en yderligere afhængighed:

```json
"dependencies": {
    "mermaid": "^9.4.3",
    "universal-dashboard": "^1.0.1"
}
```

Installér alle yderligere biblioteker, som Deres komponent har brug for:

```powershell
npm install mermaid --legacy-peer-deps
```

### 4. Opret projektstruktur

Opret de nødvendige mapper til Deres komponent:

```powershell
New-Item -Path .\Components -ItemType Directory
```

### 5. Opret Deres React-komponent

Opret en React-komponent i mappen `Components/`. Deres komponent skal:

1. Importér fra `universal-dashboard`
2. Brug `withComponentFeatures`-HOC'en (højereordenskomponent)
3. Accepter props, der matcher Deres PowerShell-funktionsparametre

**Eksempel fra ud-mermaid** (`Components/mermaid.jsx`):

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return (
    <div className="mermaid" id={id} ref={mermaidRef}>
      {diagram}
    </div>
  );
};

export default withComponentFeatures(UDMermaid);
```

### 6. Registrer Deres komponent

Opret en `index.js`-fil i mappen `Components/`, der registrerer Deres komponent i Universal Dashboard:

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

Den streng, De sender til `register()`, bliver egenskaben `type`, som De bruger i Deres PowerShell-funktion.

### 7. Opret PowerShell-funktioner

Nu skal De skrive koden til PowerShell-modulet. De skal opdatere PSM1-filen, så den indlæser aktiver og definerer funktioner, der opretter komponentdefinitioner.

PSM1-filen skal:

1. Registrer den bundtede JavaScript-fil hos AssetService
2. Definer funktioner, der returnerer hashtables med komponentegenskaber

**Eksempel fra ud-mermaid** (`UniversalDashboard.Mermaid.psm1`):

```powershell
# Register JavaScript assets with PowerShell Universal
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"  # This matches the name used in UniversalDashboard.register()
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**Vigtige hashtable-egenskaber:**

* `assetId` - Id'et, der returneres fra RegisterAsset
* `isPlugin` - Altid angivet til `$true` for brugerdefinerede komponenter
* `type` - Skal matche det navn, De brugte i `UniversalDashboard.register()`
* `id` - En unik identifikator for komponentinstansen
* Yderligere egenskaber overføres som props til Deres React-komponent

### 8. Konfigurer Webpack

Deres `webpack.config.js` skal samle Deres komponenter og eksternalisere afhængighederne React og Universal Dashboard. Her er den væsentlige konfiguration fra ud-mermaid:

```javascript
module.exports = (env) => {
  return {
    entry: {
      'index': __dirname + '/components/index.js'
    },
    output: {
      path: BUILD_DIR,
      filename: '[name].[hash].bundle.js',
      library: 'udcomponent',
      libraryTarget: 'var'
    },
    module: {
      rules: [
        { test: /\.(js|jsx)$/, exclude: [/public/], loader: 'babel-loader' },
        { test: /\.css$/, loader: "style-loader!css-loader" }
      ]
    },
    externals: {
      'react': 'react',
      'react-dom': 'reactdom',
      UniversalDashboard: 'UniversalDashboard'
    },
    resolve: {
      extensions: ['.js', '.jsx']
    }
  };
}
```

**Vigtige eksterne:**

* `react` og `react-dom` - Leveret af PowerShell Universal
* `UniversalDashboard` - Det globale Universal Dashboard-objekt

### 8.1. Konfigurer Babel

Opret en `.babelrc`-fil for at konfigurere transformationen af JSX og moderne JavaScript:

```json
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": [">0.5%", "not dead"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator"
  ]
}
```

Denne konfiguration:

* Transformerer JSX til JavaScript
* Transpilerer moderne JavaScript til browserkompatibilitet
* Aktiverer nyttige JavaScript-sprogfunktioner

### 9. Opret modulmanifest

Opret et standard-PowerShell-modulmanifest (`.psd1`) med metadata for Deres komponent:

```powershell
@{
    RootModule = 'UniversalDashboard.Mermaid.psm1'
    ModuleVersion = '1.0.0'
    Author = 'Your Name'
    Description = 'Custom component description'
    FunctionsToExport = @('New-UDMermaid')
}
```

### 10. Byg projektet

De kan nu bygge Deres projekt. Det giver et modul som output, som De kan indlæse i PowerShell Universal.

Først skal De tilføje build-scripts til Deres `package.json`:

```json
"scripts": {
    "build": "webpack -p --env production",
    "dev": "webpack-dev-server --config webpack.config.js -p --env development"
}
```

Kør derefter buildet:

```powershell
npm run build
```

**Valgfrit: Opret et build-script** (som ud-mermaids `component.build.ps1`):

```powershell
# component.build.ps1
$OutputPath = "$PSScriptRoot\output"

Remove-Item -Path $OutputPath -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "$PSScriptRoot\public" -Force -ErrorAction SilentlyContinue -Recurse

npm install --legacy-peer-deps
npm run build

New-Item -Path $OutputPath -ItemType Directory

Copy-Item $PSScriptRoot\public\*.* $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psd1 $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psm1 $OutputPath
```

Kør det derefter:

```powershell
.\component.build.ps1
```

Byggeprocessen:

1. Samler al JavaScript/React-kode ved hjælp af Webpack
2. Outputter bundtede filer med hashnavne (f.eks. `index.78a6d857.bundle.js`)
3. Kopierer modulfiler (`.psm1`, `.psd1`) til outputkataloget

### 11. Brug i PowerShell Universal

I Deres app skal De indlæse Deres modul og udføre funktionen.

```powershell
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
   New-UDMermaid -Diagram @"
graph TD
    A[Start] --> B[Process]
    B --> C[End]
"@
}
```

## Eksempel på projektstruktur

Her er den typiske struktur for et brugerdefineret komponentprojekt (fra [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid)):

```
project/
├── Components/
│   ├── index.js              # Component registration
│   └── mermaid.jsx           # React component
├── output/                   # Build output (git ignored)
│   ├── index.[hash].bundle.js
│   ├── UniversalDashboard.Mermaid.psm1
│   └── UniversalDashboard.Mermaid.psd1
├── package.json              # Node.js dependencies
├── webpack.config.js         # Webpack configuration
├── component.build.ps1       # Optional build script
├── UniversalDashboard.Mermaid.psm1   # PowerShell module
└── UniversalDashboard.Mermaid.psd1   # Module manifest
```

## Props

Props er værdier, der enten overføres fra den PowerShell-hashtable, som brugeren leverer, eller af højereordensfunktionen `withComponentsFeature` i Universal App.

### Standard

De egenskaber, som De angiver i Deres hashtable i PowerShell, sendes automatisk ind som props til React-komponenten.

For eksempel, hvis De angiver egenskaberne `diagram` og `config` i hashtabellen:

```powershell
function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        type = "ud-mermaid"
        isPlugin = $true
        assetId = $AssetId 
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

Derefter har De adgang til disse props i React:

```javascript
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
  }, [diagram, config]);

  return <div className="mermaid" id={id}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**Bedste praksis for Props:**

* Brug beskrivende propnavne, der matcher PowerShell-parameternavne
* Håndter valgfrie props med standardværdier eller betinget logik
* Hashtables i PowerShell bliver automatisk til JavaScript-objekter

### React Hooks og komponentlivscyklus

Når De bygger brugerdefinerede komponenter, kan De bruge alle standard-React-hooks. Komponenten ud-mermaid demonstrerer brugen af `useEffect` og `useRef` til administration af komponentens livscyklus og DOM-referencer:

```javascript
import React, { useEffect, useRef } from 'react';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { diagram, config } = props;

  // Run when diagram or config changes
  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]); // Dependency array

  return <div ref={mermaidRef}>{diagram}</div>;
};
```

**Almindelige mønstre:**

* `useEffect` - Til initialisering, oprydning og reaktion på ændringer af props
* `useRef` - Til direkte adgang til DOM-elementer
* `useState` - Til håndtering af intern komponenttilstand
* `useMemo` / `useCallback` - Til ydeevneoptimering

### Slutpunkter

Slutpunkter er særlige i den måde, de registreres på, og den måde, de overføres som props til Deres komponent på. De skal kalde `Register` på slutpunktet i PowerShell og sende variablerne Id og PSCmdlet med.

```powershell
function New-UD95Button {
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [string]$Text,
        [Parameter()]
        [Endpoint]$OnClick
    )

    if ($OnClick)
    {
        $OnClick.Register($Id, $PSCmdlet)
    }

    @{
        type = "ud95-button"
        isPlugin = $true 
        assetId = $AssetId

        id = $Id 
        text = $Text 
        onClick = $OnClick
    }
}
```

Slutpunkter oprettes ud fra ScriptBlocks og udføres, når den pågældende hændelse indtræffer.

```powershell
New-UD95Button -Text 'Hello' -OnClick {
    Show-UDToast -Message 'Test' 
}
```

Universal kobler automatisk slutpunktet til en funktion i JavaScript. Det betyder, at De kan bruge props til at kalde det slutpunkt.

Bemærk funktionskaldet `props.onClick`. Dette kalder automatisk PowerShell-scriptblokken på serveren.

```javascript
import React from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import { Button } from 'react95';

const UD95Button = props => {

    const p = {
        onClick: () => props.onClick()
    }

    return <Button {...p}>{props.text}</Button>
}

export default withComponentFeatures(UD95Button);
```

### setState

Proppen `setState` bruges til at angive komponentens tilstand. Dette sikrer, at tilstanden spores, og at Deres komponent fungerer med `Get-UDElement`.

Med et tekstfelt vil De f.eks. gerne kalde `props.setState` og sende den nye tekstværdi for tilstanden.

```javascript
const UDTextField = (props) => {
    const onChange = (e) => {
        props.setState({value: e.target.value})
    }

    return <TextField  {...props} onChange={onChange} />
}

export default withComponentFeatures(UDTextField);
```

### children

Proppen `children` er en standardprop i React. Hvis Deres komponent understøtter underordnede elementer, f.eks. en liste eller en valgboks, bør De bruge standardproppen `props.children` for at sikre, at cmdletterne `Add-UDElement` , `Remove-UDElement` og `Clear-UDElement` fungerer korrekt.

## Fejlfinding og fejlretning

### Almindelige problemer

**Komponenten gengives ikke:**

1. Kontrollér, at `type` i Deres PowerShell-funktion matcher navnet i `UniversalDashboard.register()`
2. Kontrollér, at aktivet er korrekt registreret hos AssetService
3. Sørg for, at `isPlugin` er angivet til `$true`
4. Bekræft, at den medfølgende JavaScript-fil findes i modulkataloget

**Props overføres ikke korrekt:**

1. Kontrollér, at egenskabsnavne stemmer overens mellem PowerShell-hashtabellen og React-komponenten
2. Kontrollér browserkonsollen for JavaScript-fejl
3. Brug React DevTools til at inspicere komponentens props

**Buildfejl:**

1. Kør `npm install --legacy-peer-deps` for at sikre, at afhængigheder er installeret
2. Kontrollér for syntaksfejl i JSX-filer
3. Kontrollér, at externals i webpack.config.js er korrekt konfigureret
4. Sørg for, at babel er korrekt konfigureret til JSX-transformation

### Tips til fejlfinding

**Browserkonsol:** Åbn browserens udviklerværktøjer (F12) for at se JavaScript-fejl og advarsler.

**React DevTools:** Installér React DevTools-browserudvidelsen for at inspicere komponenthierarkiet og props.

**PowerShell-fejlfinding:** Brug `Write-Host` eller `Write-Debug` i Deres PowerShell-funktioner til at spore udførelsen.

**Webpack Dev Server:** Under udvikling skal De bruge webpack-dev-server til varm genindlæsning:

```powershell
npm run dev
```

Konfigurer derefter PowerShell Universal til at indlæse fra udviklingsserverens URL.

## Eksempel: komplet komponentarbejdsflow

Her er et komplet eksempel baseret på projektet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid):

**1. Opret React-komponent** (`Components/mermaid.jsx`):

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return <div className="mermaid" id={id} ref={mermaidRef}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**2. Registrer komponent** (`Components/index.js`):

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

**3. Opret PowerShell-funktion** (`UniversalDashboard.Mermaid.psm1`):

```powershell
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**4. Byg og test**:

```powershell
# Build
npm run build

# Test in PowerShell Universal
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
    New-UDMermaid -Diagram @"
graph TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[Car]
"@
}
```

## Yderligere ressourcer

* **ud-mermaid GitHub-lager**: <https://github.com/ironmansoftware/ud-mermaid> - Komplet fungerende eksempel
* **React-dokumentation**: <https://react.dev>
* **Webpack-dokumentation**: <https://webpack.js.org>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.devolutions.net/powershell-universal/da/apps/components/custom-components/building-custom-components.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
