Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Links korrigiert
Table of Contents
minLevel1
maxLevel2
outlinetrue
styledefault
typelist
printablefalse

Im <Code>- Tag können wir JavaScript Code schreiben und haben damit ein sehr mächtiges Instrument, Inhalte in Vorlagen zu dynamisieren.

Alles was die Programmierung innerhalb von Code betrifft, ist hier dokumentiert: https://primesoft-group.atlassian.net/wiki/x/DgC7Fw. Wir empfehlen, zuerst diese Seite zu lesen.

Schnelleinführung in JavaScript

Generell

JavaScript in Fields

Innerhalb des <Code>-Tag sind zwei Varianten zum Ausführen von Code möglich: https://primesoft-group.atlassian.net/wiki/spaces/PDT/pages/398131214/Code#Aufrufe-in-Code

Tip

TIPP
Um den Code übersichtlicher zu gestalten, verwenden wir wenn möglich den abgekürzten Aufruf.
Erst bei komplexeren Konfigurationen kommt der ausformulierte Aufruf zum Einsatz.

Kontrollstrukturen

Eine Kontrollstruktur prüft, ob eine Bedingung erfüllt ist oder nicht und gibt dann den Inhalt aus, der definiert wurde.

if-Statements

if-Statements sind praktisch, wenn man Bedingungen formulieren möchte wie “wenn [Bedingung], dann passiert A, sonst wenn [Bedingung 2], passiert B, […], sonst passiert Z”.

Code Block
breakoutModewide
languagejs
<FormattedText Name="EnclosuresBoxTitle">
  <Code>
      function main() {
        // if-Statement
        if($("Forms.Enclosures")) { // wenn das Enclosures-Forms-Feld nicht leer ist...
          return $.translations.getFormattedText("FormattedTexts.Enclosures"); // hole den FormattedText
        } else if($("Forms.CopyTo")) { // sonst, wenn das CopyTo-Forms-Feld nicht leer ist...
          return $.translations.getFormattedText("FormattedTexts.CopyTo"); // hole den FormattedText
        } else { // sonst...
          return $.translations.getFormattedText("FormattedTexts.InvisibleLine"); // hole den FormattedText
        }
      }
  </Code>
</FormattedText>

switch-case-Statements

Wenn ein Ausdruck mehrere verschiedene Werte haben kann, welche zu verschiedenen Resultaten führen, könnte dies mit vielen if-statements gelöst werden (if(myVariable === "someContent")).
Eine bessere Methode sind switch-case-statements. Im switch wird ein Ausdruck angegeben, welcher mittels jedem case auf einen bestimmten Wert überprüft wird.

Beispiel mit direktem Rückgabewert

Code Block
breakoutModewide
languagejs
<Text Name="xHatSatz">
  <Code>
      function main() {
        switch ($('Forms.EmpfaengerTyp')) { // prüfe die Werte vom Empfängertyp
          case 'Einzelperson': return $('Forms.Vorname') + " hat "; // für den Fall 'Einzelperson', gib diesen Text zurück.
          case 'Ehepaar': return "Die Ehegattin und der Ehegatte haben ";
          case 'Familie': return "Die Familie hat ";
          default: return " "; // sonst ist der Feldwert ein Leerschlag
            // default wird ausgeführt, wenn kein Fall wahr ist.  
            // Das muss gesetzt werden, falls der Wert auch leer sein kann 
            // (z.B. der Benutzer in einem Choice-Forms-Feld nichts auswählen kann).
        }
    }
  </Code>
</Text>

Beispiel mit breaks

Code Block
breakoutModewide
languagejs
<Text Name="xHatSatz">
  <Code>
      function main() {
        let sentenceA = "";
        switch ($('Forms.EmpfaengerTyp')) { // prüfe die Werte vom Empfängertyp
          case 'Einzelperson': 
            sentenceA = $('Forms.Vorname'); // gib diesen Text zurück.
            break;
          case 'Ehepaar': 
            sentenceA = "Das Ehepaar";
            break;
          case 'Familie': 
            sentenceA = "Die Familie";
            break;
          default: 
            sentenceA = "Die Person hat "; // sonst ist der Feldwert ein leerer String
            break;
          }
          return $.joinNonEmpty(" ", sentenceA, " hat")
        }
    }
  </Code>
