JavaScript-Grundlagen¶
Die Skripte in KCForms sind in JavaScript geschrieben. Diese Seite erklärt die Sprache von Anfang an — Variablen, Text, Zahlen, Datum, Entscheidungen, Schleifen, eigene Funktionen und zum Schluss ein wenig Objektorientierung. Immer mit Beispielen, die du sofort ausprobieren kannst.
Vorwissen ist nicht nötig. Wer zuerst wissen will, was ein Objekt ist, liest JSON und Objekte verstehen — die beiden Seiten ergänzen sich.
Zum Mitmachen
Alle Beispiele laufen in der Skript-Konsole: System-Administration → Werkzeuge & Diagnose → Skript-Konsole. Hineinkopieren, Ausführen klicken, unten die Ausgabe lesen.
Wo dein Skript läuft¶
Ein Skript in KCForms läuft auf dem Server, nicht im Browser. Das hat zwei Folgen, die man am Anfang oft übersieht:
- Alles, was man aus Web-Tutorials kennt und was mit der Seite zu tun hat,
gibt es hier nicht: kein
window, keindocument, keinalert(...), keinfetch(...). Meldungen an den Benutzer laufen überview, Aufrufe ins Netz überhttp. - Dafür darf dein Skript Dinge, die ein Browser nie dürfte: Datenbank lesen und schreiben, Mails verschicken, PDFs erzeugen, Dateien anlegen.
Deine erste Zeile ist eine Ausgabe ins Protokoll:
Dazu drei Kleinigkeiten, die überall gelten:
- Kommentare beginnen mit
//und werden nicht ausgeführt. Mehrzeilig:/* … */. - Jede Anweisung endet mit einem Semikolon
;. - Groß- und Kleinschreibung zählt.
Log.Infoist nichtlog.info.
console.log bringt dir hier nichts
console.log(...) funktioniert zwar, landet aber in der Server-Konsole des
Containers — nicht im Skript-Protokoll, das du in der Oberfläche siehst.
Nimm immer log.info(...), log.warn(...) oder log.error(...).
Variablen — Dinge mit Namen¶
Eine Variable ist ein Name für einen Wert. Du vergibst ihn einmal und benutzt ihn danach, so oft du willst:
var vorname = "Anna";
var nachname = "Berg";
var alter = 34;
log.info(vorname + " " + nachname + " ist " + alter); // Anna Berg ist 34
Es gibt drei Schlüsselwörter dafür. Für den Anfang reicht diese Regel:
| Wort | Bedeutung | Wann |
|---|---|---|
var |
der klassische Weg | überall im Handbuch, funktioniert immer |
let |
wie var, aber nur im umgebenden Block gültig |
wenn du es magst |
const |
ein Name, der nicht neu gesetzt wird | für feste Werte |
const MWST_SATZ = 19; // ändert sich nie
let zwischensumme = 100; // ändert sich gleich
zwischensumme = 200;
log.info(MWST_SATZ);
Das Handbuch benutzt durchgängig var, damit alle Beispiele gleich aussehen.
Beides ist richtig.
Namen sprechen lassen
var b = 3; weiß morgen niemand mehr zu deuten, var bestellmenge = 3;
schon. Erlaubt sind Buchstaben, Ziffern und _; beginnen darf ein Name
nicht mit einer Ziffer. Üblich ist kleinMitGrossenWortanfaengen.
Die Datentypen¶
Jeder Wert hat eine Art. Es sind nur wenige, und du kennst die meisten schon aus dem Formular:
| Art | Fachwort | Beispiel |
|---|---|---|
| Text | String | "Anna Berg" |
| Zahl | Number | 42, 3.5, -7 |
| Ja/Nein | Boolean | true, false |
| Datum | Date | new Date() |
| Liste | Array | ["a", "b"] |
| Karteikarte | Object | { name: "Anna" } |
| leer / gibt's nicht | null / undefined |
— |
typeof verrät dir im Zweifel, was du vor dir hast:
log.info(typeof "Anna"); // string
log.info(typeof 42); // number
log.info(typeof true); // boolean
log.info(typeof {}); // object
Text (String)¶
Text steht in Anführungszeichen — doppelte oder einfache, beide gehen. Mit +
klebst du Texte aneinander:
Kürzer geht es mit Backticks ` und ${…} — der Wert wird direkt in
den Text hineingeschrieben:
Die wichtigsten Handgriffe für Text:
| Handgriff | Was er tut | Ergebnis bei " Anna Berg " |
|---|---|---|
.length |
Anzahl Zeichen | 11 |
.trim() |
Leerzeichen vorn und hinten weg | "Anna Berg" |
.toUpperCase() / .toLowerCase() |
Groß / klein | " ANNA BERG " |
.includes("Berg") |
kommt das vor? | true |
.indexOf("Berg") |
ab welchem Zeichen? (-1 = gar nicht) |
6 |
.substring(1, 5) |
Ausschnitt von–bis | "Anna" |
.replace("Anna", "Lena") |
ersetzen (nur das erste Vorkommen) | " Lena Berg " |
.split(" ") |
in eine Liste zerlegen | ["", "Anna", "Berg", ""] |
.padStart(5, "0") |
vorn auffüllen | für Belegnummern |
var roh = ctx.form.name; // " anna berg "
var sauber = roh.trim(); // "anna berg"
log.info(sauber.toUpperCase()); // ANNA BERG
log.info(sauber.split(" ")[0]); // anna ← das erste Wort
log.info(String(7).padStart(4, "0")); // 0007
log.info("Länge: " + sauber.length); // Länge: 9
Text und Zahl ineinander umwandeln:
var text = "42";
log.info(Number(text) + 1); // 43 — Text zu Zahl
log.info(String(42) + "1"); // 421 — Zahl zu Text
Für Werte aus Formularfeldern nimm statt Number(...) besser
kc.num(...): Das versteht
auch "1.234,56" und liefert bei leerem Feld 0 statt NaN.
Zahlen (Number)¶
Gerechnet wird mit + - * /, dazu % für den Rest einer Division:
| Handgriff | Was er tut |
|---|---|
(1.005).toFixed(2) |
auf 2 Stellen runden, ergibt Text |
Math.round(x) |
kaufmännisch runden |
Math.floor(x) / Math.ceil(x) |
ab- / aufrunden |
Math.abs(x) |
Betrag (Vorzeichen weg) |
Math.max(a, b) / Math.min(a, b) |
größter / kleinster Wert |
(1234.5).toLocaleString("de-DE") |
deutsche Schreibweise: 1.234,5 |
var summe = kc.num(ctx.form.menge) * kc.num(ctx.form.preis);
log.info(summe.toFixed(2)); // 37.50
log.info(Math.round(summe * 100) / 100); // 37.5 — auf Cent gerundet
log.info(summe.toLocaleString("de-DE") + " €"); // 37,5 €
Krumme Kommazahlen
0.1 + 0.2 ergibt nicht 0.3, sondern 0.30000000000000004. So rechnen
Computer mit Kommazahlen — das ist kein Fehler von KCForms. Runde deshalb
am Ende jeder Rechnung auf Cent: Math.round(x * 100) / 100.
Vergleichen und entscheiden¶
Ein Vergleich liefert true oder false:
| Zeichen | Bedeutung |
|---|---|
=== |
gleich (Wert und Art) |
!== |
ungleich |
< > <= >= |
kleiner, größer … |
&& |
und |
|| |
oder |
! |
nicht |
var betrag = kc.num(ctx.form.betrag);
var ausland = ctx.form.land !== "DE";
if (betrag > 1000 && ausland) {
log.info("Freigabe nötig");
} else if (betrag > 1000) {
log.info("Bitte kurz prüfen");
} else {
log.info("Alles in Ordnung");
}
Zwei oder drei Gleichheitszeichen?
= setzt einen Wert, === vergleicht. Es gibt auch ==, das
großzügig vergleicht und dabei heimlich umwandelt: "0" == 0 ist true,
"" == 0 ebenfalls. Nimm immer ===, dann gibt es keine Überraschungen.
Bei vielen festen Werten ist switch übersichtlicher:
var mwst;
switch (ctx.form.land) {
case "DE": mwst = 19; break;
case "AT": mwst = 20; break;
case "CH": mwst = 8.1; break;
default: mwst = 0;
}
log.info(mwst + " %");
Für einen einzigen Wert gibt es eine Kurzform — Bedingung ? dann : sonst:
Was gilt als „leer"?¶
if (wert) ist false bei "", 0, null, undefined und false. Das ist
tückisch: Eine eingetragene Null verhält sich damit wie ein leeres Feld.
Für Formularfelder nimm deshalb
kc.isEmpty(...) — das ist nur bei „nichts
eingegeben" wahr:
if (kc.isEmpty(ctx.form.menge)) {
log.info("Menge fehlt");
} else {
log.info("Menge ist ausgefüllt: " + ctx.form.menge);
}
Datum (Date)¶
new Date() ist der aktuelle Zeitpunkt. KCForms legt zwei Erweiterungen dazu,
die es in normalem JavaScript nicht gibt: format(...) und Date.fromString(...).
var jetzt = new Date();
log.info(jetzt.format("dd.MM.yyyy HH:mm")); // 22.09.2026 14:05
var tag = new Date(2026, 8, 22); // Achtung: Monat 8 = September!
log.info(tag.format("yyyy-MM-dd")); // 2026-09-22
log.info(tag.format("EEEE, d. MMMM yyyy", "de_DE")); // Dienstag, 22. September 2026
| Muster | Bedeutung |
|---|---|
dd / MM / yyyy |
Tag, Monat, Jahr — zweistellig bzw. vierstellig |
HH / mm / ss |
Stunde, Minute, Sekunde |
EEEE / MMMM |
Wochentag, Monatsname ausgeschrieben |
Mit Datumswerten lässt sich rechnen. Zieht man zwei voneinander ab, kommen Millisekunden heraus:
var termin = Date.fromString("24.12.2026", "dd.MM.yyyy");
var heute = new Date();
var tage = Math.ceil((termin - heute) / (1000 * 60 * 60 * 24));
log.info(tage + " Tage bis Heiligabend");
So kommt ein Datum ins Formularfeld¶
Ein Datumsfeld erwartet den Text yyyy-MM-dd. Schreibst du ein
Date-Objekt hinein, landet englischer Fließtext im Feld:
log.info(String(new Date(2026, 8, 22)));
// Tue Sep 22 2026 00:00:00 GMT+0000 (GMT) ← so nicht ins Feld!
ctx.form.datum = new Date(2026, 8, 22).format("yyyy-MM-dd");
log.info(ctx.form.datum); // 2026-09-22 ← richtig
Listen und Schleifen¶
Eine Liste hält mehrere Werte in fester Reihenfolge, eine Schleife geht sie der Reihe nach durch:
var artikel = ["Stift", "Block", "Radiergummi"];
for (var i = 0; i < artikel.length; i++) {
log.info((i + 1) + ". " + artikel[i]);
}
Kürzer mit forEach, und mit for … of liest es sich fast wie ein Satz:
var artikel = ["Stift", "Block", "Radiergummi"];
artikel.forEach(function (name) { log.info(name); });
for (var name of artikel) { log.info(name); }
while wiederholt, solange eine Bedingung gilt — und braucht immer etwas, das
sie irgendwann beendet:
Sechs Sekunden, dann ist Schluss
Ein Skript darf höchstens sechs Sekunden laufen; danach bricht KCForms ab. Das schützt den Server vor Endlosschleifen. Große Datenmengen gehören deshalb in einen Zeitplaner-Job, nicht in einen Formular-Hook.
Alles Weitere zu Listen — filtern, umbauen, summieren, sortieren — steht im Kapitel JSON und Objekte verstehen.
Eigene Funktionen¶
Eine Funktion ist ein Arbeitsschritt mit einem Namen. Du schreibst ihn einmal auf und rufst ihn beliebig oft:
function brutto(netto, steuersatz) {
return netto * (1 + steuersatz / 100);
}
log.info(brutto(100, 19).toFixed(2)); // 119.00
log.info(brutto(50, 7).toFixed(2)); // 53.50
- In den Klammern stehen die Parameter — das, was die Funktion braucht.
returngibt das Ergebnis zurück und beendet die Funktion sofort.- Ohne
returnliefert sieundefined.
Funktionen sind selbst Werte und dürfen anderen Funktionen übergeben werden —
genau das passiert bei forEach, filter oder sort:
var zahlen = [1, 2, 3];
var summe = 0;
zahlen.forEach(function (z) { summe += z; });
log.info(summe); // 6
Dieselbe Funktion geht kürzer als Pfeilfunktion:
var zahlen = [1, 2, 3];
log.info(zahlen.map(function (z) { return z * 2; }).join("-")); // 2-4-6
log.info(zahlen.map(z => z * 2).join("-")); // dasselbe, kürzer
Was drinnen bleibt, bleibt drinnen
Eine Variable, die innerhalb einer Funktion mit var angelegt wird,
gibt es außerhalb nicht (Fachwort: Gültigkeitsbereich, engl. scope).
Das ist gewollt: So stören sich zwei Funktionen nicht gegenseitig.
Wenn etwas schiefgeht¶
Ein Fehler bricht das ganze Skript ab — es sei denn, du fängst ihn auf:
try {
var zeile = db.queryOne("SELECT * FROM gibt_es_nicht");
log.info(zeile.name);
} catch (e) {
log.warn("Das ging schief: " + e.message);
} finally {
log.info("Dieser Teil läuft immer — auch nach einem Fehler.");
}
try— das, was schiefgehen kann.catch (e)— was dann passieren soll;e.messageist der Fehlertext.finally— läuft in jedem Fall, ideal zum Aufräumen.
Du kannst auch selbst abbrechen. Im Formular gehört eine Meldung an den Benutzer aber an das Feld, nicht in einen Fehler:
if (kc.num(ctx.form.menge) > 100) {
ctx.addError("menge", "Mehr als 100 Stück bitte telefonisch bestellen.");
}
Ein wenig Objektorientierung¶
Bisher waren Daten und Arbeitsschritte getrennt: hier eine Variable, dort eine
Funktion. Objektorientierung heißt schlicht: Beides zusammen in eine Karte
packen. Eine Funktion, die in einem Objekt steckt, nennt man Methode, und
this meint darin „diese Karte hier":
var kunde = {
vorname: "Anna",
nachname: "Berg",
vollerName: function () {
return this.vorname + " " + this.nachname;
}
};
log.info(kunde.vollerName()); // Anna Berg
Brauchst du viele gleichartige Objekte, schreibst du einen Bauplan (eine
Klasse) und stellst damit beliebig viele Stück her. new bedeutet
„fertige mir ein neues Exemplar nach diesem Bauplan":
class Position {
constructor(artikel, menge, preis) {
this.artikel = artikel;
this.menge = menge;
this.preis = preis;
}
summe() {
return this.menge * this.preis;
}
}
var p = new Position("Stift", 3, 1.20);
log.info("Position " + p.artikel + ": " + p.summe().toFixed(2)); // 3.60
Der constructor läuft beim Anlegen und füllt die Karte. Mehr als das brauchst
du in KCForms selten — tiefere Themen wie Vererbung kommen im Formular-Alltag
praktisch nicht vor.
Du benutzt das längst. Genau so sind die mitgelieferten Werkzeuge gebaut:
var termin = new Date(); // ein Date-Objekt
var msg = new mail.Message(); // ein Mail-Objekt nach Bauplan
msg.to = "team@example.test";
msg.subject = "Bericht vom " + termin.format("dd.MM.yyyy");
log.info("Vorbereitet für " + msg.to + ": " + msg.subject);
termin.format(...) und msg.to sind nichts anderes als Methode und
Eigenschaft eines Objekts — dasselbe Prinzip wie oben.
Willst du eigene Funktionen in mehreren Formularen nutzen, packst du sie in eine Skript-Bibliothek. Dort ist das Muster „ein Objekt mit Funktionen darin" der Normalfall:
var Rechner = {
MWST: 19,
brutto: function (netto) {
return netto * (1 + this.MWST / 100);
}
};
log.info(Rechner.brutto(100).toFixed(2)); // 119.00
Wie so eine Sammlung systemweit verfügbar wird, steht unter Bibliotheken.
Die Werkzeuge, die KCForms mitbringt¶
Diese Objekte stehen in jedem Skript bereit — du legst sie nicht selbst an:
| Objekt | Wofür | Erste Zeile |
|---|---|---|
ctx |
der aktuelle Datensatz und Benutzer | ctx.form.name |
log |
ins Protokoll schreiben | log.info("...") |
kc |
Zahlen und leere Felder | kc.num(ctx.form.betrag) |
db |
Datenbank abfragen und ändern | db.query("SELECT ...") |
forms |
Datensätze anderer Formulare | forms.list("bestellung") |
view |
Meldungen und Rückfragen | view.add(new view.Message("Hallo")) |
mail |
Mails verschicken | new mail.Message() |
http |
Schnittstellen aufrufen | http.get(url) |
pdf, docx, csv, zip, fs |
Dokumente und Dateien | pdf.render(...) |
Die vollständige Liste steht im Scripting-Überblick.
Stolpersteine für den Anfang¶
| Stolperstein | So ist es richtig |
|---|---|
= statt === in einer Bedingung |
= setzt, === vergleicht |
"3" + "4" ergibt "34" |
Formularwerte mit kc.num(...) in Zahlen wandeln |
[10, 9, 100].sort() ergibt 10, 100, 9 |
sort vergleicht als Text; Zahlen mit sort(function (a, b) { return a - b; }) |
| Ein Date-Objekt im Datumsfeld | .format("yyyy-MM-dd") verwenden |
console.log zeigt nichts |
log.info(...) verwenden |
if (wert) ist bei einer 0 falsch |
für Felder kc.isEmpty(...) nehmen |
log.info(objekt) zeigt [object Object] |
log.info(JSON.stringify(objekt)) — mehr dazu |
| Skript bricht nach 6 Sekunden ab | Große Mengen als Job verarbeiten |
Betrag ist nicht betrag |
Groß- und Kleinschreibung zählt überall |
var zahlen = [10, 9, 100];
log.info(zahlen.slice().sort().join(",")); // 10,100,9 ← als Text sortiert
log.info(zahlen.slice().sort(function (a, b) { return a - b; }).join(",")); // 9,10,100
Spickzettel¶
| Ich will … | So geht's |
|---|---|
| etwas ausgeben | log.info("Text") |
| einen Namen vergeben | var x = 5; |
| Texte verbinden | "a" + "b" oder `${a} ${b}` |
| Text zu Zahl | kc.num(ctx.form.feld) |
| auf zwei Stellen runden | zahl.toFixed(2) |
| auf Cent runden | Math.round(x * 100) / 100 |
| entscheiden | if (…) { … } else { … } |
| kurz entscheiden | bedingung ? dann : sonst |
| heutiges Datum formatiert | new Date().format("dd.MM.yyyy") |
| Datum in ein Feld schreiben | new Date().format("yyyy-MM-dd") |
| Liste durchgehen | liste.forEach(function (x) { … }) |
| eigene Funktion | function name(a, b) { return …; } |
| Fehler abfangen | try { … } catch (e) { log.warn(e.message); } |
| Objekt mit Methode | { wert: 1, tuWas: function () { return this.wert; } } |
| neues Exemplar erzeugen | new Date(), new mail.Message() |
Weiter¶
- JSON und Objekte verstehen — Objekte, Listen und wie man Werte darin ändert
- XML verstehen und verarbeiten — XML lesen, durchsuchen und bauen
- Scripting-Überblick — Hooks, Host-Objekte, Sandbox
- Erste Schritte in Beispielen — 22 kleine Skripte zum Ausprobieren
kc,log, Datum — die Helfer im Detail- Bibliotheken — eigene Funktionen mehrfach nutzen