Direkt zum Hauptinhalt

Definition einer modellgesteuerten Ansicht

Schema-ID: model-driven-view@2 Maschinenlesbares Schema: model-driven-view.schema.json

Abgelöste Version: model-driven-view@1 (model-driven-view-v1.schema.json) bleibt veröffentlicht und registriert, sodass eine bereits dagegen bereitgestellte Lösung weiterhin gegen das Schema validiert wird, für das sie erstellt wurde. Version 2 ergänzt immer nur optionale Eigenschaften – die Tabelle „Aufbau“ unten ist die aktuelle Liste –, daher ist jede v1-Definition auch eine gültige v2-Definition, und nichts muss neu geschrieben werden.

Die maßgebliche Darstellung einer Ansicht modellgesteuerter Apps (Abschnitt 6.7) – einer tabellarischen Liste von Datensätzen mit ihren Spalten, Filtern und Sortierungen.

Aufbau​

FeldTypPflichtHinweise
logicalNamestringjaMit Herausgeberpräfix, Snake-Case in Kleinbuchstaben.
displayNamestringjaLesbarer Ansichtsname.
entitystringjaDer logische Name der besitzenden Tabelle.
descriptionstringneinDie Zeile, die in der Titelleiste der Ansicht unter dem Titel angezeigt wird.
iconstringneinDas Symbol in der Titelleiste der Ansicht.
viewTypestringneinWofür die Ansicht da ist: Public (Standard), Lookup, QuickFind, Associated, QuickView, Card, SeedData.
seedDataKindstringbei einer SeedData-AnsichtPflicht bei einer SeedData-Ansicht und bei jeder anderen unzulässig: Configuration (Zeilen, die die Lösung zum Funktionieren braucht; ein Import bringt sie immer mit, auch in die Produktion) oder Demonstration (Beispielzeilen; nur importiert, wenn Demodaten einschließen angehakt ist, und niemals in die Produktion). Wird als kind des Seed-Data-Dokuments in das Paket übernommen.
columnsarrayjaLogische Feldnamen, die als Spalten angezeigt werden.
filtersarrayneinFlache Filterklauseln (mit UND verknüpft): field, operator und (bei Operatoren mit Wert) value.
filterGroupobjectneinEin hierarchischer UND/ODER-Filterbaum (im Stil der erweiterten Suche), mit filters per UND verknüpft.
sortsarrayneinSortierklauseln: field, descending.
actionsarrayneinGestaltbare Aktionen der Befehlsleiste.
hierarchyobjectneinVorhanden, wenn die Ansicht als Baum statt als Raster gerendert wird.
calendarobjectneinVorhanden, wenn die Ansicht als Kalender statt als Raster gerendert wird. Nie zusammen mit hierarchy.
columnPropertiesarrayneinWie einzelne Spalten dargestellt werden – Überschrift, Breite, Format, Ausrichtung, Steuerelement. Dünn besetzt.
commandOverridesarrayneinWas diese Ansicht an den Befehlen ändert, die der Plattform gehören.
commandOrderarrayneinEine Leiste, eine Reihenfolge: eingebaute Befehle und Befehle des Erstellers gemischt, per ID.
rowSelectionbooleanneinOb Zeilen ein Auswahlkästchen tragen. Standardmäßig true.

Operatoren: Equals, NotEquals, Contains, GreaterThan, LessThan, IsNull, IsNotNull. Jedes referenzierte Feld (Spalten, Filter, Bedingungen von Filtergruppen, Sortierungen) muss auf der Tabelle existieren.

viewType​

Der Typ einer Ansicht gibt an, welche Oberfläche sie liest. Eine Tabelle kann gleichzeitig eine Standardansicht jedes Typs haben, weil diese unterschiedliche Fragen beantworten:

WertWofür er da ist
PublicDie normale Listenseite der Tabelle. Das ist der Standard und das, was eine Definition ohne viewType ist.
LookupDie Spalten, die im Ergebnisbereich eines Nachschlagesteuerelements angezeigt werden – meist zwei bis vier.
QuickFindDie Suchspalten: die Spalten, gegen die eine eingetippte Suche abgeglichen wird. Ihre columns sind die durchsuchte Menge; ihre Filter und Sortierungen werden nicht gelesen.
SeedDataDie Zeilen und Spalten, die mit der Lösung reisen (PF22). Wird nie angezeigt: Ein Lösungsexport führt sie aus – ihre filters bestimmen die Zeilen, ihre columns die Felder – und packt das Ergebnis als seed-data@1-Dokument, das ein Import per ID einfügt oder aktualisiert (Upsert). Eine Tabelle mit mehreren verwendet ihre Standardansicht, andernfalls die erste nach logischem Namen.
AssociatedDas Unterraster für verwandte Datensätze. Reserviert – noch rendert nichts sie.

