Zum Inhalt springen

Module (NXM)

Ansichten und das View-SDK

Wie ein NXM-Modul seine Datensätze zeigt. Deklarative Ansichten, die nexxus für Sie zeichnet, eigene HTML-Ansichten in einer Sandbox und das View-SDK, das beide mit der Plattform verbindet.

Für
Entwickler
Dauer
20 Min.

Ein NXM-Modul hat zwei Wege, etwas auf den Bildschirm zu bringen. Die meisten Seiten brauchen gar keinen Code: Sie beschreiben eine Liste oder eine Datensatzseite in der module.json, und nexxus zeichnet sie mit denselben Bausteinen wie seine eingebauten Module. Braucht eine Seite ein eigenes Layout, etwa einen Kalender, ein Cockpit aus mehreren Blöcken oder ein PDF mit Notizen daneben, schreiben Sie eine HTML-Datei. nexxus führt diese Datei in einem abgeschotteten Rahmen aus und gibt ihr eine kleine JavaScript-Schnittstelle mit, das View-SDK (window.nx).

Diese Seite behandelt beides. Fangen Sie mit den deklarativen Ansichten an und greifen Sie erst dort zu einer eigenen Ansicht, wo diese nicht mehr reichen.

Deklarative AnsichtenEigene Ansichten
Geschrieben alsJSON in der module.json (type: list, record, related)Eine HTML-Datei je Ansicht (type: "custom", html: "ui/<name>.html")
Gezeichnet vonnexxus selbstIhrem HTML, in einem abgeschotteten Rahmen
Sieht aus wieder Rest von nexxus, ganz von selbstnexxus, wenn Sie das UI-Kit und die Farb-Tokens verwenden
Datenzugriffeingebaut: Listen, Filter, Formulare, Bezugslistenüber window.nx, als die Person, die die Ansicht betrachtet
Gut fürListen, Datensatzformulare, Listen verknüpfter DatensätzeSeiten, Cockpits, Kalender, Dialoge, PDF-Prüfung

Beide Arten handeln mit den Rechten der Person auf der Seite. Eine eigene Ansicht bekommt nie mehr Zugriff als die Person, die sie geöffnet hat, und der Server prüft jeden ihrer Aufrufe noch einmal.

  1. Ihre Ansicht

    Eine HTML-Datei mit eingebettetem Skript und Stil, in einem abgeschotteten Rahmen.

  2. Das View-SDK

    window.nx schickt jede Anfrage über einen eigenen Nachrichtenkanal.

  3. Der Host in nexxus

    Ergänzt Modul und Sitzung, zeigt Meldungen, Dialoge und den PDF-Viewer.

  4. Die Plattform-API

    Prüft die Rechte der betrachtenden Person und antwortet.

Eine eigene Ansicht sieht nie eine Sitzung, eine Mandanten-ID oder eine Benutzer-ID. Der Host ergänzt sie, und der Server prüft jede Anfrage.

Diese vier Typen brauchen kein HTML. nexxus baut daraus Listen, Formulare, Reiter und Karten.

  • list: die Listenseite eines Fachobjekts unter /x/<modul>/<fachobjekt>. Sie wählen die columns (einen Feldschlüssel oder { "field": "status", "display": "badge" }), die filters und eine Standardsortierung sort; "create": true zeigt die Schaltfläche für einen neuen Datensatz. Filtern und sortieren lässt sich nur nach indizierten Feldern. Fehlt eine Listenansicht, baut nexxus sie aus dem titleField und den ersten vier indizierten Feldern. Die Liste zeigt jeden Datensatz als Zeile im Listenstil von nexxus, und jede Spalte bleibt sichtbar: Das titleField wird die Überschrift, Verweise und Textfelder stehen darunter, die erste Person (core:user) mit ihrer Bezeichnung daneben. Auswahlfelder, Daten und Zahlen stehen rechts, der erste Betrag hervorgehoben, und die Badge-Spalte status (sonst die erste Badge-Spalte) als Chip. Unter Ansichtsoptionen wählt jede Person die Dichte Normal oder Kompakt und speichert eigene Ansichten.
  • record: der Reiter Daten eines Datensatzes. sections gliedern die Felder, related fügt Listen der Datensätze hinzu, die auf diesen verweisen, und actions legt fest, welche Aktionen die Aktionsleiste anbietet.
  • related: ein Reiter auf der Seite eines anderen Fachobjekts, der die Datensätze auflistet, die darauf verweisen. Bei Kerndaten ist das die Seite eines Partners, Projekts, einer Rechnung oder einer Opportunity ("target": "core:partner", "core:project", "core:invoice" oder "core:opportunity", Slot record.tab).
  • map: die Datensätze eines Fachobjekts auf einer Karte, zusammen mit ihrer Liste, als Seite des Moduls unter /x/<modul>/p/<ansicht>. geoField nennt das geo-Feld, das einen Datensatz auf die Karte setzt.

