Zum Inhalt

Picker — Such-Dialog, der mehrere Felder füllt

Wenn eine Auswahlliste nicht reicht — weil die Menge zu groß ist oder weil mit einer Auswahl gleich mehrere Felder gesetzt werden sollen (Lieferantennummer + Name + Telefon + E-Mail) — kommt kc:picker zum Einsatz. Neben dem Feld erscheint ein …-Knopf; er öffnet einen Such-Dialog mit Trefferliste zum Blättern.

Mehrere Zeilen statt mehrerer Felder

Dieselbe Konfiguration kann auch umgekehrt arbeiten: Ist das Feld vom Typ array, kreuzt man im Dialog mehrere Zeilen an, und alle gewählten Werte landen in diesem einen Feld. Beschrieben unter Mehrere Werte in einem Feld.

Zusammenklicken reicht — das JSON ist zum Nachschauen da

Einen Such-Dialog auf eine Tabelle oder ein LDAP-Verzeichnis richtest du vollständig im Formular-Editor ein, ohne eine Zeile zu tippen: Datenquelle, Tabelle, Filter, Such- und Anzeigespalten, die Übernahme in die Felder und die Sortierung. Nur die beiden freien Quellen — eine eigene sql-Abfrage und der Skript-Picker — werden als JSON gepflegt; das steht weiter unten jeweils dabei.

Die eingeklappten Kästen „Hinter den Kulissen" auf dieser Seite zeigen, was der Editor dabei in das Formular schreibt. Aufklappen lohnt sich, wenn du ein Formular exportieren und mitnehmen willst oder wissen möchtest, warum ein übernommener Wert nicht ankommt — zum Arbeiten brauchst du es nicht.

Wann Picker, wann Lookup?

Situation Variante
5–50 Einträge, ein Wert Lookup (Auswahlliste)
Hunderte oder Tausende Einträge kc:picker
Mehrere Felder auf einmal setzen immer kc:picker
Anwender soll suchen können kc:picker

Lookup und Picker lassen sich am selben Feld kombinieren (Liste plus …-Knopf).

Im Formular-Editor

Eigenschaften-Dialog → Reiter Auswahl → Auswahl-Typ Such-Dialog. Dort klickst du Datenquelle, Tabelle, Filter, Such- und Anzeigespalten, die Übernahme in die Felder (set) und die Sortierung zusammen. Nur der freie sql-Modus und Skript-Picker werden als JSON gepflegt.

Was der Editor dabei hinterlegt

Was du im Reiter „Auswahl" zusammenklickst, notiert der Editor am Feld als Eigenschaft namens kc:picker. Darin steht der ganze Dialog beschrieben: aus welcher Tabelle er liest, in welchen Spalten das Suchfeld sucht, welche Spalten der Anwender in der Trefferliste sieht, welche Formularfelder beim Anklicken einer Zeile gefüllt werden, wonach sortiert wird und wie viele Treffer eine Seite zeigt.

Hinter den Kulissen: vollständiger Such-Dialog auf eine Lieferantentabelle
{
  "lieferantennummer": {
    "type": "string", "title": "Lieferanten-Nr.",
    "kc:picker": {
      "title": "Lieferant suchen",
      "table": "vendor",
      "search":  ["vendor_no", "name"],
      "columns": [
        { "column": "vendor_no", "title": "Nr." },
        { "column": "name",      "title": "Lieferant" },
        { "column": "phone",     "title": "Telefon" }
      ],
      "set": {
        "lieferantennummer":  "vendor_no",
        "lieferantenName":    "name",
        "lieferantenTelefon": "phone",
        "lieferantenMail":    "email"
      },
      "orderBy":  "name ASC",
      "pageSize": 20
    }
  }
}

Klick auf … → Dialog mit Suchfeld und Trefferliste. Ein Klick auf eine Zeile schreibt die Werte in die unter set genannten Felder.

Die Bausteine eines Such-Dialogs

Schlüssel Pflicht Bedeutung
table oder sql oder script oder ldapConnector ja Datenquelle
search ja Spalten, in denen das Suchfeld sucht (Teiltreffer)
columns ja im Dialog angezeigte Spalten — column, optional title
set ja Übernahme: Formularfeld → Quelle
where nein fester Filter (nur mit table)
orderBy nein Sortierung der Trefferliste
pageSize nein Treffer je Seite (Standard 12, höchstens 100)
title nein Titel des Dialogs
dataSourceId nein eine externe Datenquelle statt der KCForms-Datenbank

Worin gesucht wird

Das Suchfeld erzeugt WHERE col1 LIKE ? OR col2 LIKE ? … mit %eingabe%. Ob Groß- und Kleinschreibung eine Rolle spielt, bestimmt die Datenbank (bei MySQL mit der Standard-Sortierung nicht). Ein führendes % nutzt keinen Index — bei sehr großen Tabellen die Suchspalten sparsam wählen.

Was im Dialog zu sehen ist

Reihenfolge im Array = Reihenfolge im Dialog. Ohne title steht der Spaltenname als Überschrift. Spalten in columns müssen nicht in set vorkommen und umgekehrt.

Mehrere Felder auf einmal füllen