Ein Nachschlagesteuerelement, das keine Ansicht nennt, löst automatisch eine auf: die Lookup-Standardansicht der Zieltabelle, andernfalls deren Public-Standardansicht, gekürzt auf ihre ersten drei Spalten, andernfalls allein das Primärfeld der Tabelle. Eine Suche ohne QuickFind-Ansicht gleicht nur das Primärfeld ab. Suchspalten müssen Textfelder sein, da der Abgleich ein „enthält“ ohne Berücksichtigung der Groß-/Kleinschreibung ist.

calendar (eine Ansicht, die als Kalender gerendert wird)​

Eine Ansicht mit einem calendar-Block zeichnet ihre Datensätze auf einem Tages-, Wochen- oder Monatsblatt platziert – anhand zweier ihrer eigenen Datumsfelder – statt als Raster. Fehlt der Block, ist es das Raster, genau wie bei hierarchy, sodass jede gespeicherte Ansicht gültig bleibt. Eine Ansicht mit beiden Blöcken wird abgewiesen: Eine Ansicht wird auf eine einzige Art gezeichnet.

Es ist eine Zuordnung, kein Datenmodell. Es gibt keine Buchungstabelle und keinen Termintyp: Jede Tabelle mit einem Datum lässt sich auf einem Kalender anordnen, und welches ihrer Daten „beginnt“ bedeutet, legt der Ersteller fest.

FeldTypPflichtHinweise
startstringjaDas Datums-/Uhrzeitfeld, an dem ein Block platziert wird. Die einzige Pflichtrolle: Ein Datensatz ohne Beginn lässt sich auf keinem Blatt platzieren, es gibt also keinen Rückfallwert. Ein Systemfeld ist zulässig – created_at_utc ordnet Datensätze nach ihrem Eingang an.
endstringneinDas Datums-/Uhrzeitfeld, an dem ein Block endet. Fehlt es, ist das für eine Tabelle gedacht, die einen Zeitpunkt statt einer Zeitspanne erfasst, und jeder Block ist defaultDurationMinutes lang – nicht null, weil ein Block ohne Höhe nicht angeklickt werden kann.
titlestringneinDas Feld, aus dem die erste Zeile des Blocks gelesen wird. Fehlt es, ist es das Primärfeld der Tabelle – das, was ein Raster, ein Nachschlagebereich und eine Breadcrumb-Leiste bereits anzeigen.
categorystringneinEin Auswahl-, Status- oder Grund-Feld, dessen Optionen die Blöcke einfärben und die Legende füllen.
bookedForstringneinDas Feld, das nennt, für wen der Termin ist, dezent unter dem Titel angezeigt. Beliebiger Typ: Ein Nachschlagefeld wird zum Namen des Zieldatensatzes aufgelöst.
notestringneinDas Feld, das auf einem Block angezeigt wird, der hoch genug für eine dritte Zeile ist, andernfalls in seinem Tooltip.
defaultScalestringneinMit welchem Blatt sich die Ansicht öffnet: Day, Week (Standard) oder Month.
startHourintegerneinDie erste Stunde, die Tages- und Wochenblatt anzeigen, 0–23. Standardmäßig 0.
endHourintegerneinDie Stunde, mit der diese Blätter enden, 1–24, und größer als startHour. Standardmäßig 24.
defaultDurationMinutesintegerneinWie lang ein Termin ist, wenn der Leser auf einen leeren klickt – womit der neue Datensatz ankommt –, und die Höhe jedes Blocks in einer Ansicht, die kein end zuordnet. Mindestens 1; standardmäßig 60.

🔴 start und end müssen Datums-/Uhrzeitfelder sein, und category muss Farben tragen. Beides wird beim Speichern abgewiesen, weil beides auf dem Bildschirm stillschweigend fehlschlägt: Ein start, das eine Textspalte ist, zeichnet ein leeres Blatt, das sich liest wie „diese Woche ist nichts gebucht“ – genau die eine Schlussfolgerung, auf die ein Leser hin handelt und die er nicht überprüfen kann. Die Farbe liegt auf den eigenen Optionen der Auswahl; so lässt sich „Prüfung ist bernsteinfarben“ einmal festlegen und von Kalender, Rasterzelle und Diagramm gleichermaßen lesen.