Die vollständige Feldreferenz dieser Ansichten steht unter Paketaufbau.

Die Datensatzseite eines Paket-Fachobjekts zeigt ihre Reiter in dieser Reihenfolge:

  1. alle Ansichten mit Slot record.detail (eigene Ansichten zu diesem Datensatz),
  2. den Reiter Daten (die record-Ansicht oder, falls es keine gibt, alle Felder),
  3. alle Ansichten des Moduls mit Slot record.tab,
  4. die Bezugslisten.

Dort erscheinen nur Ansichten des eigenen Pakets: Pakete bauen nicht aufeinander auf, also bringt kein anderes Paket Reiter oder Dialoge auf die Seite. Aktionen stehen in der Aktionsleiste unten im Reiter.

Datensatzseite eines Pakets mit einer eigenen Ansicht als erstem Reiter
Zuerst die eigenen Ansichten, dann „Daten“, dann die Reiter des Moduls, dann die Bezugslisten.

Eine eigene Ansicht nennt einen slot. Er bestimmt, wo nexxus sie zeigt und welchen Kontext sie bekommt.

SlotIm Katalog angezeigt alsWo sie erscheinttarget
pageSeiteEine eigene Seite unter /x/<modul>/p/<ansicht>. Sie füllt den Bereich und kann ein Menüeintrag sein.keins
record.detailAnsicht am DatensatzDie ersten Reiter einer Datensatzseiteein Fachobjekt des Pakets
record.tabReiter am DatensatzEin Reiter nach Daten; mit einem Kernziel ein Reiter auf der Partner-, Projekt-, Rechnungs- oder Opportunity-Seiteein Fachobjekt oder core:partner, core:project, core:invoice, core:opportunity
record.actionDialog am DatensatzEin Dialog, den die Aktionsleiste des Datensatzes öffnetein Fachobjekt
dashboard.widgetKachel auf dem DashboardEine Kachel mit size s, m oder lkeins
memberSeite im KundenbereichEine Seite des Kundenbereichs auf der Portal-Domain, unter /konto/<ansicht>, fensterfüllendkeins

Über einer Seite und einer Ansicht am Datensatz zeigt nexxus den Titel: auf einer Seite die Beschriftung der Ansicht, auf einem Datensatz dessen Titel. Die Ansicht wiederholt ihn nicht und beginnt mit dem, was sie ergänzt, etwa einem Zeitraum, einer Anzahl oder Filtern. Auch Kachel und Dialog bekommen ihre Titelleiste von nexxus.

Auf die Startseite kommt eine Kachel auf zwei Wegen: Eine Person legt sie mit Anpassen und Widget hinzufügen an, wo Beschriftung, Symbol und Modulname der Ansicht stehen, oder sie wendet eine Vorlage des Pakets an (siehe Startseiten-Vorlagen). Von selbst erscheint keine. Ist das Modul deaktiviert oder entfernt, zeigt eine platzierte Kachel Modul nicht verfügbar und kommt mit dem Modul zurück.

{ "key": "faellig", "type": "custom", "slot": "page", "html": "ui/faellig.html",
"label": { "de": "Fällige Rückgaben", "en": "Due returns" } }

Ansichten und Aktionen nehmen eine optionale Bedingung when. nexxus blendet die Ansicht aus, wenn die Bedingung für den aktuellen Datensatz nicht gilt, und der Server prüft sie vor jeder Aktion erneut.

"when": { "field": "status", "in": ["verfuegbar"] }
"when": { "field": "dokument", "set": true }

in gilt, wenn der Wert des Felds einer der genannten ist (bei einem Listenfeld, wenn es irgendein Element ist). set: true gilt, wenn das Feld einen Wert hat, set: false, wenn es leer ist.

Jede eigene Ansicht läuft in einem <iframe> mit sandbox="allow-scripts allow-forms" ohne eigenen Ursprung und unter derselben Inhaltsrichtlinie wie Apps:

default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline';
img-src data: blob:; font-src data:; media-src data: blob:; form-action 'none'; base-uri 'none'

Die Sandbox gibt es, weil der Code eines Pakets nicht von nexxus stammt. Was die Ansicht auch tut, sie erreicht kein Netzwerk, liest keine Sitzung und berührt nicht die Seite um sie herum. Im Alltag heißt das:

  • Alles eingebettet. Skripte, Stile, Schriften und Bilder stehen in der Datei oder als data:- bzw. blob:-URL. fetch, XHR, WebSockets und <script src> aus dem Netz sind gesperrt.
  • Keine Cookies, kein localStorage. Der Rahmen hat keinen Ursprung. Was eine Person wiederfinden soll, legen Sie mit nx.storage ab.
  • Formulare ja, Absenden nein. Ein <form> löst sein submit-Ereignis aus, aber form-action 'none' sperrt alles, was die Seite verlassen würde. Rufen Sie preventDefault() auf und verarbeiten Sie die Daten selbst.
  • Keine Pop-ups, keine Downloads.
  • Keine Symbolschrift. Die Schrift Material Symbols von nexxus steht im Rahmen nicht zur Verfügung. Verwenden Sie eingebettetes SVG.
  • Text als Text. Datensätze enthalten, was Menschen eingetippt haben. Setzen Sie solche Werte mit textContent ein, nie mit innerHTML.

