JavaScript in Fields.
We can write JavaScript code in the <Code>
tag and thus have a very powerful tool for dynamising content in templates.
Everything relating to programming within code is documented here: https://primesoft-group.atlassian.net/wiki/x/DgC7Fw (in German). We recommend that you read this page first.
Quick introduction to JavaScript
General
To call a function predefined in primedocs,
$.
must be written first. Example:$.myFunction('Profile.User.FirstName')
, see Code | Funktionsaufrufe (in German)A primedocs field is called as follows:
$("Profile.User.FirstName")
, SeeCode | Zugriff auf Felder (in German)The contents of the brackets in a function call is called a parameter (in the example above, "Profile.User.Name" is the parameter).
A function call can also contain several parameters. These are separated by commas:
$.myFunction('Profile.User.FirstName', anotherVariable)
A variable that later takes on a different value in your script is defined with
let
.
Example:let result = " ";
A variable that never takes on a different value (known as a constant) is defined as
const
. Example:const name = $.getText('Profile.User.FirstName');
A "command" is always concluded with a semicolon:
;
.The code in a
Code
tag requires a return value via areturn
statement. The return value must be either implicit or explicit. See the next chapter JavaScript in Fields. Also note Code | Rückgabewert von Field Typen (in German).Single-line comments are started using
//
.For multi-line comments, the desired text is written between
/*
and*/
.
JavaScript in Fields
Two variants for executing code are possible within the tag:
Code | Aufrufe in Code (In German)
TIP
To make the code clearer, we use the abbreviated call whenever possible.
The fully-formulated call is only used for more complex configurations.
Control structures
A control structure checks whether a condition is fulfilled or not and then outputs the content that was defined.
if-Statements
if-statements are useful if you want to formulate conditions such as "if [condition], then A happens, otherwise if [condition 2], B happens, [...], otherwise Z happens".
<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
If an expression can have several different values that lead to different results, this could be solved with many if
-statements (if(myVariable === "someContent")
). A better method is switch-case
-statements. An expression is specified in the switch
, which is checked for a specific value using each case
.
Example with direct return value
<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>
Example with breaks
<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"; // sonst ist der Feldwert ein leerer String
break;
}
return $.joinNonEmpty(" ", sentenceA, " hat")
}
}
</Code>
</Text>
Ternary-Operator ?:
The ternary operator enables a control structure on a line of code:
[If a condition is true] ?
[make a] :
[otherwise make the other];
Example
<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>
This code means:
If the unit profile field is not empty,
enter the text "Message from " followed by Unit,
otherwise "Message".
Example of nesting
Instructions can also be nested:
<Text Name="MitteilungTitel">
<Code>$("Profile.Org.Unit") ?
"Mitteilung " + ($("Profile.Org.Unit") === "Einwohnerdienste" ? "an alle Einwohner" : " der" + $("Profile.Org.Unit"))
: "Mitteilung"</Code>
</Text>
This code means:
If the unit profile field is not empty, enter "Message" and then check whether the unit profile field contains exactly "Resident services";
if yes, enter "to all residents",
otherwise " the [unit profile field]".
If the unit profile field is empty, enter "Message".
Loops
Generally
Loops can be used to repeat commands until a cancellation condition is reached. One of the most common use cases is iterating over an array.
Arrays or collections have a finite number of elements. This is called "length" and can be retrieved in this way:
myCollection.length
.Procedure:
for(Initialisierung; Test; Aktualisierung){/* Anweisungsblock */ }
Initialisation: The running variable
i
is defined first and a start value is assigned (almost always 0).Test: The result of this expression must be
true
orfalse
. A check is made here each time the loop is run as to whether the loop must continue or whether the end value has been reached.Update: The running variable
i
is changed with every iteration of the loop. In most cases, it will be incremented:i++
Classic for-loop
With the classic for loop, you iterate over an array (e.g. an ObjectCollection) and can do something with each element in the array.
Example: for(var i=0; i < myArray.length; i++){/* mach etwas mit i */ }
My array contains four elements: [0], [1], [2], [3]. What exactly is in these elements is irrelevant at this point.
Initialisation: Initialise running variable
i
with 0.Test: “As long as
i
is smaller than the number of elements in my array”Update: increment
i
after each loop by +1.
Detailed example for-Loop
In this example, each element in the array is output directly as part of a text:
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";
}
Alternatives to the classic for-loop
Berequires at least primedocs version 4.0.20160
Alternatively, use the following easier-to-understand variants of for loops:
Loop with map(), without index
Code
const fruits = ["apple", "grape", "strawberry", "lemon"];
// Der Parameter 'fruit' ist der zurückgegebene Wert
return fruits
.map(fruit => "Fruit: " + fruit)
.join("\n");
Result
Fruit: apple
Fruit: grape
Fruit: strawberry
Fruit: lemon"
Loop with map(), with index
Code
const fruits = ["apple", "grape", "strawberry", "lemon"];
// Die Parameter 'fruit' ist der zurückgegebene Wert an der Position 'index'
return fruits
.map((fruit, index) => "Fruit: " + fruit + " at position " + index)
.join("\n");
Result
Fruit: apple at position 0
Fruit: grape at position 1
Fruit: strawberry at position 2
Fruit: lemon at position 3
forEach with list element, without index
Code
const fruits = ["apple", "grape", "strawberry", "lemon"];
let result = "";
// Der Parameter 'fruit' ist der zurückgegebene Wert
for(let fruit of fruits){
result += "Fruit: " + fruit + "\n";
};
return result;
Result
Fruit: apple
Fruit: grape
Fruit: strawberry
Fruit: lemon
forEach with list element and with index
Code
const fruits = ["apple", "grape", "strawberry", "lemon"];
let result = "";
// Der Parameter 'fruit' ist der zurückgegebene Wert an der Position 'index'
fruits.forEach((fruit, index) => {
result += "Fruit: " + fruit + " at position: " + index + "\n";
});
return result;
Result
Fruit: apple at position: 0
Fruit: grape at position: 1
Fruit: strawberry at position: 2
Fruit: lemon at position: 3
CDATA-Tag
There are certain special characters in every programming or scripting language. As we are using JavaScript (JS) within XML here, characters from JS could be incorrectly interpreted as special XML characters.
To prevent this, the entire JS can be enclosed with CDATA: <![CDATA[Mein Text]]>
. This means that you have to write &
without a CDATA tag and with a CDATA tag , you can write &
.
See examples and more information in the technical documentation: Code | CDATA Tag (in German)
Functions
This section explains a few functions in more detail. This chapter serves as a basis: Code | API Beschreibung (In German)
Funktion joinNonEmpty()
Gemäss Definition unter Code | Funktionen im Detail 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 |
.
<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.
<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“
<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.Unit
leer.
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.
<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>
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).
TIPP
Der Einsatz von joinNonEmpty()
gibt weniger zu Schreiben.
Datumsfunktionen und mit Daten rechnen
Beachte diese Liste: Code | Funktionen im Detail pro API
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
<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
<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
<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>
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
<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
Fields Konfiguration
<FieldsConfiguration>
<Fields>
<Text Name="ItemCollection">
<Code>
function main() {
let firstItem = $("Forms.Essen")[0]; // hole das erste Element
if(firstItem !== null){
const isGesund = firstItem.IsGesund ? " ist gesund." : " ist nicht gesund.";
return $.joinNonEmpty("\n", // schwacher Zeilenumbruch
"Name: " + firstItem.Name + isGesund,
"Zuletzt gegessen am: " + firstItem.GegessenAm.FormattedValue,
"Ist im Kühlschrank? " + firstItem.ImKuehlschrank,
" ");
} else {
return "In dieser Liste hat es keine Einträge.";
}
}
</Code>
</Text>
</Fields>
</FieldsConfiguration>
Resultat Fields
Dies resultiert mit folgenden Objects im Forms in diesem Text im Dokument:
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:
Einen Loop/Schlaufe; lies dazu zuerst das: https://primesoft-group.atlassian.net/wiki/spaces/DELDEV/pages/175112198/JavaScript+in+primedocs+Fields#Schlaufen-%2F-Loops
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()
<Text Name="ItemsCollectionMap">
<Code><![CDATA[ // Wird benötigt für Zeichen in Zeile 12
function main() {
let collection = $("Forms.Essen");
if(collection !== null){ // Wenn ein Eintrag vorhanden ist
// 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
<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 Objekten in Forms 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
Field ItemsCollection
Für mehr Informationen und JavaScript-eigene Funktionen, besuche Mozilla’s JavaScript Dokumentation.
PrimeSoft AG, Bahnhofstrasse 4, 8360 Eschlikon, Switzerland