startHour und endHour machen den Unterschied zwischen einem brauchbaren Buchungsblatt und einem, das sich zu zwei Dritteln leer öffnet: Eine Praxis, die von 8 bis 19 Uhr arbeitet, will keine sechzehn Zeilen Mitternacht. Sie legen fest, wo sich das Blatt öffnet – der Leser wechselt dennoch den Maßstab und schränkt die Stunden über die eigene Kopfzeile des Blatts ein, denn das Zeitfenster gehört ihm, nicht dem Ersteller.

Eine Rolle resource gibt es bewusst nicht. Die Blätter zeigen die Tage nebeneinander; ein Feld, das die Spalte nennt, in die ein Block gehört, wäre eine Zuordnung, die ein Ersteller anlegen und dann dabei zusehen könnte, wie sie nichts bewirkt. Sie kommt mit der Achse, die sie zeichnet.

hierarchy (eine Ansicht, die als Baum gerendert wird)​

Eine Ansicht mit einem hierarchy-Block zeichnet ihre Datensätze über ein Nachschlagefeld verschachtelt – ein Organigramm mit den Personen in jeder Einheit, einen Kategorienbaum, eine Stückliste – statt als Raster. Fehlt der Block, ist es das Raster, das jede Ansicht schon immer war; deshalb bleibt jede gespeicherte Ansicht gültig.

Wofür ein Baum da ist, ist Navigation: Zeilen werden fensterweise abgerufen, sodass sich ein Organigramm mit fünfzigtausend Personen öffnet, ohne irgendetwas davon zu laden.

FeldTypPflichtHinweise
nodeTypesarrayjaJede Art von Knoten, die der Baum zeichnen kann. Mindestens eine.
orderarrayneinDie Reihenfolge, in der Segmente unter einem Knoten gezeichnet werden, per Knotentyp-ID. Fehlt sie, gilt die Deklarationsreihenfolge.
rootPickerbooleanneinOb ein Leser den Baum auf einen Knoten fokussieren darf – etwa mit einer Stadt statt der gesamten Organisation öffnen.
expandRootsbooleanneinOb die erste Ebene beim Laden aufgeklappt wird. Standardmäßig true.

Jeder Eintrag in nodeTypes:

FeldTypPflichtHinweise
idstringjaWie dieser Typ innerhalb des Blocks heißt – unit, person. Hier eindeutig, und worauf sich order und jedes childOf.of beziehen. Dieselbe Tabelle kann in einem Diagramm zweimal vorkommen; deshalb ist das ein eigenes Wort des Erstellers und kein Tabellenname.
entitystringjaDie Tabelle, deren Datensätze diese Knoten sind.
childOfobjectjaWie dieser Typ unter seinem übergeordneten Element hängt – siehe unten.
labelstringneinDas Feld, dessen Wert die Zeile des Knotens bildet. Fehlt es, ist es das Primärfeld der Tabelle.
secondarystringneinDas Feld, das dezent nach der Beschriftung angezeigt wird. Fehlt es, ist es das eigene Sekundärfeld der Tabelle; eine leere Zeichenfolge bedeutet keines – bei einer Einheit steht der Name des übergeordneten Elements direkt in der Zeile darüber, ein Organigramm ist also ein Ort, an dem die Standard-Zweitzeile einer Tabelle falsch ist.
iconstringneinDas Symbol in der Zeile. Fehlt es, ist es das eigene Symbol der Tabelle.
imagebooleanneinOb die Zeile das Bild des Datensatzes trägt.
detailobjectneinWas den Bereich neben dem Diagramm füllt, wenn ein Knoten ausgewählt ist. Fehlt es, ist es das Hauptformular der Tabelle.

childOf nimmt ein field – das Nachschlagefeld auf der Tabelle dieses Typs, das den übergeordneten Datensatz enthält – und ein optionales of, den Knotentyp, unter dem er hängt, per ID.

🔴 Genau ein Knotentyp lässt of weg. Das ist die eigene Tabelle des Baums, die sich unter sich selbst verschachtelt, und ihre Datensätze ohne übergeordnetes Element bilden die oberste Ebene des Diagramms; jeder andere Typ nennt, worunter er hängt. Heterogene Knoten sind der Normalfall und kein Anbau: Einheiten, dann die Personen darin ist derselbe Mechanismus wie Provinz → Stadt → Filiale, modelliert als drei Tabellen.