SDK und UI-Kit werden vor Ihrem eigenen Code eingefügt, window.nx steht also bereit, wenn Ihr erstes Skript läuft. Eine HTML-Datei darf bis zu 2 MB groß sein.

Diese Seite listet die verliehenen Geräte aus dem Tutorial zum ersten Modul:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>body { padding: 16px; }</style>
</head>
<body>
<div class="nx-list" id="list"></div>
<script>
(async () => {
const ctx = await nx.ready();
const {items} = await nx.records.search('geraet', {
filter: {status: ['verliehen']}, sort: 'rueckgabe_am', size: 50,
});
for (const geraet of items) {
const item = document.createElement('button');
item.className = 'nx-list__item';
item.textContent = geraet.title;
item.onclick = () => nx.nav.open({entity: 'geraet', id: geraet.id});
document.getElementById('list').append(item);
}
})();
</script>
</body>
</html>

await nx.ready() kehrt zurück, sobald der Host verbunden ist, und liefert den Kontext der Ansicht:

FeldBedeutung
sdkProtokollversion, derzeit 1
module, version, view, slotWo die Ansicht läuft
localede, en oder pl, die Sprache der Person
themelight oder dark
user{id, name} der Person, die die Ansicht betrachtet
record{entity, id} auf der Datensatzseite eines Fachobjekts aus dem Paket der Ansicht, z. B. {entity: 'akte', id: 12}
target{type, id} auf einem Kerndatensatz, z. B. {type: 'core:project', id: 7}
paramsWas der Aufrufer mitgegeben hat

nx.on(name, fn) meldet einen Empfänger für ein Ereignis an und gibt eine Funktion zurück, die ihn wieder abmeldet.

  • context: Der Kontext hat sich geändert (ein anderer Datensatz, eine andere Sprache). Das Argument ist der neue Kontext.
  • theme: Hell oder dunkel hat gewechselt, als {theme, tokens}. Das SDK hat den Wechsel schon übernommen. Hören Sie nur zu, wenn Sie selbst zeichnen, etwa auf einem Canvas oder in einem Diagramm.
  • record: nexxus hat den Datensatz, um den es in Ihrer Ansicht geht, außerhalb der Ansicht geändert, als {entity, id, version}. Das passiert, nachdem eine Aktion darauf lief, ein Dialog ihn geschrieben hat, der Reiter Daten gespeichert wurde oder eine andere Ansicht ihn geändert hat. Bei Kerndatensätzen ist version null. Ihre eigenen Schreibvorgänge kommen nicht als Ereignis zurück.

Das Ereignis sagt, dass sich etwas geändert hat, nicht was. Lesen Sie den Datensatz neu:

const ctx = await nx.ready();
let shown = await nx.records.get(ctx.record.entity, ctx.record.id);
render(shown);
nx.on('record', async ({version}) => {
if (version !== null && version === shown.version) return;
shown = await nx.records.get(ctx.record.entity, ctx.record.id);
render(shown);
});

Änderungen, die nexxus nicht in diesem Browser-Tab vorgenommen hat, etwa von einer anderen Person oder von einem Moduldienst im Hintergrund, werden nicht gemeldet.

Jeder Aufruf gibt ein Promise zurück. Ein fehlgeschlagener Aufruf wird mit einem nx.NxError abgelehnt, der code, message und bei Prüffehlern fieldErrors trägt. Lehnt ein Moduldienst eine Aktion ab, ist code SERVICE_REJECTED, serviceCode der eigene Code des Dienstes (etwa COMPOSE_STALE) und serviceStatus sein HTTP-Status; prüfen Sie serviceCode, nicht den Text, der in der Sprache der Person steht.