</Text>

Ternary-Operator ?:

Der Ternary-Operator ermöglicht eine Kontrollstruktur auf einer Code-Zeile:

[Wenn eine Bedingung wahr ist] ? [mach das eine] : [sonst, mach das andere];

Beispiel

Code Block
breakoutModewide
languagejs
<Text Name="MitteilungTitel">
  <!-- Wenn das Profilfeld nicht leer ist, 
                             gib den einen Text aus,                     sonst den anderen -->
  <Code>$("Profile.Org.Unit") ? "Mitteilung der " + $("Profile.Org.Unit") : "Mitteilung"</Code>
</Text>

Dieser Code bedeutet:

  1. Wenn das Unit-Profilfeld nicht leer ist,

    1. gib den Text “Mitteilung der “ gefolgt von Unit aus,

    2. sonst “Mitteilung”.

Beispiel Verschachtelung

Anweisungen können auch verschachtelt werden:

Code Block
breakoutModewide
languagejs
<Text Name="MitteilungTitel">
  <Code>$("Profile.Org.Unit") ? 
          "Mitteilung " + ($("Profile.Org.Unit") === "Einwohnerdienste" ? "an alle Einwohner" : " der" + $("Profile.Org.Unit"))
          : "Mitteilung"</Code>
</Text>

Dieser Code bedeutet:

  1. Wenn das Unit-Profilfeld nicht leer ist, gib “Mitteilung “ aus und prüfe dann, ob das Unit-Profilfeld genau “Einwohnerdienste” enthält;

    1. falls ja, gib “an alle Einwohner” aus,

    2. sonst “ der [Unit-Profilfeld]”.

  2. Wenn das Unit-Profilfeld leer ist, gib “Mitteilung” aus.

Schlaufen (Loops)

Generell

  • Mit Loops können Befehle wiederholt werden, bis eine Abbruchbedingung erreicht wird. Einer der häufigsten Anwendungsfälle ist das Iterieren über einen Array.

  • Arrays bzw. Collections haben eine endliche Anzahl Elemente. Dies nennt man “Länge” und sie kann so geholt werden: myCollection.length.

  • Vorgehen: for(Initialisierung; Test; Aktualisierung){/* Anweisungsblock */ }

    • Initialisierung: Die Laufvariable i wird zuerst definiert und ein Startwert gesetzt (praktisch immer 0).

    • Test: Das Ergebnis dieser Expression muss true oder false ergeben. Hier wird bei jedem Durchlaufen geprüft, ob die Schleife weiterlaufen muss oder der Endwert erreicht worden ist.

    • Aktualisierung (Update): Die Laufvariable i wird nach jedem Durchlaufen der Schleife verändert. In den meisten Fällen wird sie hochgezählt: i++

Klassischer for-Loop

Mit dem klassischen for-Loop iteriert man über ein Array (z.B. eine ObjectCollection) und kann mit jedem Element im Array etwas machen.

Beispiel: for(var i=0; i < myArray.length; i++){/* mach etwas mit i */ }

Mein Array enthält vier Elemente: [0], [1], [2], [3]. Was in diesen Elementen genau ist, ist an dieser Stelle irrelevant.

  • Initialisierung: Initialisiere die Laufvariable i mit 0.

  • Test: “Solange i kleiner ist als die Anzahl Elemente in meinem Array”

  • Aktualisierung: zähle i nach jedem Durchlauf um + 1 hoch.

Ausführliches Beispiel for-Loop

In diesem Beispiel wird jedes Element im Array direkt ausgegeben als Teil eines Textes:

Code Block
languagejs
const fruits = [apple], [grape], [strawberry], [lemon];

for(var i=0; i < fruits.length; i++){
  const fruit = fruits[i]; // hole das Element an Position i
  return "Fruit: " + fruit + " at position: " + i + "\n";
}