Das in childOf.field genannte Nachschlagefeld muss auf die Tabelle des übergeordneten Typs zielen – beim Wurzeltyp auf seine eigene. Eines, das anderswohin zeigt, beschreibt keine Verschachtelung und wird beim Speichern abgewiesen, statt eine Ebene zu rendern, unter der nichts steht.

detail nimmt eine kind von Form (das eigene Formular der Tabelle, gespeichert über den Schreibpfad des Datensatzformulars), Card (eine Kartenansicht, die zum Lesen statt zum Eintippen angelegt ist – was man bei einer Person unter einer Einheit meist möchte) oder None (für einen Typ, der nur Struktur ist), dazu optional einen name für das jeweilige Formular bzw. die Kartenansicht.

columnProperties (wie eine Spalte dargestellt wird)​

Eine dünn besetzte Liste: Nur Spalten, die ein Ersteller konfiguriert hat, erscheinen, und eine Spalte ohne Eintrag wird allein aus ihren Feldmetadaten gerendert. Sie steht neben columns, statt daraus eine Liste von Objekten zu machen, sodass jede v1-Definition gültig bleibt und jeder Leser, der nur wissen muss, welche Spalten eine Ansicht hat, unberührt bleibt.

FeldTypPflichtHinweise
fieldstringjaDie Spalte, die hiermit konfiguriert wird. Muss eine der columns der Ansicht sein.
labelstring | nullneinÜberschreibt die Überschrift. Fehlt die Angabe, wird der Anzeigename des Felds verwendet.
widthinteger | nullneinBreite in Pixeln, 40–1200. Fehlt die Angabe, bemisst sich die Spalte selbst.
formatstring | nullneinWerteformat. Für eine DateTime-Spalte derselbe Tokensatz, den die Datumssteuerelemente des Formulars akzeptieren (yyyy, yy, MM, dd, HH, mm, ss) – ein Formatierungsvokabular für das gesamte Produkt.
alignstring | nullneinstart, center oder end. Logisch, nicht links/rechts, sodass es der Leserichtung folgt (§6.13.6).
controlTypestring | nullneinDas Steuerelement, mit dem die Spalte gerendert wird – ein Schaltersymbol für einen booleschen Wert, ein persisches Datum für ein Datum.

controlType nimmt Text, TextArea, Check, Toggle, Date, DateTime, Time, PersianDate, PersianDateTime, PersianTime, Lookup, PolymorphicLookup, Dropdown oder Label. Fehlt die Angabe, gilt der eigene Standard des Felds und danach der des Datentyps.

🔴 Sie wird immer schreibgeschützt gerendert. Ein Raster hat keine Inline-Bearbeitung, ein Schalter zeigt hier also einen Zustand, statt einen Klick entgegenzunehmen. Außerdem ist es die einzige Möglichkeit, eine Spalte in einem gregorianischen Mandanten im Jalali-Kalender zu rendern, da format ein Muster ohne zugeordneten Kalender ist.

commandOverrides (einen Befehl ändern, der der Plattform gehört)​

Was diese Ansicht an den eingebauten Befehlen ändert – Neu, Bearbeiten, Löschen, Aktualisieren, Speichern, Speichern und schließen, Anheften. Fehlt die Angabe, ändert sich nichts; das bedeutet jede Ansicht, die erstellt wurde, bevor es diese Option gab.

FeldTypPflichtHinweise
commandstringjaDie stabile ID des überschriebenen Befehls.
labelstringneinErsetzt die Beschriftung der Plattform. Fehlt die Angabe, bleibt sie erhalten – einschließlich ihrer Übersetzungen, die eine eingetippte Überschreibung zwangsläufig verliert.
iconstringneinErsetzt das Symbol der Plattform.
initiallyHiddenbooleanneinOb der Befehl anfangs ausgeblendet ist.
initiallyDisabledbooleanneinOb der Befehl anfangs deaktiviert ist.

🔴 Verknüpft über eine stabile Befehls-ID, niemals über Beschriftung oder Position. Die ID ist der eigene Name der Plattform und ändert sich nicht, wenn sich die Beschriftung ändert; eine Überschreibung kann sich also nicht von dem lösen, was sie überschreibt, sobald jemand es bearbeitet – und ein eingebauter Befehl kann später umbenannt oder abgeschafft werden, ohne dass jedes gespeicherte Dokument angeglichen werden muss, denn eine Überschreibung, die einen nicht mehr existierenden Befehl nennt, wird einfach ignoriert.

