Zum Hauptinhalt springen

Maskendesigner – Scripts

Mit Scripts erweitern Sie Ihre Masken um dynamisches Verhalten, das über Formeln und Feldeigenschaften hinausgeht: Felder abhängig voneinander ein- und ausblenden, Pflichtangaben situativ setzen, Summen live berechnen, Eingaben validieren, Felder sperren oder farblich hervorheben. Scripts werden in JavaScript geschrieben, direkt im Maskendesigner bearbeitet und zusammen mit der Maske gespeichert.

Verfügbarkeit

Die Script-Funktion ist Teil von Netxp:Verein Pro. Ohne Pro-Lizenz wird die Schaltfläche „Scripts" im Maskendesigner nicht angezeigt, und bereits hinterlegte Scripts werden nicht ausgeführt.

Plattformneutral by Design

Scripts greifen niemals direkt auf die Windows-Oberfläche zu, sondern ausschließlich auf die hier dokumentierte form/data-API. Dieselben Scripts laufen dadurch unverändert in der geplanten Web-Variante (Angular/form.io) weiter. Schreiben Sie Scripts in klassischem JavaScript (ES5): var statt let, klassische Funktionen statt Arrow-Syntax.

Was ist damit möglich?

  • Sichtbarkeit steuern: Paketadresse nur zeigen, wenn Versandart = „Paket".
  • Pflichtfelder dynamisch: Feld X ist nur Pflicht, wenn Feld Y gefüllt ist.
  • Berechnungen: Gesamtbetrag = Beitrag + Zusatzbeitrag, live beim Tippen.
  • Validierung: Wertebereiche und Plausibilitäten prüfen, eigene Fehlermeldungen, Speichern bei Fehlern verhindern.
  • Sperren: Ein Feld je nach Kontext auf schreibgeschützt oder inaktiv setzen.
  • Darstellung: Felder ab einem Schwellwert farblich markieren.

Voraussetzungen

  • Netxp:Verein Pro (siehe oben).
  • Sie sind als Admin angemeldet (der Maskendesigner ist eine Admin-Funktion).
  • Eine Maske ist im Maskendesigner geöffnet oder neu angelegt.
  • Grundkenntnisse in JavaScript sind hilfreich — für die häufigsten Fälle reichen die mitgelieferten Funktionsrümpfe und Snippets (siehe unten).

Den Script-Editor öffnen

Es gibt zwei Wege zum Script-Editor:

  1. Aus dem Maskendesigner: Maske öffnen, in der Aktionsleiste unten auf Scripts klicken.
  2. Direkt aus den Mitgliederdetails: Rechtsklick auf die Reiterleiste bzw. den freien Bereich der Maske → „Maske im Designer bearbeiten…". Der Designer öffnet sich mit genau der aktuell angezeigten Maske; nach dem Schließen wird die Mitgliederansicht sofort neu geladen. Dieser Eintrag steht Pro-Vereinen sowie dem Netxp:Verein-Support zur Verfügung (jeweils mit Maskendesigner-Recht).

Der Script-Editor ist zweigeteilt:

  • Links der Baum: Formular (global) sowie jedes Feld der Maske mit den jeweils möglichen Ereignissen. Einträge mit vorhandenem Script werden fett dargestellt.
  • Rechts der Code-Editor mit Syntax-Hervorhebung und einer Werkzeugleiste:
SchaltflächeFunktion
Funktionsrumpf einfügenFügt für das gewählte Feld × Ereignis eine kommentierte Code-Vorlage ein — Sie starten nie mit einer leeren Seite.
SnippetsBibliothek der häufigsten Muster (Sichtbarkeit, Summe, Wertebereich, Pflichtfeld, Farbe, Datenkontext, Alter, Datumsprüfung). Platzhalter wie <feld> ersetzen Sie nach dem Einfügen.
Syntax prüfenPrüft den Code auf Syntaxfehler (mit Zeilenangabe), ohne ihn auszuführen.
Testlauf ▶Führt das Script sofort in der echten Script-Umgebung aus und zeigt im Ausgabefenster alle form.*-Aufrufe und console.log-Ausgaben.
Debounce (ms)Nur bei changed: Wartezeit nach dem letzten Tastendruck, bevor das Script läuft (empfohlen 100–200 ms bei Textfeldern).

