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.
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.
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:
- Aus dem Maskendesigner: Maske öffnen, in der Aktionsleiste unten auf Scripts klicken.
- 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äche | Funktion |
|---|---|
| Funktionsrumpf einfügen | Fügt für das gewählte Feld × Ereignis eine kommentierte Code-Vorlage ein — Sie starten nie mit einer leeren Seite. |
| Snippets | Bibliothek der häufigsten Muster (Sichtbarkeit, Summe, Wertebereich, Pflichtfeld, Farbe, Datenkontext, Alter, Datumsprüfung). Platzhalter wie <feld> ersetzen Sie nach dem Einfügen. |
| Syntax prüfen | Prü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.
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.
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).
| Ereignis | Wann läuft es? | Skopus | Typische Verwendung |
|---|---|---|---|
load | Formular ist aufgebaut, Daten sind gebunden | Formular | Anfangszustand: Sichtbarkeit, Startberechnungen |
changed | Der Wert eines Feldes hat sich geändert | Feld | Folgefelder steuern, Summen berechnen |
click | Feld/Schaltfläche wurde angeklickt | Feld | Aktionen auslösen |
validate | Vor dem Speichern sowie beim Verlassen des Feldes | Feld/Formular | Fehler setzen/entfernen mit form.setError |
beforeSubmit | Unmittelbar vor dem Speichern | Formular | Finale Berechnungen und Prüfungen |
afterSubmit | Nach erfolgreichem Speichern | Formular | Aufrä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 zusammenWas 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
| Funktion | Wirkung |
|---|---|
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.
Verwenden Sie bevorzugt die Tokens ("warning" statt "#FFEB9C") — sie
werden zentral gepflegt und funktionieren später auch im Web-Design.
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):
| Quelle | Inhalt |
|---|---|
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.getFü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
| Funktion | Wirkung |
|---|---|
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 |
util.parseNumberform.getValue liefert bei Textfeldern eine Zeichenkette. "10" + "5"
ergibt in JavaScript "105" (Textverkettung)! Rechnen Sie deshalb immer mit
util.parseNumber(...).
> vergleichenform.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
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.
Der Testlauf ▶ zeigt sofort, welche form.*-Aufrufe Ihr Script
auslöst — ohne die Maske zu öffnen. Syntaxfehler findet Syntax prüfen
mit Zeilenangabe.
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
- 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?
loadläuft nur einmal beim Öffnen,changednur bei tatsächlicher Wertänderung. - Nach Maskenänderungen muss Netxp:Verein ggf. neu gestartet werden (wie bei allen Maskendesign-Änderungen).
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.
Eine Checkbox liefert über form.getValue(...) einen Wahrheitswert, keinen
Text. Prüfen Sie deshalb mit === true (nicht === "true").
"10" + "5" ist in JavaScript "105" (Textverkettung!). Immer
util.parseNumber(...) verwenden — es versteht auch das deutsche
Komma-Format.
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).