Der eigentliche Mehrwert: eine Zuordnung vom Formularfeld zur Quelle. Links steht jeweils das Feld im Formular, rechts die Spalte, aus der sein Wert kommt. Die Zielfelder müssen im Schema existieren — meist als schreibgeschützte Felder, damit niemand den übernommenen Wert von Hand verändert.

Hinter den Kulissen: Übernahme-Zuordnung und die Zielfelder dazu
"set": {
  "lieferantennummer":  "vendor_no",
  "lieferantenName":    "name",
  "lieferantenTelefon": "phone"
}

Und die zugehörigen Zielfelder im Schema:

"lieferantenName":    { "type": "string", "kc:readonly": true },
"lieferantenTelefon": { "type": "string", "kc:readonly": true }

Im Formular-Editor bietet die set-Zeile die vorhandenen Feldnamen zur Auswahl an.

Was übernommen wird: Spalte, Textvorlage oder Datum

Quelle Beispiel Ergebnis
Spalte "FIRST_NAME" Wert der Spalte
Textvorlage "{LAST_NAME}, {FIRST_NAME} ({LOGIN_NAME})" {SPALTE} wird aus der Zeile ersetzt → „Fischer, Horst (hfischer)"
@today "@today" heutiges Datum (JJJJ-MM-TT, passt zu format: date)
@now "@now" Datum + Uhrzeit (JJJJ-MM-TT HH:mm)