Mit OK übernehmen Sie die Scripts in die Maske. Gespeichert werden sie erst mit dem Speichern der Maske (Schaltfläche Speichern im Maskendesigner) — zusammen mit dem Layout, in derselben Maskendefinition.

Adressiert wird immer der Feldname

Scripts sprechen Felder über ihren logischen Feldnamen an (den Tag des Feldes) — nicht über den Control-Namen und nicht über die Beschriftung. Beispiel: form.setValue("beitrag", 120).

Woher kenne ich die Feldnamen?

Die Feldnamen müssen Sie nicht raten — es gibt drei zuverlässige Quellen:

  • Im Script-Editor (am einfachsten): Der Baum links listet jedes Feld der Maske mit genau dem Namen auf, den Sie im Script verwenden. Wählen Sie Feld und Ereignis aus und klicken Sie Funktionsrumpf einfügen — der richtige Feldname steht dann bereits im eingefügten Code.
  • Im Maskendesigner: Feld anklicken → im Eigenschafts-Panel rechts steht unter Tag der Feldname.
  • Eigene Felder folgen immer dem Schema OwnField:<Feldname>, wobei <Feldname> der Name aus Optionen → Eigene Felder ist — z. B. OwnField:Ehrenmitglied.

Standardfelder tragen ihren technischen Namen, nicht die deutsche Beschriftung — z. B. LeaveDate für das Austrittsdatum. Auch diesen sehen Sie im Script-Baum bzw. unter Tag.

Wert eines Feldes lesen, das nicht auf der Maske liegt

data.get("mitglied.…") benötigt den technischen Feldnamen des Mitglieds. Wenn Sie unsicher sind: Ziehen Sie das gewünschte Feld aus dem Feld-Katalog auf die Maske (bei Bedarf mit form.setVisible("feld", false) unsichtbar) und lesen Sie es dann bequem mit form.getValue("feld") — so müssen Sie keinen Namen erraten.

Ereignisse

Ein Script hängt immer an einem Ereignis — entweder an einem Feld oder am Formular als Ganzem. Welche Ereignisse ein Feld anbietet, hängt vom Feldtyp ab (z. B. bietet eine Schaltfläche nur click).

EreignisWann läuft es?SkopusTypische Verwendung
loadFormular ist aufgebaut, Daten sind gebundenFormularAnfangszustand: Sichtbarkeit, Startberechnungen
changedDer Wert eines Feldes hat sich geändertFeldFolgefelder steuern, Summen berechnen
clickFeld/Schaltfläche wurde angeklicktFeldAktionen auslösen
validateVor dem Speichern sowie beim Verlassen des FeldesFeld/FormularFehler setzen/entfernen mit form.setError
beforeSubmitUnmittelbar vor dem SpeichernFormularFinale Berechnungen und Prüfungen
afterSubmitNach erfolgreichem SpeichernFormularAufräumarbeiten

Im Script steht das auslösende Ereignis als Objekt event zur Verfügung:

event.name // z. B. "changed"
event.field // Feldname des Auslösers (bei Feld-Ereignissen)
event.newValue // neuer Wert (bei changed)
event.oldValue // vorheriger Wert (bei changed)
load und changed gehören zusammen

Was ein changed-Script steuert, sollte ein load-Script beim Öffnen initialisieren — sonst stimmt der Zustand beim Laden eines bestehenden Datensatzes nicht. Am einfachsten legen Sie denselben Code in beide Ereignisse.

API-Referenz

Im Script sind fünf Objekte verfügbar: form, data, event, util und console. Alles andere (Dateisystem, Netzwerk, Datenbank) ist bewusst nicht erreichbar.

form — die Maske steuern