AufrufErgebnis
nx.ready()Der Kontext (siehe oben)
nx.on(name, fn)Meldet einen Empfänger für context, theme oder record an; gibt eine Abmeldefunktion zurück
nx.records.search(entity, {q?, filter?, sort?, from?, size?}){items, total}. filter je Feld: ein Wert, eine Liste von Werten oder {from, to}. sort: 'feld', 'feld,asc' oder 'feld,desc' (siehe unten). size höchstens 200
nx.records.get(entity, id)Der Datensatz
nx.records.create(entity, data)Der neue Datensatz
nx.records.update(entity, id, patch, {version?})Der Datensatz. Mit version schlägt der Aufruf mit CONFLICT fehl, wenn auf dem Server ein neuerer Stand liegt
nx.records.remove(entity, id)true
nx.lookup(target, {q?, ids?})[{id, label, sublabel}]. Ziele: ein Fachobjekt des Moduls, 'core:partner', 'core:partnerContact', 'core:project', 'core:invoice', 'core:opportunity', 'core:user', 'core:dmsDocument', 'core:ismsMeasure'. Ohne q die ersten 20
nx.invoke(action, {recordId?, payload?}){result, record?}. Führt eine Aktion des Moduls aus; der Server prüft ihr Recht und ihr when. Eine Aktion, die eine E-Mail versendet, fragt die Person vorher, siehe unten
nx.actions.preview(action, recordId){recipient: {name, address}, subject, text, fileName}: was eine Aktion, die eine E-Mail versendet, für den Datensatz versenden würde. Versendet nichts
nx.nav.open(target)Öffnet {entity, id}, {list: entity}, {view} (eine Seite des Moduls) oder {core, id} mit core gleich 'partner', 'project', 'invoice' oder 'opportunity'. {view, id} mit einer record.action-Ansicht öffnet deren Dialog für diesen Datensatz und liefert, was der Dialog an nx.dialog.close() übergeben hat, oder null, wenn er abgebrochen wurde. {view, id} mit einer map-Ansicht öffnet die Karte auf diesem Datensatz (?focus=<id>)
nx.ui.toast(message, {kind?})Zeigt eine Meldung; kind ist success, error oder info
nx.ui.confirm({title, message, confirmLabel?, danger?})true, wenn bestätigt. nexxus zeigt dafür seinen eigenen Dialog
nx.dialog.close(result?)Nur in record.action-Ansichten: schließt den Dialog und gibt result an den Aufrufer
nx.storage.get(key)Der gespeicherte Wert, je Person und Ansicht
nx.storage.set(key, value)Speichert einen Wert im Profil der Person, er folgt ihr also auf andere Geräte. set(key, null) entfernt ihn
nx.assistant.setContext({summary, records?})Sagt dem KI-Assistenten, was die Ansicht zeigt. records ist [{entity, id}]; ein leeres Objekt zieht den Kontext zurück
nx.assistant.ask(prompt)Öffnet die Assistentenspalte und stellt prompt als neues Gespräch
nx.t({de, en, pl})Der Text in der Sprache der Person, sonst der englische, sonst der deutsche. pl ist optional
nx.pdf.mount(element, {documentId, page?, highlights?})Ein Handle für einen PDF-Viewer über element (siehe unten)
nx.images.url(documentId, {width?})Eine blob:-Adresse für das Bild des Dokuments, für <img src>. nexxus lädt die Datei mit den Leserechten der Person, die die Ansicht ansieht, und nimmt nur Bilder bis 15 MB an. Mit width, der Breite auf dem Bildschirm in CSS-Pixeln, kommt eine passend verkleinerte Kopie statt des Originals, und der Browser behält sie zehn Minuten. Dieselbe ID und Breite gibt dieselbe Adresse und wird einmal geladen; höchstens vier laden gleichzeitig
nx.files.upload(file, {entity, field, name?}){id, documentId, name, type, size, url}. Legt eine Datei aus dem eigenen Eingabefeld der Ansicht, aus einem Ablegen oder von der Kamera als Dokument der Person ab, für ein Feld von entity, das auf Dokumente verweist. documentId schreibt die Ansicht selbst in dieses Feld; url zeigt die Datei sofort. In Ansichten der Slots record.detail, record.tab und page, mit dem Scope core:dmsDocument:create, siehe Fotos und Dateien aus der Ansicht

Fachobjekte sind die des eigenen Pakets, genannt mit ihrem Schlüssel ('geraet'). Eine Ansicht erreicht kein anderes Paket: Ein Name mit einem Paketschlüssel davor, etwa 'curiaos.kanzlei:akte', scheitert mit INVALID_PARAMS. Suchen filtern nur nach indizierten Feldern.

sort nimmt ein indiziertes Feld oder eines von id, createdAt (angelegt) und updatedAt (zuletzt geändert). Ohne Richtung sortiert es aufsteigend. Ohne sort kommt der zuletzt angelegte Datensatz zuerst. Datensätze ohne Wert im Sortierfeld stehen am Ende, bei gleichem Wert steht der zuletzt angelegte vorn. nx.records.search('geraet', {sort: 'updatedAt,desc', size: 10}) liefert die zehn zuletzt geänderten Datensätze.

Eine Aktion vom Typ mail lässt nexxus im Namen Ihrer Organisation eine E-Mail mit einem PDF an die Adresse eines Kontakts versenden. Die Adresse nimmt nexxus aus dem Kontakt selbst, nie aus Ihrer Ansicht, und fragt die Person, bevor etwas hinausgeht.

