An diesem Punkt haben wir eine ziemlich ordentliche Komponente. Wenn wir sie jedoch an andere weitergeben wollen (insbesondere an Nicht-Entwickler), ist ihr Nutzen sehr begrenzt. Sie bietet nur eine Auswahl zwischen Äpfeln und Birnen. Während dies für unseren ersten Kunden (wahrscheinlich eine Obstkuchenfabrik) perfekt war, wird das Automobilunternehmen, an dem wir als Nächstes arbeiten, Augmented Reality wahrscheinlich nicht verwenden, um den Arbeitern bei der Auswahl von Obst im Pausenraum zu helfen. Es ist an der Zeit, unsere Komponenten wiederverwendbar und konfigurierbar zu machen.
Sie wissen bereits, wie man Komponenten konfiguriert. Wenn Sie auf eine Komponente klicken, wird eine Seitenleiste mit den Konfigurationsoptionen eingeblendet. Die Konfigurierbarkeit von Komponenten besteht aus zwei Teilen: der Definition dessen, was der Benutzer konfigurieren kann, und der Verarbeitung der ausgewählten Werte.
Festlegen der Konfigurationsoptionen
Die Konfigurationsoptionen, die Sie anbieten möchten, werden im JSON-Format definiert. Die Benutzer können aus einer Reihevon Eingabetypen wählen, z. B. einfache Texteingabe, Eingabe über ein Kontrollkästchen und Datei-Upload-Eingabe.
Hier ist eine Beispielkonfiguration.
{
"tab1": {
"title_text": {
"title": "Title",
"inputType": "textinput",
"value": "Please select"
}
},
"tab2": {
"options": {
"title": "Options",
"inputType": "map-input",
"placeholder": {
"key": "Option Key",
"value": "Option Title"
},
"value": [
{
"key": "option1",
"value": "Option 1"
},
{
"key": "option2",
"value": "Option 2"
},
{
"key": "option3",
"value": "Option 3"
}
]
},
"use_all": {
"title": "Offer last option",
"inputType": "checkbox-input",
"value": "true"
}
}
}
Die Objekte auf der Stammebene der JSON-Datei sind die Registerkarten des Konfigurationspanels (ihr Schlüssel wird als Titel der Registerkarte angezeigt). Jedes Eingabefeldobjekt hat mindestens drei Attribute:
- eine
Überschrift
, die über dem Eingabefeld im Konfigurationsfenster angezeigt wird - einen
inputType
, d.h. die Art des Eingabefeldes, das angezeigt werden soll - einen
Wert
: das ist das, was der Benutzer in das Eingabefeld eingegeben hat (oder ein Standardwert)
Sie können auch ein Info-Attribut
hinzufügen, das als Tooltip dargestellt wird und für eine ausführlichere Erläuterung des Zwecks des Eingabefeldes verwendet werden kann.
Darüber hinaus gibt es viele spezifische Attribute, die von der verwendeten Eingabeart abhängen.
Es gibt fortgeschrittene Techniken, mit denen Sie die Benutzeroberfläche des Konfigurationspanels verbessern können. So ist es beispielsweise möglich, mehrere Eingabefelder in wiederholbaren, zusammenklappbaren Containern zusammenzufassen oder bestimmte Eingabefelder nur bedingt anzuzeigen. Dies wird in den folgenden Beispielen gezeigt.
Verarbeitung ausgewählter Werte
Sobald Sie die Konfiguration definiert haben, ist es an der Zeit, die konfigurierten Werte in Ihre Komponente einzubetten. Sie können auf die Werte der Konfigurationsdatei mit §{ ... }§.
Innerhalb der Klammern können Sie die Punktnotation verwenden, um auf das Konfigurationsobjekt zuzugreifen. Diese Platzhalter werden in einem Vorkompilierungsschritt durch die konfigurierten Werte ersetzt.
Sie könnten zum Beispiel eine Zuordnung erstellen, um den Wert "Titel", den wir im obigen Beispiel definiert haben, in Ihr Schritt-Layout aufzunehmen, und zwar so:
<mapping>
<ui_element name="Topic">
<param name="content">§{ tab1.title_text.value }§</param>
</ui_element>
</mapping>
Tipps und Tricks: Ein typischer Fehler beim Zugriff auf die Konfiguration ist es, das abschließende ".value" zu vergessen.
Sie können auch Hilfsfunktionen verwenden, um Ihre Konfiguration zu implementieren. Diese Funktionen helfen Ihnen, viele Dinge zu tun, wie z. B.:
- Regeln implementieren, die dem Workflow nur bedingt auf der Grundlage der Konfiguration hinzugefügt werden
- Schleife durch konfigurierte Werte und Erstellen von Regeln/Aktionen/... für jeden Wert
- die konfigurierten Werte für die weitere Verarbeitung manipulieren
Schauen wir uns auch ein Beispiel für die Verwendung von Helfern an:
§{#each tab2.options.value}§
§{#unless @last}§
<rule id="opt_§{key}§">
<expression><![CDATA[ #{event:command} == '§{value}§' ]]></expression>
<actions>
<action ref="set_command"/>
</actions>
</rule>
§{/unless}§
§{#if @last}§
§{#if (and tab2.use_all.value (compare (collection tab2.options.value "size") ">" 1))}§
<rule id="special_opt_§{key}§">
<expression><![CDATA[ #{event:command} == '§{value}§' ]]></expression>
<actions>
<action ref="special_action"/>
</actions>
</rule>
§{/if}§
§{/if}§
§{/each}§
Hier gibt es einige wichtige Punkte zu beachten:
- Das
#
vor den Hilfsfunktionen ist erforderlich und signalisiert, dass es einen Rahmen bildet und sich auf einen Codeblock bezieht. - Sie können Hilfsfunktionen verschachteln und sie werden von innen nach außen ausgewertet. In diesem Beispiel gibt die
Auflistungshilfe
eine Größe von 3 zurück, die dann mit 1 verglichen
wird, was zu true führt. Nach der Anwendung eines "logischen und" auf dieses Ergebnis und den Wert der Checkbox-Eingabe erhalten wir einen endgültigen booleschen Wert für unsere if-Helferfunktion. - Es gibt eine Reihe von automatisch gesetzten Variablen in einigen Blockhelfern, die Ihnen helfen, Ihre Position in Arrays/Maps von Daten zu verwalten. Diese sind
@first
, @last
, @index
und @key
.
Nach der Vorkompilierung mit der obigen Beispielkonfiguration würde dies zu folgendem Ergebnis führen:
<rule id="opt_option1">
<expression><![CDATA[ #{event:command} == 'Option 1' ]]></expression>
<actions>
<action ref="set_command"/>
</actions>
</rule>
<rule id="opt_option2">
<expression><![CDATA[ #{event:command} == 'Option 2' ]]></expression>
<actions>
<action ref="set_command"/>
</actions>
</rule>
<rule id="special_opt_option3">
<expression><![CDATA[ #{event:command} == 'Option 3' ]]></expression>
<actions>
<action ref="special_action"/>
</actions>
</rule>
Beispiele
Die folgenden zwei Beispiele zeigen, wie Sie Ihre Komponentenkonfiguration übersichtlicher gestalten können.
Gruppierung von Eingabefeldern
Sie können mehrere Eingabefelder mit dem inputType "container" gruppieren. Dies sorgt für eine gewisse visuelle Klarheit und ermöglicht auch Funktionen wie das Duplizieren einer Gruppe von Elementen.
Hier sind einige spezifische Attribute und ihre Beschreibungen.
containerGroup: Unterscheidet zwischen verschiedenen Arten von Gruppen. Dies kann verwendet werden, um Container im Workflow-Markup zu übergehen.
wiederholbar: Ermöglicht es dem Benutzer, Kopien einer Gruppe zu erstellen. Diese können separat geändert werden, was die Implementierung von wiederholbaren Elementen ermöglicht.
zusammenklappbar: Damit kann die Gruppe minimiert werden, so dass nur noch der Titel angezeigt wird.
löschbar: Entfernt einen Container aus der Konfiguration. Er wird automatisch für kopierte Container gesetzt und sollte nicht für Basiscontainer verwendet werden.
bearbeitbar: Ermöglicht es dem Benutzer, den Titel des Containers zu ändern.
"base_sensor": {
"title": "Sensor 1",
"inputType": "container",
"containerGroup": "sensors",
"repeatable": true,
"collapsible": true,
"deleteable": false,
"editable": true,
"value": {
"sensor_shown": {
"title": "Value Shown",
"inputType": "checkbox-input",
"value": false,
"showIfComputed": true
},
"sensor_type": {
"inputType": "file-upload",
"title": "Icon",
"accept": "image/png",
"multiple": false,
"value": "",
"showIfComputed": true
},
"sensor_unit": {
"title": "Unit",
"inputType": "textinput",
"value": "rpm",
"showIfComputed": true
},
"sensor_json_path": {
"title": "JSON Path ",
"inputType": "textinput",
"value": "rpm",
"showIfComputed": true
}
},
"showIfComputed": true,
"container_editing": false,
"container_opened": true
}
Bedingte Anzeige von Eingabefeldern
Mit dem Attribut "showIf" können Sie eine Bedingung festlegen, unter der ein Eingabefeld ein- oder ausgeblendet werden soll. Nehmen wir zum Beispiel an, dass Ihre Komponente eine optionale Funktion hat, die detailliert konfiguriert werden kann. Wenn die Funktion überhaupt nicht verwendet wird, möchten Sie die detaillierten Konfigurationsparameter nicht anzeigen.
Schauen wir uns ein Beispiel an:
{
"Camera":{
"use_camera":{
"title": "Use Device Camera",
"inputType": "checkbox-input",
"value": "false"
},
"zoom_level":{
"title": "Zoom Level",
"inputType": "dropdown-input",
"showIf": "root.Camera.use_camera.value",
"value": { "name": 1 },
"elements": [
{
"name": 1
},
{
"name": 2
},
{
"name": 3
}
]
},
"show_zoom_level": {
"title": "Show Zoom Level",
"inputType": "checkbox-input",
"showIf": "root.Camera.use_camera.value && root.Camera.zoom_level.value.name > 1",
"value": "false"
},
"timeout":{
"title": "Camera Timeout (ms)",
"showIf": "root.Camera.use_camera.value",
"inputType": "textinput",
"value": 5000
}
}
}
Nachstehend sehen Sie die erwartete Ausgabe.
Da das Kontrollkästchen auf false gesetzt ist, werden alle anderen Eingabefelder nicht angezeigt. Wäre das Kontrollkästchen auf true gesetzt, würden alle Felder bis auf das Kontrollkästchen "Zoomstufe anzeigen" angezeigt, da dieses Kontrollkästchen nur angezeigt wird, wenn die Zoomstufe größer als eins ist:
Zuweisung
Aufgabe 1:
- Fügen Sie Konfigurationsfelder für den Titel, das Bild und den Text der beiden Schaltflächen hinzu.
- Zeigen Sie die Bilder nur an, wenn für beide Schaltflächen ein Bild vorhanden ist. Wenn nur für eine Schaltfläche ein Bild konfiguriert ist, wird nur der Text angezeigt.
Download-Komponente (Pre-Assignment)
Hilfe und Ressourcen
Zugriff auf die Konfiguration in Layoutdateien:
Innerhalb der Attribute von Layout-Tags können Sie wie immer auf die Konfiguration zugreifen:
<Button Name="§{ ... }§" .../>
Für Blockhilfsfunktionen müssen Sie jedoch ein <Script>
wie dieses verwenden:
<Script>§{#if ...}§</Script>
<Button .../>
<Script>§{/if}§</Script>
Wichtig ist auch, dass Sie den <Script>-Tag
nicht in allen anderen Tags verwenden können. Nachstehend ein ungültiges Beispiel:
<Button>
<Script>§{#...}§</Script>
...
<Script>§{/...}§</Script>
</Button>
Wenn schließlich Ihre Bedingungen zu einem Syntaxfehler Duplicate unique value
führen, weil ein Elementname zweimal vorhanden ist, Sie aber sicher sind, dass nach der Vorkompilierung jeweils nur einer von ihnen existiert, können Sie den Syntaxfehler ignorieren.
Lösung
Nachfolgend finden Sie eine Beispiellösung für das Layout:
<LayoutModel Name="ChoiceScreen" Page="DefaultMaster" Orientation="Vertical">
<Content PlaceHolder="Content" Weight="1" Orientation="Horizontal">
<Script>§{#if (and (compare configuration.leftImage.value.image.value "!=" "") (compare configuration.rightImage.value.image.value "!=" ""))}§</Script>
<Button Name="§{configuration.leftImage.value.text.value}§" FocusOrder="0" Weight="0.5" Style="ImageButtonStyle">
<Image Name="LEFT_IMAGE" Weight="0.8" Margin="0,0,0,0" Content="§{configuration.leftImage.value.image.value}§" ScaleType="CenterCrop"/>
<Text Name="LEFT_TEXT" Style="FooterButtonTextStyle" Weight=".2" MaxSize="30" Content="§{configuration.leftImage.value.text.value}§"/>
<Events/>
</Button>
<Script>§{else}§</Script>
<Button Name="§{configuration.leftImage.value.text.value}§" FocusOrder="0" Weight="0.5" Style="ImageButtonStyle">
<Text Name="LEFT_TEXT" Style="FooterButtonTextStyle" Weight="1" MaxSize="30" Content="§{configuration.leftImage.value.text.value}§"/>
<Events/>
</Button>
<Script>§{/if}§</Script>
<Script>§{#if (and (compare configuration.leftImage.value.image.value "!=" "") (compare configuration.rightImage.value.image.value "!=" ""))}§</Script>
<Button Name="§{configuration.rightImage.value.text.value}§" FocusOrder="1" Weight="0.5" Style="ImageButtonStyle">
<Image Name="RIGHT_IMAGE" Weight="0.8" Margin="0,0,0,0" Content="§{configuration.rightImage.value.image.value}§" ScaleType="CenterCrop"/>
<Text Name="RIGHT_TEXT" Style="FooterButtonTextStyle" Weight=".2" MaxSize="30" Content="§{configuration.rightImage.value.text.value}§"/>
<Events/>
</Button>
<Script>§{else}§</Script>
<Button Name="§{configuration.rightImage.value.text.value}§" FocusOrder="1" Weight="0.5" Style="ImageButtonStyle">
<Text Name="RIGHT_TEXT" Style="FooterButtonTextStyle" Weight="1" MaxSize="30" Content="§{configuration.rightImage.value.text.value}§"/>
<Events/>
</Button>
<Script>§{/if}§</Script>
</Content>
</LayoutModel>
Download-Komponente (Post-Assignment)
Der Zugriff auf die Konfiguration aus Layoutdateien wie dieser kann unübersichtlich sein. Wenn Sie wieder in eine ähnliche Situation geraten, könnte es sich lohnen, einen Blick auf unser Wildcard-Widget-UI-Element zu werfen. Dieses Element kann während der Laufzeit dynamisch bearbeitet werden.
Eine Lösung mit dem Wildcard-Widget würde folgendermaßen aussehen:
Zunächst würden Sie zwei PartTemplates
mit den Zwei-Schaltflächen-Varianten erstellen.
<PartTemplate Name="OptionButtonsWithImage" Orientation="Horizontal">
<Button Name="§{configuration.rightImage.value.text.value}§" FocusOrder="0" Weight="0.5" Style="ImageButtonStyle">
<Image Name="RIGHT_IMAGE" Weight="0.8" Margin="0,0,0,0" Content="§{configuration.rightImage.value.image.value}§" ScaleType="CenterCrop"/>
<Text Name="RIGHT_TEXT" Style="FooterButtonTextStyle" Weight=".2" MaxSize="30" Content="§{configuration.rightImage.value.text.value}§"/>
<Events/>
</Button>
<Button Name="§{configuration.leftImage.value.text.value}§" FocusOrder="1" Weight="0.5" Style="ImageButtonStyle">
<Image Name="LEFT_IMAGE" Weight="0.8" Margin="0,0,0,0" Content="§{configuration.leftImage.value.image.value}§" ScaleType="CenterCrop"/>
<Text Name="LEFT_TEXT" Style="FooterButtonTextStyle" Weight=".2" MaxSize="30" Content="§{configuration.leftImage.value.text.value}§"/>
<Events/>
</Button>
</PartTemplate>
<PartTemplate Name="OptionButtonsText" Orientation="Horizontal">
<Button Name="§{configuration.leftImage.value.text.value}§" FocusOrder="0" Weight="0.5" Style="ImageButtonStyle">
<Text Name="LEFT_TEXT" Style="FooterButtonTextStyle" Weight="1" MaxSize="30" Content="§{configuration.leftImage.value.text.value}§"/>
<Events/>
</Button>
<Button Name="§{configuration.rightImage.value.text.value}§" FocusOrder="1" Weight="0.5" Style="ImageButtonStyle">
<Text Name="RIGHT_TEXT" Style="FooterButtonTextStyle" Weight="1" MaxSize="30" Content="§{configuration.rightImage.value.text.value}§"/>
<Events/>
</Button>
</PartTemplate>
Ihr LayoutModel
enthält nur das "WildcardWidget".
<LayoutModel Name="ChoiceScreen" Page="DefaultMaster" Orientation="Vertical">
<Content PlaceHolder="Content" Weight="1" Orientation="Horizontal">
<WildcardWidget Name="Options" PartTemplateName="OptionButtonWithImage" Weight="1"/>
</Content>
</LayoutModel>
Schließlich legen Sie im Workflow das PartTemplate fest, das Sie auf der Grundlage der Konfiguration verwenden möchten:
<mapping>
<ui_element name="Options">
<param name="parttemplatename">§{#if (and (compare configuration.leftImage.value.image.value "!=" "") (compare configuration.rightImage.value.image.value "!=" ""))}§OptionButtonsWithImage§{else}§OptionButtonsText§{/if}§</param>
</ui_element>
</mapping>
Auf diese Weise vermeiden Sie Syntaxfehler und Sonderfälle beim Zugriff auf die Konfiguration aus den Layoutdateien.
Lösung mit Wildcard Widget herunterladen (Post-Assignment).