FunktionWirkung
form.getValue(feld)Aktuellen Wert eines Feldes lesen (Text, Zahl, true/false oder Datum – je nach Feldtyp)
form.setValue(feld, wert)Wert setzen
form.setVisible(feld, true/false)Feld (samt zugehöriger Beschriftung) ein-/ausblenden
form.setEnabled(feld, true/false)Feld aktivieren/deaktivieren (ausgegraut, nicht mehr anwählbar)
form.setReadOnly(feld, true/false)Schreibschutz setzen (Wert bleibt sicht- und markierbar)
form.setRequired(feld, true/false)Pflichtfeld-Kennzeichen setzen (geprüft beim Speichern)
form.setError(feld, "Meldung")Fehlermarkierung mit Meldung setzen — verhindert das Speichern
form.clearError(feld)Fehlermarkierung entfernen
form.setColor(feld, farbe)Hintergrundfarbe: Token "error", "warning", "ok", "info", "default" oder "#RRGGBB"
form.setLabel(feld, "Text")Beschriftung des Feldes ändern
form.focus(feld)Eingabefokus auf das Feld setzen
form.exists(feld)true, wenn das Feld in der Maske vorhanden ist
setReadOnly oder setEnabled?

setReadOnly(feld, true) sperrt die Eingabe, lässt den Wert aber lesbar und markierbar — meist die bessere Wahl bei Text- und Datumsfeldern. setEnabled(feld, false) graut das Feld komplett aus und nimmt es aus der Tab-Reihenfolge — passend für Schaltflächen oder wenn ein Feld „tot" wirken soll.

Farb-Tokens statt Hex-Werte

Verwenden Sie bevorzugt die Tokens ("warning" statt "#FFEB9C") — sie werden zentral gepflegt und funktionieren später auch im Web-Design.

Gesperrte Felder

Ein Feld mit Bearbeitungsverhalten ImmerGesperrt kann ein Script zwar zusätzlich sperren, aber nicht freischalten — die harte Sperre gewinnt immer.

data — Daten lesen (nur lesend!)

Zugriff auf die Daten des geöffneten Datensatzes über Punkt-Pfade. data kann nur lesen — geschrieben wird ausschließlich über form.setValue in Maskenfelder.

data.get("mitglied.LeaveDate") // Wert lesen (null, wenn nicht vorhanden)
data.has("mitglied.LeaveDate") // true, wenn ein Wert vorhanden ist

Verfügbare Datenquellen (erste Pfad-Ebene):

QuelleInhalt
mitglied.*Daten des geöffneten Mitglieds (in Mitgliedermasken)
sparte.*Daten der geöffneten Sparte (in Spartenmasken, sofern für die Maske aktiviert)

Der Teil nach dem Punkt ist der technische Feldname des Datenobjekts (wie im Feld-Tag). Die Groß-/Kleinschreibung spielt keine Rolle.

form.getValue vs. data.get

Für Felder, die in der Maske sichtbar sind, verwenden Sie am einfachsten form.getValue("feld") — das liefert den aktuell angezeigten Wert. data.get("mitglied.…") brauchen Sie vor allem für Werte, die nicht als Feld auf der Maske liegen.

util — Hilfsfunktionen

FunktionWirkung
util.parseNumber(wert)Wert in Zahl wandeln — versteht auch deutsche Schreibweise ("1.234,56"1234.56); liefert 0 statt Fehler
util.round(zahl, stellen)Kaufmännisch runden
util.formatDate(wert, "dd.MM.yyyy")Datum formatieren; leer bei ungültigem Datum
util.today()Heutiges Datum als "JJJJ-MM-TT"
util.ageInYears(geburtsdatum)Alter in vollen Jahren
util.dateKey(wert)Datum als vergleichbare Zahl JJJJMMTT (z. B. 20240501); 0 bei leer/ungültig
util.hasRealDate(wert)true, wenn ein echtes Datum nach dem 01.01.1900 gesetzt ist
util.isEmpty(wert)true bei null, leer oder nur Leerzeichen
Zahlen immer über util.parseNumber