Alternativen zum klassischen for-Loop

Note

Benötigt mindestens primedocs Version 4.0.20160

Nutze alternativ die folgenden besser verständlichen Varianten von for-Loops:

forEach

Loop mit

Listenelement

map(), ohne

index

Index

Code

Code Block
languagejs
const fruits = ["apple", "grape", "strawberry", "lemon"];

// Der Parameter 'fruit' ist der zurückgegebene Wert
for(const fruit of fruits){
  return
return fruits
        .map(fruit => "Fruit: " + fruit + )
        .join("\n");
};

Resultat

Code Block
languagetext
Fruit: apple

Fruit: grape

Fruit: strawberry

Fruit: lemon
"
forEach mit Listenelement und mit index

Loop mit map(), mit Index

Code

Code Block
languagejs
const fruits = ["apple", "grape", "strawberry", "lemon"];

// DerDie Parameter 'fruit' ist der zurückgegebene Wert an der Position 'index'
return fruits.forEach
        .map((fruit, index) => { 
  return ""Fruit: " + fruit + " at position: " + index + )
        .join("\n";
});

Resultat

Code Block
languagetext
Fruit: apple at position: 0 
Fruit: grape at position: 1 
Fruit: strawberry at position: 2 
Fruit: lemon at position: 3
Loop

forEach mit

map()

Listenelement, ohne

Index

index

Code

Code Block
languagejs
const fruits = ["apple", "grape", "strawberry", "lemon"];

// Der Parameter 'fruit' ist der zurückgegebene Wert
returnfor(let fruitsfruit of fruits){
      .map(fruit =>return += "Fruit: " + fruit)         .join(+ "\n");
};

Resultat

Code Block
languagetext
Fruit: apple
Fruit: grape
Fruit: strawberry
Fruit: lemon"
Loop mit map(), mit Index

forEach mit Listenelement und mit index

Code

Code Block
languagejs
const fruits = ["apple", "grape", "strawberry", "lemon"];

// DieDer Parameter 'fruit' ist der zurückgegebene Wert an der Position 'index'
return fruits
        .map.forEach((fruit, index) => { 
  return += "Fruit: " + fruit + " at position: " + index)         .join(+ "\n";
});

Resultat

Code Block
languagetext
Fruit: apple at position: 0

Fruit: grape at position: 1

Fruit: strawberry at position: 2

Fruit: lemon at position: 3

CDATA-Tag

In jeder Programmier- oder Scripting-Sprache gibt es bestimmte Sonderzeichen. Da wir hier JavaScript (JS) innerhalb von XML einsetzen, könnten Zeichen von JS fälschlicherweise als XML-Sonderzeichen interpretiert werden.

Um dies zu verhindern, kann das gesamte JS mit CDATA umschlossen werden: <![CDATA[Mein Text]]>. Somit muss man ohne ein CDATA-Tag &amp; und kann mit einem CDATA-Tag nur & schreiben.

Funktionen

In diesem Abschnitt werden ein paar Funktionen genauer erklärt. Diese Seite dient dabei als GrundlageSiehe Beispiele und mehr Informationen in der technischen Dokumentation: https://primesoft-group.atlassian.net/wiki/spaces/PDT/pages/398131214/Code#FunktionenCode#CDATA-Tag

Funktionen

In diesem Abschnitt werden ein paar Funktionen genauer erklärt. Dieses Kapitel dient dabei als Grundlage: https://primesoft-group.atlassian.net/wiki/spaces/PDT/pages/398131214/Code#API-Beschreibung

Funktion joinNonEmpty()

Gemäss Definition unter https://primesoft-group.atlassian.net/wiki/spaces/PDT/pages/398131214/Code#Erkl%C3%A4rungCode#Funktionen-allerim-FunktionenDetail-pro-API, fügt diese Funktion die items nacheinander zusammen und trennt sie dabei mit dem separator.

Wir benutzen diese Funktion überall, wo wir Wörter aneinanderreihen müssen, z.B. in Kopfzeilen für das Aneinanderreihen von Profildaten.

Die wichtigste Eigenschaft dieser Funktion ist, dass dabei leere Parameter ausgelassen werden, sodass auch der separator nur einmal ausgegeben wird.

Beide Parameter, separator und items, sind obligatorisch und müssen in genau dieser Reihenfolge gesetzt werden. D.h. der erste Parameter ist der Separator, alle weiteren Parameter sind die Felder, die zusammengeführt werden sollen.

Simples Beispiel

Die Funktion reiht alle Elemente an einander und trennt sie mit einem |.

Code Block
languagejs
<Text Name="Example1">
  <!-- Resultat: "Beispielfirma | Beispielabteilung" -->
  <Code>$.joinNonEmpty(" | ", 'Profile.Org.Title', 'Profile.Org.Unit')</Code>
</Text>  

Umfangreicheres Beispiel

Die Funktion reiht alle Elemente an einander und trennt sie mit einem Zeilenumbruch (\n).

In diesem Beispiel enthält das Feld Profile.Org.Unit keinen Wert.

Code Block
languagejs
<Text Name="Example2">
  <Code>$.joinNonEmpty("\n", // schwacher Zeilenumbruch
                   $('Profile.Org.Title'),
                   $('Profile.Org.Unit'),
                   $('Profile.Org.Postal.Street'),
                   $('Profile.Org.Postal.Zip') + " " + $('Profile.Org.Postal.City'));
    /* Resultat:
     Another Company
     Hardstrasse 201
     8005 Zürich
    */
  </Code>
</Text>

Umfangreicheres Beispiel “Empfängerliste“

Code Block
languagejs
<Text Name="Example3">
  <Code>
    function main(){
      let personArray = $("Forms.RecipientCollection").map(person => $.joinNonEmpty(" ", person.FirstName, person.LastName));
      return $.joinNonEmpty("\n", personArray);
    }
    /* Resultat:
     Max Muster
     Anna Ansicht
     Barbara Beispiel
    */
  </Code>
</Text>

Beachte, wie oben die einzelnen Zeilen übersichtlich untereinander dargestellt werden. So ist der Code schneller von anderen lesbar!

Wieso joinNonEmpty() nutzen?

Alternativ könnte man oberes Beispiel auch ohne die Funktion joinNonEmpty() zusammenbauen (siehe Code unten).

Auch in diesem Beispiel ist das Feld Profile.Org.Unitleer.
Das resultiert aber gerade darin, dass der “Separator” von der Zeile mit Profile.Org.Title (+ "\n" +) auch eingefüllt wird, wenn Profile.Org.Unit keinen Wert enthält.
D.h. es wird so ein Zeilenumbruch eingefügt, obwohl man das in den meisten Fällen nicht möchte, siehe Resultat im Code.

Code Block
languagejs
<Text Name="Example2">
  <Code>$('Profile.Org.Title') + "\n" +
        $('Profile.Org.Unit') + "\n" +
        $('Profile.Org.Postal.Street') + "\n" +
        $('Profile.Org.Postal.Zip') + " " + $('Profile.Org.Postal.City');
        
    /* Resultat:
     Another Company
  
     Hardstrasse 201
     8005 Zürich
    */
  </Code>
</Text>  
Note

WICHTIG
Die Funktion joinNonEmpty() ist für alle Fälle unerlässlich, wo Elemente eventuell leer sind (z. B. Profilfelder, die vom Benutzer nicht ausgefüllt werden müssen).

Tip

TIPP
Der Einsatz von joinNonEmpty() gibt weniger zu Schreiben.

Datumsfunktionen und mit Daten rechnen

Beachte diese Liste: https://primesoft-group.atlassian.net/wiki/spaces/PDT/pages/edit-v2398131214/398131214#Erkl%C3%A4rungCode#Funktionen-allerim-FunktionenDetail-pro-API

Info

Die meisten Funktionen verlangen als Parameter nicht nur das Datum über $("Forms.Date") sondern den reinen Wert des Datums. Den Wert holt man über .Value.
Beispiel: $("Forms.Date").Value

Beispiel: 60 Tage zu einem Datum addieren

Code Block
languagexmljs
<Date Name="DatePlus60">
  <Code>
    function main(){
      let result = $("Forms.Date").Value; // auf den Wert zugreifen: result ist dann vom Typ Date
      result.setDate(result.getDate() + 60); // Berechnung
      return result;
    }
  </Code>
</Date>

Beispiel: Datum in einem bestimmten Datumsformat ausgeben

Datumsformat aus Text

Code Block
languagexml
<Text Name="DatePlus60">
  <!-- 1. Parameter date, 2. Parameter: format, Resultat: 2024-07-23 -->
  <Code>$.formatDate($("Forms.Date").Value, "yyyy-MM-dd")</Code>
</Text>

Datumsformat aus globalen Übersetzungen

Code Block
languagexml
<Text Name="DatePlus60">
  <!-- 1. Parameter date, 2. Parameter: format, Resultat: Dienstag, 23. Juli 2024 -->
  <Code>$.formatDate($("Forms.Date").Value,
                     $.translations.getText("Configuration.Date.WrittenOutWithDay"))</Code>
</Text>
Info

Diese Liste zeigt, welche Optionen man für Datumsformate hat: Custom date and time format strings - Microsoft Learn.

ObjectCollections/Objects in Fields verwenden

Ein Element einer ObjectCollection in einem Field anzeigen

In diesem Beispiel haben wir folgende ObjectCollection in Forms und machen daraus via Fields eine kleine Übersicht mit dem ersten Element dieser Collection:

Forms Konfiguration

Code Block
breakoutModewide
languagexml
<FormsConfiguration>
  <Elements>
    <ObjectCollection Id="Essen" Label="Essen">
      <Schema>
        <Text Id="Name" Label="Name" />
        <YesNo Id="IsGesund" Label="Ist gesund?" />
        <Date Id="GegessenAm" Format="dd.MM.yyyy" RelativeDate="Today" Label="Gegessen am" />
        <Choice Id="ImKuehlschrank" Label="Ist im Kühlschrank?" SelectedValue="ja">
          <Option Value="ja" Label="Ja" />
          <Option Value="nein" Label="Nein" />
        </Choice>
      </Schema>
    </ObjectCollection>
  </Elements>
</FormsConfiguration>

Resultat Forms

image-20240823-061540.pngImage Added

Fields Konfiguration

Code Block
breakoutModewide
languagexmljs
<FieldsConfiguration>
  <Fields>
    
    <Text Name="ObjectCollectionFieldItemCollection">
      <Code>
        function main() {
          constlet firstElementfirstItem = $('"Forms.Essen'")[0]; // nehme nurhole das erste Element
          if(firstElementfirstItem !== null){
            const gesundisGesund = firstElementfirstItem.IsGesund ? " ist gesund." : " ist nicht gesund.";
            return $.joinNonEmpty("\n", // schwacher Zeilenumbruch
                         "Name: " + firstElementfirstItem.Name + gesundisGesund,

                        "Zuletzt gegessen am: " + firstElementfirstItem.GegessenAm.FormattedValue,
 
                       "Ist im Kühlschrank:? " + firstElementfirstItem.ImKuehlschrank);,
                       " ");
            } else { 
            return "In dieser Liste hat es keine Einträge.";
          }
        }
      </Code>
    </Text>
    
  </Fields>
</FieldsConfiguration>

Resultat Fields

Dies resultiert mit folgenden Objects im

Forms…

Image Removed

Forms in diesem Text im Dokument:

Image Removed

Das obere Beispiel zeigt nur das erste Element.

image-20240827-120957.pngImage Added
Note

In der Informatik beginnt man in der Regel mit 0 anstatt mit 1. Dementsprechend: das 0te Element ist umgangssprachlich das “erste Element”.

Doch…

Mehrere Elemente einer ObjectCollection anzeigen

Wie können wir nun alle Elemente der ObjectCollection anzeigen?

Hierzu benötigen wir nun noch zwei neue Konzepte um dies umzusetzen:

  1. Einen Loop/Schlaufe; lies dazu zuerst das: https://primesoft-group.atlassian.net/wiki/spaces/DELDEV/pages/175112198/JavaScript+in+primedocs+Fields#Schlaufen-%2F-Loops

  2. Das CDATA-Tag; lies dazu zuerst das: https://primesoft-group.atlassian.net/wiki/spaces/DELDEV/pages/175112198/JavaScript+in+primedocs+Fields#CDATA-Tag

Wieso benötigen wir das im nächsten Beispiel? Im Loop benötigen wir auf Zeile 16 spezielle Zeichen. Der Einsatz dieser Zeichen ohne CDATA-Tag würde zu invalidem Code führen.

Mehrere Elemente einer Collection in einem Field anzeigen - Lösungsansatz 1 mit map()

Code Block
breakoutModewide
languagejs
<Text Name="ObjectCollectionFieldItemsCollectionMap">
  <Code><![CDATA[ // Wird benötigt für Zeichen in Zeile 1612
    function main() {

         let resultcollection = ""$("Forms.Essen");
          if($("Forms.Essen")collection !== null){ // Wenn ein Eintrag vorhanden ist
        let collection = $("Forms.Essen");// Jedes item wird mittels map() in die gewünschte Beschreibung umgewandelt. 
        // Aus der Liste von items entsteht eine Liste von Beschreibungen.
        
        return collection.map(item => $.joinNonEmpty("\n",
          "Name: " + item.Name + (item.IsGesund ? " ist gesund." : " ist nicht gesund."),
          "Zuletzt gegessen am: " + item.GegessenAm.FormattedValue,
          "Ist im Kühlschrank: " + item.ImKuehlschrank,
          "Muss wieder eingekauft werden? " + ((item.IsGesund || item.GegessenAm < $("Forms.Date")-30) && item.ImKuehlschrank === "nein" ? "Ja" : "Nein")))
        .join("\n\n"); // Alle Beschreibungen werden anschliessend zusammengesetzt, mit einem Zeilenumbruch als Trennzeichen.
      
      } else { // Wenn kein Eintrag vorhanden ist
        return "In dieser Liste hat es keine Einträge.";
      }
    }
  ]]></Code>
</Text>

Mehrere Elemente einer Collection in einem Field anzeigen - Lösungsansatz 2 mit for

Code Block
breakoutModewide
languagejs
<Text Name="ItemsCollection">
  <Code><![CDATA[ // Wird benötigt für Zeichen in Zeile 16
    function main() {
          let result = "";
          let collection = $("Forms.Essen");
          if(collection !== null){
            for(const item of collection){
              let isGesund = item.IsGesund ? " ist gesund." : " ist nicht gesund."
           
                 result += $.joinNonEmpty("\n", // schwacher Zeilenumbruch
                           "Name: " + item.Name + isGesund,
                           "Zuletzt gegessen am: " + item.GegessenAm.FormattedValue,
                           "Ist im Kühlschrank:? " + item.ImKuehlschrank,
                           "Muss wieder eingekauft werden? " + ((item.IsGesund || item.GegessenAm < $("Forms.Date")-30) && item.ImKuehlschrank === "nein" ? "Ja" : "Nein";),
                           " ") + "\n";
            } 
            return result;
          } else {
            return "In dieser Liste hat es keine Einträge.";
          }
        }
    ]]>
  </Code>
</Text>

Resultat Fields

Dies resultiert mit den

gleichen Objects wie

Objekten in Forms

wie oben…image-20240823-061540.pngImage Removed

in diesem Text im Dokument:

image-20240823-063203.pngImage Removed

in den folgenden Texten im Dokument.

Beim Lösungsansatz 2 wird noch ein Zeilenumbruch mehr eingefügt, daher empfehlen wir falls möglich den Lösungsansatz 1.

Field ItemsCollectionMap

image-20240827-122753.pngImage Added

Field ItemsCollection

image-20240827-120919.pngImage Added

Für mehr Informationen und JavaScript-eigene Funktionen, besuche Mozilla’s JavaScript Dokumentation.