Ruft Ihre Ansicht nx.invoke für eine solche Aktion auf, öffnet nexxus außerhalb Ihres Rahmens seinen eigenen Dialog E-Mail versenden. Er zeigt den confirm-Text der Aktion, dann An mit Name und Adresse des Empfängers, Betreff, den Text der E-Mail und Anhang mit dem Dateinamen des PDFs, alles so, wie der Server es ermittelt hat. Nur Versenden versendet, und zwar genau das, was der Dialog gezeigt hat. Abbrechen, das Schließen-Symbol oder Escape lehnen Ihren Aufruf mit CANCELLED ab. Derselbe Dialog öffnet sich, wenn jemand die Aktion in der Aktionsleiste des Datensatzes anklickt.

  • Fragen Sie nicht vorher mit nx.ui.confirm. Die Person müsste sonst zweimal bestätigen.
  • Hat der Kontakt keine E-Mail-Adresse, zeigt der Dialog die Meldung des Servers und bietet nur Schließen an. Ihr Aufruf scheitert dann mit CANCELLED, nx.actions.preview mit NO_ADDRESS.
  • Haben sich der Datensatz oder der Empfänger zwischen Vorschau und Versand geändert, scheitert der Aufruf mit CONFIRMATION_STALE. Rufen Sie nx.invoke erneut auf, dann kommt eine frische Vorschau.
  • nx.actions.preview(action, recordId) liefert dieselbe Vorschau, damit Ihre Ansicht sie neben ihrem Knopf zeigen kann. Sie enthält nie das Token, das einen Versand an die Vorschau bindet. Eine Ansicht kann den Dialog also nicht umgehen.
const mail = await nx.actions.preview('entwurf-versenden', ctx.record.id);
showPreview(mail.recipient.name, mail.recipient.address, mail.subject, mail.text, mail.fileName);
try {
await nx.invoke('entwurf-versenden', {recordId: ctx.record.id});
} catch (e) {
if (e.code !== 'CANCELLED') nx.ui.toast(e.message, {kind: 'error'});
}

Eine Ansicht auf einer Datensatzseite oder auf einer eigenen Seite kann Fotos und andere Dateien aufnehmen, etwa auf einem Tablet vor Ort. Das <input type="file"> bleibt das der Ansicht. nexxus legt die Datei unter Dateien als Dokument der Person ab und gibt ihre ID zurück, die die Ansicht in ein Feld schreibt, das auf Dokumente verweist.

  1. Tragen Sie den Scope core:dmsDocument:create unter scopes ein. Die Administration stimmt ihm beim Installieren des Pakets zu (Dokumente anlegen). Ohne ihn scheitert der Aufruf mit CONSENT_REQUIRED.
  2. Geben Sie der Entität ein ref-Feld mit "target": "core:dmsDocument", mit "multiple": true für mehrere Dateien und "accept": ["image/*"], wenn dort nur Bilder hingehören.
  3. Rufen Sie nx.files.upload(file, {entity, field}) auf und speichern Sie die zurückgegebene documentId im Feld.
// <input type="file" id="photo" accept="image/*" capture="environment">
const input = document.querySelector('#photo');
input.addEventListener('change', async () => {
const file = input.files[0];
if (!file) return;
try {
const photo = await nx.files.upload(file, {entity: 'component', field: 'photos'});
preview.src = photo.url;
const record = await nx.records.get('component', ctx.record.id);
const photos = [...(record.data.photos || []), photo.documentId];
await nx.records.update('component', record.id, {photos}, {version: record.version});
} catch (e) {
nx.ui.toast(e.message, {kind: 'error'});
}
});
  • Was nexxus prüft: Die Person darf Datensätze der Entität ändern, das Feld verweist auf Dokumente und sein accept nimmt die Datei, die Datei ist ein Dokument, Bild, Office-Datei oder eine E-Mail, und sie ist höchstens 25 MB groß. Eine größere Datei weist nexxus ab, bevor sie gesendet wird (TOO_LARGE).
  • Wo die Datei landet: in einem Ordner mit dem Namen des Pakets in den eigenen Dateien der Person, angelegt beim ersten Hochladen. Einzurichten ist nichts, denn wer einen Datensatz lesen darf, der auf die Datei verweist, darf auch die Datei lesen.
  • Wahlweise ein Ordner fürs Team: Die Administration legt unter Dateien einen obersten Ordner mit dem Namen des Pakets in der Sprache der Organisation an und gibt ihn mit dem Recht zum Bearbeiten frei. Die Dateien aller, die ihn sehen und keinen eigenen Ordner dieses Namens haben, landen dann dort. Ist er nur zum Lesen freigegeben, scheitert ihr Hochladen.
  • Wer sie sieht: die Person, die sie hochgeladen hat, alle, für die der Ordner freigegeben ist, und alle, die einen Datensatz des Pakets lesen dürfen, der auf die Datei verweist.
  • Das Hochladen allein ändert keinen Datensatz. Eine Datei, die nie in ein Feld kommt, bleibt in den Dateien der Person.
  • HEIC: iPads und iPhones geben einem Dateifeld meist ein JPEG. Eine HEIC-Datei wird so abgelegt, wie sie ist, und außer Safari zeigt sie kein Browser an.
  • Nicht verfügbar in Kacheln der Startseite, in Aktionsdialogen (record.action) und in öffentlichen Formularen: Dort scheitert der Aufruf mit NOT_AVAILABLE.

