> 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/it/app/components/custom-components/building-custom-components.md).

# Creazione di componenti JavaScript personalizzati

Universal è estensibile e permette di creare componenti e framework JavaScript personalizzati. Questo documento illustra come creare componenti personalizzati che si integrano con la piattaforma di app Universal.

{% hint style="warning" %}
Questo è un argomento avanzato e non è necessario se desidera semplicemente utilizzare le Universal Apps.
{% endhint %}

{% hint style="info" %}
Per un esempio completo e funzionante, consulti il progetto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) su GitHub. Si tratta di un componente personalizzato pronto per la produzione che integra la libreria di diagrammi Mermaid con PowerShell Universal.
{% endhint %}

## Panoramica

La creazione di un componente React personalizzato per PowerShell Universal coinvolge diversi elementi chiave:

1. **Struttura del progetto**: un progetto Node.js con Webpack per il bundling dei componenti React
2. **Componente React (JSX)**: il componente React che esegue il rendering della sua interfaccia utente
3. **Modulo PowerShell (PSM1)**: funzioni PowerShell che creano le definizioni dei componenti
4. **Manifesto del modulo (PSD1)**: metadati standard del modulo PowerShell
5. **Processo di build**: configurazione Webpack per il bundling degli asset JavaScript
6. **Registrazione del componente**: registrazione del componente con Universal Dashboard

### Come funziona

L'integrazione tra PowerShell e React funziona in questo modo:

1. **Lato PowerShell**: la sua funzione PowerShell restituisce una hashtable con le proprietà del componente
2. **Registrazione degli asset**: il JavaScript in bundle viene registrato con l'AssetService di PowerShell Universal
3. **Tipo di componente**: la proprietà `type` collega la hashtable di PowerShell al componente React
4. **Passaggio delle props**: le proprietà della hashtable diventano automaticamente props di React
5. **Rendering**: Universal Dashboard carica il bundle JavaScript ed esegue il rendering del componente React

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

### L'esempio ud-mermaid

In questa guida faremo riferimento al progetto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) come esempio reale. Questo componente incapsula la libreria di diagrammi Mermaid.js per l'utilizzo in PowerShell Universal, dimostrando:

* Integrazione di librerie JavaScript di terze parti
* Utilizzo degli hook React (useEffect, useRef)
* Passaggio di oggetti di configurazione
* Struttura professionale del progetto
* Automazione della build

## Passo dopo passo

La sezione seguente la guiderà passo dopo passo attraverso i diversi aspetti della creazione di un componente per Universal App.

### 1. Installazione delle dipendenze

Prima di creare il componente dovrà installare le seguenti dipendenze.

* [NodeJS](https://nodejs.org/en/) - Necessario per npm e per l'esecuzione degli strumenti di build

### 2. Creare un nuovo progetto

Crei una nuova directory per il progetto del componente:

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

Inizializzi un nuovo progetto npm:

```powershell
npm init -y
```

Questo crea una struttura di progetto di base che include:

* `package.json` - Dipendenze Node.js e script di build

### 3. Installare le dipendenze JavaScript

Installi gli strumenti di build e le dipendenze React necessari:

```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
```

Installi il pacchetto Universal Dashboard:

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

Ad esempio, il progetto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) include il pacchetto `mermaid` come dipendenza aggiuntiva:

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

Installi eventuali librerie aggiuntive necessarie al suo componente:

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

### 4. Creare la struttura del progetto

Crei le directory necessarie per il suo componente:

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

### 5. Creare il componente React

Crei un componente React nella directory `Components/`. Il componente dovrebbe:

1. Importare da `universal-dashboard`
2. Utilizzare l'HOC `withComponentFeatures` (Higher-Order Component)
3. Accettare props che corrispondono ai parametri della sua funzione PowerShell

**Esempio da 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. Registrare il componente

Crei un file `index.js` nella directory `Components/` che registri il suo componente con Universal Dashboard:

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

La stringa che passa a `register()` diventa la proprietà `type` che utilizzerà nella sua funzione PowerShell.

### 7. Creare le funzioni PowerShell

Ora dovrà scrivere il codice del modulo PowerShell. Dovrà aggiornare il file PSM1 per caricare gli asset e definire le funzioni che creano le definizioni dei componenti.

Il file PSM1 dovrebbe:

1. Registrare il file JavaScript in bundle con l'AssetService
2. Definire funzioni che restituiscono hashtable con le proprietà del componente

**Esempio da 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
    }
}
```

**Proprietà chiave della hashtable:**

* `assetId` - L'ID restituito da RegisterAsset
* `isPlugin` - Sempre impostato su `$true` per i componenti personalizzati
* `type` - Deve corrispondere al nome utilizzato in `UniversalDashboard.register()`
* `id` - Un identificatore univoco per l'istanza del componente
* Le proprietà aggiuntive vengono passate come props al suo componente React

### 8. Configurare Webpack

Il suo `webpack.config.js` dovrebbe eseguire il bundling dei componenti ed esternalizzare le dipendenze React e Universal Dashboard. Ecco la configurazione essenziale di 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']
    }
  };
}
```

**Externals importanti:**

* `react` e `react-dom` - Forniti da PowerShell Universal
* `UniversalDashboard` - L'oggetto globale Universal Dashboard

### 8.1. Configurare Babel

Crei un file `.babelrc` per configurare la trasformazione di JSX e del JavaScript moderno:

```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"
  ]
}
```

Questa configurazione:

* Trasforma JSX in JavaScript
* Transpila il JavaScript moderno per la compatibilità con i browser
* Abilita utili funzionalità del linguaggio JavaScript

### 9. Creare il manifesto del modulo

Crei un manifesto standard del modulo PowerShell (`.psd1`) con i metadati del suo componente:

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

### 10. Compilare il progetto