So entsteht eine zusammengesetzte Anzeige oder ein Auswahldatum ohne SQL. Im Formular-Editor bietet die set-Zeile dafür ein Menü („Textvorlage {…} zusammensetzen", „Heutiges Datum", „Datum + Uhrzeit"); Spalten-Chips fügen die Platzhalter per Klick ein.

Datenquellen

Eine Tabelle auswählen

KCForms baut die Abfrage selbst: Aus Tabelle, Suchspalten, Anzeigespalten und Sortierung entsteht ein SELECT mit LIKE-Bedingungen und einer seitenweisen Begrenzung — du musst dafür kein SQL schreiben.

Hinter den Kulissen: Tabellen-Konfiguration und das SQL, das daraus wird
{
  "table":   "vendor",
  "search":  ["vendor_no", "name"],
  "columns": [{ "column": "vendor_no" }, { "column": "name" }],
  "set":     { "lieferantennummer": "vendor_no" }
}

ergibt sinngemäß:

SELECT vendor_no, name
FROM vendor
WHERE (vendor_no LIKE ? OR name LIKE ?)
ORDER BY name ASC
LIMIT 12 OFFSET 0

Tabellen- und Spaltennamen müssen gültige SQL-Bezeichner sein.

Einen festen Filter setzen

Mit where schränkst du die Trefferliste dauerhaft ein — etwa auf aktive Benutzerkonten, damit gesperrte gar nicht erst auftauchen.

Hinter den Kulissen: Personensuche, auf aktive Konten begrenzt
{
  "table":  "USERS",
  "where":  "ACC_STATUS = 1",
  "search": ["FIRST_NAME", "LAST_NAME"],
  "columns": [{ "column": "FIRST_NAME" }, { "column": "LAST_NAME" }]
}

Der Filter wird mit der Suche per AND verknüpft. Er kennt keine ${feld}-Platzhalter und wird auf Lese-Anweisungen geprüft (kein DELETE, UPDATE, ;). Für feldabhängige Filter den sql-Modus verwenden.

Eine eigene SQL-Abfrage

Für Joins, CASE oder COALESCE schreibst du die Abfrage selbst — dieser Modus wird als JSON gepflegt, nicht im Editor zusammengeklickt. Alles, was die Abfrage liefert, steht danach als Spalte zur Verfügung, auch berechnete: im Beispiel eine „Preisklasse", die es in der Tabelle gar nicht gibt.

Hinter den Kulissen: Artikelsuche mit berechneter Preisklasse
{
  "sql": "SELECT artikelnr, bezeichnung, preis, CASE WHEN preis > 10 THEN 'hochpreisig' ELSE 'Standard' END AS preisklasse FROM bueromaterial",
  "search":  ["artikelnr", "bezeichnung"],
  "columns": [
    { "column": "artikelnr",   "title": "Nr." },
    { "column": "bezeichnung", "title": "Bezeichnung" },
    { "column": "preisklasse", "title": "Preisklasse" }
  ],
  "set": { "artikel": "artikelnr" }
}

Die Abfrage muss mit SELECT oder WITH beginnen; Schreibbefehle werden abgelehnt. Bei Joins in search die Alias-Schreibweise (b.bezeichnung) nutzen.

Ein LDAP-Verzeichnis

Statt einer Tabelle durchsucht der Dialog ein Verzeichnis: Du wählst den Konnektor, den Ast, ab dem gesucht wird, und den Filter; search und columns nennen dann Verzeichnis-Attribute statt Tabellenspalten.

Hinter den Kulissen: Personensuche über einen LDAP-Konnektor
{
  "title": "Person suchen",
  "ldapConnector": "kcads",
  "ldapBase":   "ou=Users,dc=kcforms,dc=test",
  "ldapFilter": "(objectClass=inetOrgPerson)",
  "search":  ["cn", "mail", "uid"],
  "columns": [
    { "column": "cn",   "title": "Name" },
    { "column": "mail", "title": "E-Mail" }
  ],
  "set": { "person": "uid", "personMail": "mail" }
}

Der Suchbegriff wird als Teiltreffer-Filter über die search-Attribute angehängt. Die Spalte dn liefert den DN des Eintrags. Konnektoren richtest du unter LDAP ein.

Ein Skript

Für Daten aus einem Webservice oder berechnete Listen baust du die Trefferliste per JavaScript zusammen und meldest jede Zeile einzeln an. Auch dieser Modus wird als JSON gepflegt.

Hinter den Kulissen: Trefferliste aus einem Skript
{
  "kc:picker": {
    "title": "Land suchen",
    "search":  ["code", "label"],
    "columns": [
      { "column": "code",  "title": "ISO" },
      { "column": "label", "title": "Land" }
    ],
    "set": { "land": "code", "landName": "label" },
    "script": "db.query('SELECT code, label FROM country').forEach(function (r) { handle({ code: r.code, label: r.label }); });"
  }
}

Im Skript stehen handle({ … }) (eine Zeile hinzufügen), db, log und kc zur Verfügung. Das Skript darf mindestens 30 Sekunden laufen. Bei großen Listen lohnt es sich, schon im Skript zu filtern.

Picker in Detail-Tabellen

In einer Spalte einer Detail-Tabelle funktioniert der Picker genauso. set bezieht sich dann auf Spalten derselben Zeile: Im Beispiel unten sucht der Anwender in der Spalte „Artikel-Nr." einen Artikel — übernommen werden Nummer und Preis, beide in die Zeile, in der er gerade steht.

Hinter den Kulissen: Artikel-Picker in einer Bestellpositions-Tabelle
"positionen": {
  "type": "array",
  "kc:childTable": {
    "properties": {
      "bestellnr": {
        "type": "string", "title": "Artikel-Nr.",
        "kc:picker": {
          "table": "bueromaterial",
          "search":  ["artikelnr", "bezeichnung"],
          "columns": [
            { "column": "artikelnr",   "title": "Nr." },
            { "column": "bezeichnung", "title": "Bezeichnung" },
            { "column": "preis",       "title": "Preis" }
          ],
          "set": { "bestellnr": "artikelnr", "preis": "preis" }
        }
      },
      "menge": { "type": "number", "kc:decimals": 0 },
      "preis": { "type": "number", "kc:decimals": 2, "kc:readonly": true }
    },
    "initialRows": 1
  }
}

Blättern

Standard sind 12 Treffer je Seite, höchstens 100. Der Dialog zeigt die Gesamtzahl der Treffer und lädt weitere Seiten vom Server nach.

Vollständiges Beispiel

Zum Abschluss ein ganzes Bestellformular: Bestellnummer, ein auf das heutige Datum vorbelegtes Bestelldatum und ein Lieferantenfeld mit Such-Dialog. Daneben stehen zwei schreibgeschützte Felder für Name und Telefon, die der Dialog mitfüllt.

Hinter den Kulissen: Bestellformular mit Lieferanten-Suchdialog
{
  "title": "Bestellung",
  "type": "object",
  "kc:layout": "horizontal-2",
  "properties": {
    "bestellnr":    { "type": "string", "title": "Bestell-Nr." },
    "bestelldatum": { "type": "string", "format": "date", "title": "Datum",
                      "kc:defaultExpression": "new Date().format('yyyy-MM-dd')" },
    "lieferantennummer": {
      "type": "string", "title": "Lieferant",
      "kc:picker": {
        "title": "Lieferant suchen",
        "table": "vendor",
        "search": ["vendor_no", "name"],
        "columns": [
          { "column": "vendor_no", "title": "Nr." },
          { "column": "name",      "title": "Name" },
          { "column": "phone",     "title": "Telefon" }
        ],
        "set": {
          "lieferantennummer":  "vendor_no",
          "lieferantenName":    "name",
          "lieferantenTelefon": "phone"
        },
        "orderBy": "name ASC",
        "pageSize": 20
      }
    },
    "lieferantenName":    { "type": "string", "kc:readonly": true, "title": "Name" },
    "lieferantenTelefon": { "type": "string", "kc:readonly": true, "title": "Telefon" }
  },
  "required": ["lieferantennummer"]
}

Klick auf … → Suche → Zeile wählen → drei Felder sind auf einmal gefüllt.

Stolperfallen

  • set-Ziel fehlt im Schema — der Wert wird gesetzt, aber nicht gespeichert. Im Formular-Editor die Feldauswahl der set-Zeile nutzen.
  • Übernommene Spalte nicht in columns — funktioniert, der Anwender sieht den Wert im Dialog aber nicht. Meist ist es besser, alle übernommenen Spalten auch anzuzeigen.
  • Mehrdeutige Spalten — im sql-Modus mit Joins in search den Alias angeben (v.name statt name).
  • Skript-Picker ohne eigene Filterung — liefert immer alles; bei großen Listen im Skript filtern.