Später zeigt die Ansicht ein abgelegtes Foto mit nx.images.url(documentId).

CodeStammt vonBedeutung
VALIDATIONServerDie Daten wurden abgelehnt; fieldErrors nennt die Felder
FORBIDDENServerDer Person fehlt das Recht
NOT_FOUNDServerDatensatz, Aktion oder Ansicht gibt es nicht
MODULE_INACTIVEServerDas Modul ist deaktiviert
CONFLICTServerJemand hat den Datensatz geändert, seit Sie ihn gelesen haben
UNIQUEServerEin eindeutiger Wert ist schon vergeben
NO_ADDRESSServerDer Kontakt einer Aktion, die eine E-Mail versendet, hat keine E-Mail-Adresse
CONFIRMATION_STALEServerDatensatz oder Empfänger haben sich geändert, seit die Vorschau der E-Mail gezeigt wurde, oder die Vorschau ist abgelaufen
CONSENT_REQUIREDServerDem Paket fehlt der Scope, den der Aufruf braucht, etwa core:dmsDocument:create für nx.files.upload
INVALID_PARAMSHostEin Parameter hatte die falsche Form; die Meldung nennt ihn
UNKNOWN_METHODHostDiese SDK-Methode gibt es nicht
BUSYHostZu viele Aufrufe gleichzeitig, oder eine Bestätigung ist schon offen
CANCELLEDHostDie Person hat den Dialog einer Aktion, die eine E-Mail versendet, geschlossen, ohne zu versenden
STORAGE_FULLHostDas Speicherlimit dieser Ansicht ist erreicht
NOT_IN_DIALOGHostnx.dialog.close() außerhalb einer record.action-Ansicht
NO_USER_ACTIONHostDer Aufruf braucht zuerst einen Klick oder Tastendruck in der Ansicht
NOT_AVAILABLEHostDen Aufruf gibt es in dieser Art von Ansicht nicht, etwa nx.files.upload in einer Kachel
NOT_AN_IMAGEHostnx.images.url: Das Dokument ist kein Bild
TOO_LARGEHostnx.images.url: Das Bild ist größer als 15 MB. nx.files.upload: Die Datei ist größer als 25 MB
LIMITHostEin Limit wurde erreicht, etwa zu viele PDF-Viewer
NETWORKHostDie Anfrage hat den Server nicht erreicht
UNAUTHENTICATEDHostDie Sitzung ist abgelaufen

Zeigen Sie der Person error.message. Bei Prüffehlern ist die Meldung allgemein, und fieldErrors enthält die Einzelheiten: Eine Suche, die nach einem nicht indizierten Feld sortiert, scheitert mit „Die Suche ist ungültig.“ und fieldErrors.sort „Nach ‘notiz’ lässt sich nicht sortieren: das Feld ist nicht indexed.“

Der Rahmen ist durchsichtig und folgt dem Farbschema von nexxus: <html data-theme="light"> oder "dark". Auf einer Datensatzseite liegt keine Karte dahinter: Ihre Ansicht steht direkt auf der Seite, wie die Abschnitte im Reiter Daten, also gehören ihre Inhalte in Karten (nx-card). Das UI-Kit ist eine CSS-Datei, die nexxus in jede Ansicht einfügt. Sie dürfen es ignorieren, mit ihm sieht Ihre Ansicht aber in beiden Farbschemata und in der Markenfarbe des Mandanten wie nexxus aus.