form.getValue liefert bei Textfeldern eine Zeichenkette. "10" + "5" ergibt in JavaScript "105" (Textverkettung)! Rechnen Sie deshalb immer mit util.parseNumber(...).

Datumsfelder nicht direkt mit > vergleichen

form.getValue(...) liefert bei Datumsfeldern eine Zeichenkette im deutschen Format ("01.05.2024"). Ein direkter Vergleich (> oder new Date(...)) schlägt damit fehl. Verwenden Sie util.dateKey(...) — das liefert eine vergleichbare Zahl im Format JJJJMMTT. Ein leeres Datumsfeld ist im Datenmodell der Platzhalter „kein Datum" bzw. 01.01.1900 und ergibt 0, zählt also immer als kleiner.

console — Ausgaben für die Fehlersuche

console.log("Neuer Wert:", event.newValue);

Ausgaben erscheinen beim Testlauf im Ausgabefenster des Editors und zur Laufzeit im Programm-Protokoll.

Beispiele

Feld abhängig ein-/ausblenden

Ereignis changed am Feld versand (und identisch als load-Script, damit der Anfangszustand stimmt):

var v = form.getValue("versand");
form.setVisible("paketAdresse", v === "Paket");
form.setRequired("paketAdresse", v === "Paket");
if (v !== "Paket") { form.clearError("paketAdresse"); }

Summe live berechnen

Ereignis changed an beitrag und zusatzbeitrag (Debounce 150 ms empfohlen):

var beitrag = util.parseNumber(form.getValue("beitrag"));
var zusatz = util.parseNumber(form.getValue("zusatzbeitrag"));
form.setValue("gesamtbetrag", util.round(beitrag + zusatz, 2));

Eingabe validieren

Ereignis validate am Feld beitrag:

var wert = util.parseNumber(form.getValue("beitrag"));
if (wert < 0) {
form.setError("beitrag", "Der Beitrag muss 0 oder größer sein.");
} else {
form.clearError("beitrag");
}

Feld sperren statt ausblenden

Manchmal soll ein Feld sichtbar bleiben, aber nicht änderbar sein. Ereignis changed an der Checkbox foerdermitglied (und als load):

var foerder = form.getValue("foerdermitglied") === true;
form.setReadOnly("beitrag", foerder);
form.setColor("beitrag", foerder ? "info" : "default");

Farbliche Warnung ab Schwellwert

var gesamt = util.parseNumber(form.getValue("gesamtbetrag"));
form.setColor("gesamtbetrag", gesamt > 1000 ? "warning" : "default");

Wert aus den Mitgliedsdaten übernehmen

Ereignis load (formglobal):

var alter = util.ageInYears(data.get("mitglied.geburtsdatum"));
form.setValue("alterAnzeige", alter);
form.setVisible("erziehungsberechtigte", alter < 18);

Vollständiges Beispiel: Ehrenmitglied ohne Austrittsdatum

Ein Ehrenmitglied soll kein Austrittsdatum haben. Solange kein Datum gesetzt ist, wird das Feld gesperrt; ist bereits eines vorhanden, erscheint ein Fehler (das Feld bleibt dann editierbar, damit man das Datum entfernen kann). Ohne Ehrenmitglied ist das Feld frei.

Denselben Code an drei Ereignissen hinterlegen: changed an der Checkbox OwnField:Ehrenmitglied, changed am Feld LeaveDate und load (formglobal):

var ehrenmitglied = form.getValue("OwnField:Ehrenmitglied") === true;
var hatAustritt = util.hasRealDate(form.getValue("LeaveDate"));

if (ehrenmitglied) {
if (hatAustritt) {
// Ehrenmitglied MIT Austrittsdatum → nicht erlaubt.
// Feld editierbar lassen, damit der Benutzer das Datum entfernen kann.
form.setReadOnly("LeaveDate", false);
form.setError("LeaveDate", "Ein Ehrenmitglied darf kein Austrittsdatum haben.");
} else {
// Ehrenmitglied OHNE Austrittsdatum → Feld sperren.
form.clearError("LeaveDate");
form.setReadOnly("LeaveDate", true);
}
} else {
// Kein Ehrenmitglied → Feld wieder frei.
form.clearError("LeaveDate");
form.setReadOnly("LeaveDate", false);
}