Ora può compilare il progetto. Verrà generato un modulo che potrà caricare in PowerShell Universal.

Per prima cosa, aggiunga gli script di build al suo `package.json`:

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

Quindi esegua la build:

```powershell
npm run build
```

**Facoltativo: creare uno script di build** (come il `component.build.ps1` di ud-mermaid):

```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
```

Poi lo esegua:

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

Il processo di build:

1. Esegue il bundling di tutto il codice JavaScript/React utilizzando Webpack
2. Genera file in bundle con nomi con hash (ad esempio, `index.78a6d857.bundle.js`)
3. Copia i file del modulo (`.psm1`, `.psd1`) nella directory di output

### 11. Utilizzo in PowerShell Universal

All'interno della sua app, carichi il modulo ed esegua la funzione.

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

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

## Esempio di struttura del progetto

Ecco la struttura tipica di un progetto di componente personalizzato (da [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

Le props sono valori passati dalla hashtable PowerShell fornita dall'utente oppure dalla funzione high-order `withComponentsFeature` di Universal App.

### Standard

Le proprietà impostate nella hashtable in PowerShell verranno inviate automaticamente come props al componente React.

Ad esempio, se imposta le proprietà `diagram` e `config` nella hashtable:

```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
    }
}
```

Avrà quindi accesso a quelle props in 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);
```

**Best practice per le props:**

* Utilizzi nomi di props descrittivi che corrispondano ai nomi dei parametri PowerShell
* Gestisca le props facoltative con valori predefiniti o logica condizionale
* Le hashtable in PowerShell diventano automaticamente oggetti JavaScript

### Hook React e ciclo di vita dei componenti

Quando crea componenti personalizzati, può utilizzare tutti gli hook React standard. Il componente ud-mermaid dimostra l'utilizzo di `useEffect` e `useRef` per gestire il ciclo di vita del componente e i riferimenti al DOM:

```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>;
};
```

**Pattern comuni:**

* `useEffect` - Per l'inizializzazione, la pulizia e la risposta alle modifiche delle props
* `useRef` - Per accedere direttamente agli elementi del DOM
* `useState` - Per gestire lo stato interno del componente
* `useMemo` / `useCallback` - Per l'ottimizzazione delle prestazioni

### Endpoint

Gli endpoint sono speciali per il modo in cui vengono registrati e per il modo in cui vengono passati come props al suo componente. Dovrà chiamare `Register` sull'endpoint in PowerShell e passare le variabili Id e PSCmdlet.

```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
    }
}
```

Gli endpoint vengono creati da ScriptBlock e vengono eseguiti al verificarsi dell'evento corrispondente.

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

Universal collegherà automaticamente l'endpoint a una funzione in JavaScript. Ciò significa che può utilizzare le props per chiamare quell'endpoint.

Noti la chiamata alla funzione `props.onClick`. Questa chiamerà automaticamente lo script block PowerShell sul server.

```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

La prop `setState` viene utilizzata per impostare lo stato del componente. Ciò garantisce che lo stato venga monitorato e che il suo componente funzioni con `Get-UDElement`.

Ad esempio, con un campo di testo, dovrà chiamare `props.setState` e passare il nuovo valore di testo per lo stato.

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

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

export default withComponentFeatures(UDTextField);
```

### children

La prop `children` è una prop React standard. Se il suo componente supporta elementi figlio, come un elenco o una casella di selezione, dovrebbe utilizzare la prop standard `props.children` per garantire il corretto funzionamento dei cmdlet `Add-UDElement`, `Remove-UDElement` e `Clear-UDElement`.

## Risoluzione dei problemi e debug

### Problemi comuni

**Il componente non viene renderizzato:**

1. Verifichi che il `type` nella sua funzione PowerShell corrisponda al nome di `UniversalDashboard.register()`
2. Controlli che l'asset sia registrato correttamente con AssetService
3. Si assicuri che `isPlugin` sia impostato su `$true`
4. Confermi che il file JavaScript in bundle esista nella directory del modulo

**Le props non vengono passate correttamente:**

1. Verifichi che i nomi delle proprietà corrispondano tra la hashtable PowerShell e il componente React
2. Controlli la console del browser per eventuali errori JavaScript
3. Usi React DevTools per ispezionare le props del componente

**Errori di build:**

1. Esegua `npm install --legacy-peer-deps` per assicurarsi che le dipendenze siano installate
2. Controlli la presenza di errori di sintassi nei file JSX
3. Verifichi che gli externals di webpack.config.js siano configurati correttamente
4. Si assicuri che babel sia configurato correttamente per la trasformazione JSX

### Suggerimenti per il debug

**Console del browser:** Apra gli strumenti di sviluppo del browser (F12) per vedere errori e avvisi JavaScript.

**React DevTools:** Installi l'estensione del browser React DevTools per ispezionare la gerarchia dei componenti e le props.

**Debug di PowerShell:** Usi `Write-Host` o `Write-Debug` nelle sue funzioni PowerShell per tracciare l'esecuzione.

**Webpack Dev Server:** Durante lo sviluppo, usi webpack-dev-server per il hot reloading:

```powershell
npm run dev
```

Quindi configuri PowerShell Universal per caricare dall'URL del dev server.

## Esempio: flusso di lavoro completo di un componente

Ecco un esempio completo basato sul progetto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid):

**1. Creare il componente React** (`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. Registrare il componente** (`Components/index.js`):

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

**3. Creare la funzione PowerShell** (`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. Compilare e testare**:

```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]
"@
}
```

## Risorse aggiuntive

* **Repository GitHub ud-mermaid**: <https://github.com/ironmansoftware/ud-mermaid> - Esempio funzionante completo
* **Documentazione di React**: <https://react.dev>
* **Documentazione di Webpack**: <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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