VariableVerwendung
--nx-text, --nx-text-muted, --nx-text-faintText, Nebentext, Hinweise
--nx-surface, --nx-surface-raised, --nx-surface-sunken, --nx-pageKarten, Menüs, vertiefte Flächen, der Seitengrund
--nx-line, --nx-line-softRahmen und Trennlinien
--nx-brand, --nx-brand-contrast, --nx-brand-inkMarkenfarbe als Fläche, Text darauf, Markenfarbe als Text
--nx-accent, --nx-success, --nx-warning, --nx-danger, --nx-infoSignalfarben
--nx-radius, --nx-radius-sm, --nx-shadow, --nx-font, --nx-font-monoForm und Schrift
KlasseFür
nx-btn mit nx-btn--primary, --danger, --quiet, --sm, --iconSchaltflächen
nx-badge mit nx-badge--success, --warning, --danger, --info, --brandStatusetiketten
nx-card, nx-card__titleEine Karte
nx-field, nx-field__label, nx-field__hint, nx-field__error, nx-input, nx-select, nx-textareaFormulare; aria-invalid="true" markiert einen Fehler
nx-list, nx-list__item (is-active)Listen; ein <button class="nx-list__item"> ist anklickbar
nx-rows, nx-entity-row (is-active), nx-metaDatensätze so, wie nexxus seine Listen zeichnet; nx-list-compact an der Liste macht daraus Zeilen in einem Rahmen
nx-tableTabellen
nx-empty, nx-empty__icon, nx-empty__titleLeere Zustände
nx-title, nx-subtitle, nx-label, nx-muted, nx-faint, nx-mono, nx-numSchrift
nx-stack, nx-row, nx-spacer, nx-dividerLayout-Helfer
nx-kbd, nx-progress (--nx-value), nx-spinnerKleinigkeiten
nx-pdf-slotDie Fläche, die für ein PDF reserviert ist

Eine Ansicht kann ein PDF aus den Dateien zeigen, ohne je seine Bytes in der Hand zu haben. Sie reservieren ein Element, nexxus legt seinen eigenen PDF-Viewer darüber und lädt das Dokument mit den Rechten der Person, die es ansieht.

const handle = nx.pdf.mount(document.getElementById('pdf'), {
documentId: 88, highlights: [{text: 'binnen zwei Wochen', page: 2}],
});
handle.on('select', ({page, text, rects}) => showNoteButton(page, text));
handle.on('page', page => updatePageLabel(page));
await handle.goTo(3);
const {total, index} = await handle.find('Zustellung');
await handle.setHighlights([{text: quote, page}]);
await handle.destroy();
  • Geben Sie dem Element eine Größe, vor allem eine Höhe: <div id="pdf" class="nx-pdf-slot"></div>. Der Viewer folgt dem Element, wenn es sich bewegt, scrollt oder seine Größe ändert, und wird dort abgeschnitten, wo Ihre scrollenden Container das Element abschneiden.
  • Ereignisse: select ({page, text, rects}, rects als Anteile 0 bis 1 der Seite, Ursprung oben links; eine aufgehobene Auswahl kommt mit leerem text), page (die sichtbare Seite), load ({pages}), error ({code}). handle.ready erfüllt sich, sobald der Viewer steht.
  • Markierungen treffen unabhängig von Groß- und Kleinschreibung und Leerraum, über Zeilenumbrüche hinweg und auch dann, wenn typografische Anführungszeichen und Striche einfach getippt sind. Ohne page markiert der Viewer jedes Vorkommen.
Eine eigene Ansicht mit dem PDF-Viewer von nexxus und einer Notizspalte
Ein Klick auf eine Notiz markiert ihr Zitat; nexxus lädt das PDF mit den Rechten der lesenden Person.
  • Die Höhe folgt Ihrem Inhalt, bis zu 20 000 px. Eine page-Ansicht füllt stattdessen den Bereich unter ihrer Oberkante (mindestens 320 px). Lassen Sie <html> bei seiner natürlichen Höhe, solange Sie keine Seite sind.
  • Tasten mit Strg, Cmd oder Alt sowie Escape gehen weiter an nexxus, es sei denn, Ihr Handler ruft preventDefault() auf. Escape schließt deshalb den Dialog einer record.action-Ansicht.
  • Dialoge (record.action): nexxus zeichnet den Kopf des Dialogs mit Beschriftung, Symbol und Schließen-Schaltfläche der Ansicht. Ihre Ansicht zeichnet den Inhalt und ihre eigenen Schaltflächen und endet mit nx.dialog.close(result). Wird der Dialog auf anderem Weg geschlossen, gilt die Aktion als nicht ausgeführt. Eine andere Ansicht des Moduls öffnet den Dialog mit nx.nav.open({view, id}).
  • Kacheln (dashboard.widget): nexxus zeichnet die Karte mit Symbol, Beschriftung und Modulname, die Ansicht nur den Inhalt. Größen auf einem breiten Bildschirm: s ein Viertel der Zeile, m die Hälfte, l die ganze Zeile.
Eine Ansicht im Slot record.action als Dialog
nexxus zeichnet den Kopf, die Ansicht Inhalt und Schaltflächen.
WasLimit
Gleichzeitige Aufrufe je Ansicht32
Meldungen (Toasts)20 pro Minute, mindestens 400 ms Abstand
nx.storage je Person und Ansicht15 000 Zeichen als JSON; Schlüssel [A-Za-z0-9._-], höchstens 64 Zeichen
Datensatz-Nutzlast je Aufruf256 KB als JSON
Höhe der Ansicht20 000 px
PDF-Viewer je Ansicht4, mit bis zu 50 Markierungen von je 2 000 Zeichen
HTML-Datei2 MB