Warum drei Ereignisse? Die Checkbox-Prüfung reagiert auf das Setzen/Entfernen des Hakens, das LeaveDate-changed fängt den Fall ab, dass ein bereits gesetztes Datum entfernt wird (dann verschwindet der Fehler wieder), und load sorgt für den richtigen Zustand direkt beim Öffnen des Mitglieds. Der über form.setError gesetzte Fehler blockiert außerdem das Speichern, bis er über einen der beiden Wege (Haken entfernen oder Datum löschen) aufgelöst ist.

Sicherheit und Grenzen

Scripts laufen in einer abgeschotteten Umgebung (Sandbox):

  • Kein Zugriff auf Dateisystem, Netzwerk, Datenbank oder die Windows-Oberfläche — nur die dokumentierte API ist verfügbar.
  • Laufzeitbegrenzung: Ein Script darf nur wenige hundert Millisekunden laufen (spätestens nach 2 Sekunden hart abgebrochen); Endlosschleifen werden automatisch beendet.
  • Ein Script-Fehler blockiert die Maske nicht — das Formular bleibt bedienbar, der Fehler wird protokolliert.
  • Felder mit Bearbeitungsverhalten ImmerGesperrt können von Scripts nicht freigeschaltet werden.
  • Scripts können nur von Admins im Maskendesigner gepflegt werden und laufen nur in Netxp:Verein Pro.

Tipps & Hinweise

Mit dem Funktionsrumpf starten

Wählen Sie im Baum das Feld-Ereignis und klicken Sie Funktionsrumpf einfügen — die Vorlage zeigt genau das passende Muster für dieses Ereignis. Danach nur noch Feldnamen anpassen.

Testlauf vor dem Speichern

Der Testlauf ▶ zeigt sofort, welche form.*-Aufrufe Ihr Script auslöst — ohne die Maske zu öffnen. Syntaxfehler findet Syntax prüfen mit Zeilenangabe.

Debounce bei Textfeldern

Ohne Debounce läuft ein changed-Script bei jedem Tastendruck. 100–200 ms Debounce machen die Maske spürbar flüssiger — besonders bei Berechnungen.

Häufige Fehler

Script läuft nicht
  • Ist Netxp:Verein Pro aktiv? Ohne Pro werden Scripts nicht ausgeführt.
  • Wurde die Maske gespeichert (nicht nur der Script-Editor mit OK geschlossen)?
  • Hängt das Script am richtigen Ereignis? load läuft nur einmal beim Öffnen, changed nur bei tatsächlicher Wertänderung.
  • Nach Maskenänderungen muss Netxp:Verein ggf. neu gestartet werden (wie bei allen Maskendesign-Änderungen).
Feld wird nicht gefunden

Scripts adressieren den Feldnamen (Tag), nicht die Beschriftung und nicht den Control-Namen. Den Feldnamen sehen Sie im Eigenschafts-Panel des Designers. form.exists("feldname") prüft zur Laufzeit, ob das Feld vorhanden ist.

Vergleich mit Checkbox schlägt fehl

Eine Checkbox liefert über form.getValue(...) einen Wahrheitswert, keinen Text. Prüfen Sie deshalb mit === true (nicht === "true").

Rechnung ergibt seltsame Werte

"10" + "5" ist in JavaScript "105" (Textverkettung!). Immer util.parseNumber(...) verwenden — es versteht auch das deutsche Komma-Format.

Zwei Scripts ändern sich gegenseitig

Wenn Script A Feld B setzt und das changed-Script von B wieder Feld A setzt, unterbindet das System die Endlos-Schleife automatisch — die Ergebnisse können aber unerwartet sein. Besser: Berechnungen in eine Richtung fließen lassen (Eingabefelder → Ergebnisfeld).

Verwandte Themen