---
title: "Action Polling"
slug: "action-polling"
category: "Patterns"
hosts: "ninox"
---

# Action Polling (`polling` auf `update`)

## AI Defaults (read first)

- **`polling` nur auf `type: "update"`** — wiederholt dasselbe Feld-Update in Intervallen (typisch: Trigger-Feld).
- **API-Calls gehören in den Ninox-Trigger** („Nach Änderung“), nicht ins Widget. Kein CORS-Thema.
- **Stopp:** Formel-Boolean `active` (Live über Widget-Reload) **und** `maxAttempts` als Sicherheitsnetz.
- **Loading** (Button/Layout) bleibt an, solange `perform` läuft — inkl. Poll-Dauer. Status-Texte: `loading.overlay.sequences` + `when:`.
- **Trigger-Feld zurücksetzen** am Ende des Triggers (`false` / `null`), sonst feuert „Nach Änderung“ bei erneutem `true` oft nicht.

---

## Problem

Nach einem Klick soll Ninox eine API prüfen oder ein Skript erneut ausführen, **solange** ein Job noch läuft — ohne dass der Browser die API direkt aufruft.

## Lösung

`update` mit `polling`: das Widget schreibt das Trigger-Feld wiederholt; der Trigger macht `http(...)` / Logik und schreibt den Status (z. B. in ein Data-Bag-Feld). Die Formel setzt `active` aus diesem Status.

```javascript
let state := parseJSON(text(helper_uiState));
let pollActive := state.jobStatus = "running";

actions: [{
	type: "update",
	recordId: Nr,
	fieldId: fieldId(Nr, "trigger_checkApi"),
	value: true,
	polling: {
		intervalMs: 2000,
		maxAttempts: 30,
		active: pollActive
	}
}]
```

| Feld | Default | Bedeutung |
|------|---------|-----------|
| `intervalMs` | `2000` | Pause zwischen Updates (ms) |
| `maxAttempts` | `30` | Max. Updates inkl. dem ersten |
| `active` | `true` | Poll weiter, solange `true`; Formel-Boolean (z. B. aus Data Bag) |

**Ablauf:** erstes Update → warten → wenn Live-`active` noch `true` und Attempts nicht erreicht → erneut Update → … → Stopp → Loading aus.

## Live-`active` (Button / Layout)

Bei Formel-Reload nach dem Trigger rufen Button und Layout `syncPollingGates` auf. So sieht die laufende Poll-Schleife den neuen Boolean — nicht nur den Snapshot vom Klick.

Ohne Sync (andere Widgets): Snapshot + `maxAttempts` gelten weiterhin.

## Zusammenspiel mit Loading / Sequences

```javascript
loading: {
	show: true,
	overlay: {
		title: "Bitte warten…",
		sequences: [
			{ after: 0, title: "Starte…", priority: 5 },
			{ when: pollActive, title: "API prüft…", priority: 40 }
		]
	}
}
```

`when` und `polling.active` können denselben Boolean nutzen.

## Trigger-Muster (Ninox)

1. Ja/Nein-Feld z. B. `trigger_checkApi`
2. Trigger „Nach Änderung“: wenn `true` → HTTP / Logik → Status ins Bag schreiben → `trigger_checkApi := false`
3. Widget pollt mit `value: true` und `active` aus dem Status

## Lokaler Smoke-Test (Interactive)

1. `npm start` im Widgets-Repo
2. Sidebar → **Button** → Variante **★ Polling Test (Console)** (oder P1–P3)
3. DevTools-Console filtern: `[arc-polling]`
4. Erwartung:
   - **Poll until active=false** — 3× `database.update`, dann Log `active=false`, Poll stoppt
   - **maxAttempts=3** — genau 3 Updates
   - **Single update** — genau 1 Update

## Grenzen

- Läuft nur solange der Tab / die Ansicht offen ist (kein echter Cron).
- v1: nur `type: "update"`; Live-Gate-Sync: arcCustomButton + arcCustomLayout.