Paket-Module bringen keinen eigenen Chat mit. Der KI-Assistent von nexxus arbeitet auf jeder Paketseite, und eine Ansicht kann ihm sagen, was sie zeigt.

  • Auf Listen-, Datensatz- und Seitenansichten teilt nexxus dem KI-Assistenten bereits Modul, Fachobjekt, Datensatz und Ansicht mit.
  • nx.assistant.setContext({summary, records}) ergänzt, was nur Ihre Ansicht weiß, zum Beispiel „3 Geräte überfällig“ samt der Datensätze dahinter. Das erscheint als Chip in der Assistentenspalte und geht mit jeder Nachricht mit, solange die Ansicht zu sehen ist.
  • nx.assistant.ask(prompt) öffnet die Assistentenspalte mit einem neuen Gespräch. Das klappt nur direkt nach einem Klick oder Tastendruck in Ihrer Ansicht, eine Ansicht kann also kein Gespräch von sich aus beginnen.
  • Die Beispiel-Anweisungen aus dem Manifest (assistant.prompts mit einem Schlüssel view) erscheinen als Chips in der Assistentenspalte, solange diese Ansicht zu sehen ist.
Die Assistentenspalte mit Beispiel-Anweisungen und Kontext aus einer eigenen Ansicht
Die Chips stammen aus den Beispiel-Anweisungen des Manifests; der Kontext-Chip nennt die Ansicht.

Wie der KI-Assistent die Werkzeuge eines Moduls nutzt, beschreiben Mit dem KI-Assistenten arbeiten und Paketaufbau.

Sie brauchen es selten, aber es hilft beim Lesen der Nachrichten im Browser.

  1. Das SDK schickt {type: 'nxm:ready', sdk} an das übergeordnete Fenster.
  2. nexxus prüft, dass die Nachricht aus dem eigenen Rahmen stammt (event.source), und antwortet mit {type: 'nxm:connect'} und einem MessagePort.
  3. Alles Weitere läuft über diesen Port: Anfragen {t: 'req', id, method, params}, Antworten {t: 'res', id, ok, result} oder {t: 'res', id, ok: false, error} und Ereignisse {t: 'evt', name, data} in beide Richtungen.

Von nexxus an die Ansicht gehen die Ereignisse init, context, theme, record und pdf.*, von der Ansicht an nexxus height, key und pdf.rect. Die Methoden heißen records.search, records.get, records.create, records.update, records.remove, lookup, invoke, actions.preview, nav.open, ui.toast, ui.confirm, dialog.close, storage.get, storage.set, assistant.setContext, assistant.ask, images.get, pdf.mount, pdf.update, pdf.goTo, pdf.find und pdf.destroy.

  • Keine Modulbeschreibung in der Ansicht. Eine Ansicht kann die Feld- und Werte-Beschriftungen des Moduls nicht lesen. Wiederholen Sie die Beschriftungen, die Sie zeigen, mit nx.t({de, en, pl}) und halten Sie sie mit dem Manifest gleich.
  • Keine Rechte in der Ansicht. Eine Ansicht kann nicht fragen, ob die Person eine Aktion ausführen darf. Bieten Sie die Schaltfläche an und zeigen Sie die Meldung des Servers, wenn FORBIDDEN zurückkommt, oder legen Sie die Aktion in die Aktionsleiste des Datensatzes, die nexxus nur Personen zeigt, die sie ausführen dürfen.
  • Keine Symbolschrift. Verwenden Sie eingebettetes SVG.

Kann eine Ansicht nicht laufen, zeigt nexxus an ihrer Stelle eine eigene Meldung:

MeldungUrsache und Abhilfe
„Diese Ansicht konnte nicht geladen werden.“Die Ansicht ließ sich nicht laden, etwa nach einem Netzwerkfehler. Erneut versuchen lädt sie noch einmal.
„Für diese Ansicht fehlt die Berechtigung.“Der Person fehlt ein Recht des Moduls. Geben Sie ihr eine Rolle des Pakets, siehe Rollen.
„Diese Ansicht gibt es nicht (mehr).“Den Ansichtsschlüssel gibt es in der installierten Version nicht, zum Beispiel weil ein Update ihn entfernt hat.
„Das Modul dieser Ansicht ist nicht aktiv.“Das Paket ist deaktiviert. Aktivieren Sie es auf seiner Paketseite.

In Ihrer eigenen Ansicht sind die häufigsten Überraschungen ein fetch, das die Inhaltsrichtlinie sperrt (nehmen Sie nx.records), eine Sortierung oder ein Filter auf einem Feld ohne indexed (VALIDATION) und nx.assistant.ask ohne vorherigen Klick (NO_USER_ACTION).