🔴 Ein Ersteller darf ausblenden und deaktivieren, niemals einblenden oder aktivieren. Der Player blendet Neu für jemanden aus, der nichts erstellen darf, und deaktiviert Löschen, wenn nichts ausgewählt ist; eine Überschreibung, die das rückgängig machen könnte, würde eine Leiste bauen, die anbietet, was der Server ablehnen wird.

„Anfangs“ ist das entscheidende Wort. Es ist der Zustand, in dem sich die Leiste öffnet, kein dauerhafter Zustand – eine Geschäftsregel oder das eigene Skript des Befehls kann ihn danach einblenden. Ein Befehl, der für jemanden niemals erscheinen darf, ist nicht das hier; das ist eine Berechtigung, entschieden auf dem Server, wo ein Browser nicht dagegen argumentieren kann.

commandOrder (eine Leiste, eine Reihenfolge)​

Die eingebauten Befehle und die eigenen Befehle des Erstellers, gemischt so, wie der Ersteller sie angeordnet hat, per ID.

Eine explizite Liste statt eines Rangs auf jedem Befehl, weil ein Rang bei jeder Verschiebung auf jedem Befehl neu geschrieben werden muss und zwei Befehle denselben Rang haben können – ein Gleichstand, den der Renderer dann anders auflösen würde, als der Designer ihn aufgelöst hat.

🔴 Alles, was diese Liste nicht erwähnt, behält seinen natürlichen Platz am Ende. Deshalb hält die Regel der Zeit stand: Ein eingebauter Befehl, der der Plattform nächstes Jahr hinzugefügt wird, oder ein Befehl, den ein Upgrade hinzufügt, erscheint, statt zu verschwinden, weil eine gespeicherte Reihenfolge älter ist als er. Eine ID, die etwas nicht mehr Existierendes nennt, wird übersprungen.

rowSelection (ob Zeilen ein Auswahlkästchen tragen)​

Standardmäßig true, und das bedeutet jede Ansicht, die erstellt wurde, bevor es diese Option gab: Das Raster erhält seine Kopfzeile „Alle auswählen“ und Kästchen pro Zeile, auf die die Befehlsleiste wirkt.

false ist für eine Ansicht, die immer nur gelesen wird – eine Liste, aus der niemand löscht oder neu zuweist –, wo ein Auswahlkästchen eine Mehrfachauswahl anbietet, mit der die Oberfläche nichts anfangen kann, und eine Spalte an Breite kostet.

filterGroup (hierarchisches UND/ODER)​

Eine filterGroup ist ein rekursiver Knoten, der seine Elemente über einen connector verknüpft:

FeldTypPflichtHinweise
connectorstringjaAnd (alle Elemente treffen zu) oder Or (irgendein Element trifft zu).
conditionsarrayneinBlattbedingungen (field, operator, value) in dieser Gruppe.
groupsarrayneinVerschachtelte Untergruppen (gleiche Form) für beliebige UND/ODER-Verschachtelung.

Beispiel​

{
"logicalName": "par_invoice_active",
"displayName": "Active Invoices",
"entity": "par_invoice",
"viewType": "Public",
"columns": ["par_name", "par_total"],
"filters": [ { "field": "par_total", "operator": "GreaterThan", "value": 0 } ],
"filterGroup": {
"connector": "And",
"conditions": [ { "field": "par_name", "operator": "Contains", "value": "INV" } ],
"groups": [
{
"connector": "Or",
"conditions": [
{ "field": "par_total", "operator": "GreaterThan", "value": 1000 },
{ "field": "par_total", "operator": "IsNull" }
]
}
]
},
"sorts": [ { "field": "par_name", "descending": false } ]
}

Beispiel – eine Nachschlageansicht​

{
"logicalName": "par_contact_pick",
"displayName": "Contact Lookup",
"entity": "par_contact",
"viewType": "Lookup",
"columns": ["par_name", "par_email", "par_city"],
"sorts": [ { "field": "par_name", "descending": false } ]
}

Beispiel – eine Schnellsuchansicht​

Nur columns wird gelesen: Das sind die Spalten, gegen die eine eingetippte Suche abgeglichen wird.

{
"logicalName": "par_contact_find",
"displayName": "Contact Quick Find",
"entity": "par_contact",
"viewType": "QuickFind",
"columns": ["par_name", "par_email", "par_accountnumber"]
}