À ce stade, nous disposons d'un composant assez intéressant. Toutefois, si nous voulons le transmettre à d'autres (en particulier à des non-développeurs), son utilité est très limitée. Il n'offrira qu'un choix entre des pommes et des poires. Alors que c'était parfait pour notre premier client (probablement une usine de gâteaux aux fruits), l'entreprise automobile sur laquelle nous travaillons ensuite n'utilisera probablement pas la réalité augmentée pour aider les travailleurs à choisir des fruits dans la salle de pause. Il est temps de rendre nos composants plus réutilisables et configurables.
Vous savez déjà comment configurer les composants. Lorsque vous cliquez sur un composant, une barre latérale s'affiche avec les options de configuration. Rendre les composants configurables consiste en deux parties : définir ce que l'utilisateur peut configurer et traiter les valeurs sélectionnées.
Définition des options de configuration
Les options de configuration que vous souhaitez proposer sont définies au format JSON. Les utilisateurs peuvent choisir parmi une série detypes de saisie, tels que la saisie de texte simple, la saisie de cases à cocher et la saisie de fichiers à télécharger.
Voici un exemple de configuration.
{
"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"
}
}
}
Les objets situés à la racine du fichier JSON sont les onglets du panneau de configuration (leur clé sera affichée en tant que titre de l'onglet). Chaque objet champ de saisie possède au moins trois attributs :
- un
titre
qui est l'intitulé affiché au-dessus du champ de saisie dans le panneau de configuration - un
inputType
qui est le type de champ de saisie que vous souhaitez afficher - une
valeur
: c'est ce que l'utilisateur a saisi dans le champ de saisie (ou une valeur par défaut)
Vous pouvez également ajouter un attribut info
, qui sera affiché sous forme d'infobulle et pourra être utilisé pour une explication plus détaillée de l'objectif du champ de saisie.
En outre, il existe de nombreux attributs spécifiques qui dépendent du type d'entrée utilisé.
Il existe des techniques avancées qui permettent d'améliorer l'interface utilisateur du panneau de configuration. Par exemple, il est possible de regrouper plusieurs champs de saisie dans des conteneurs répétables et pliables, ou d'afficher certains champs de saisie de manière conditionnelle. Ces techniques seront présentées dans les exemples suivants.
Traitement des valeurs sélectionnées
Une fois la configuration définie, il est temps d'intégrer les valeurs configurées dans votre composant. Vous pouvez accéder aux valeurs du fichier de configuration en utilisant §{ ... }§.
À l'intérieur des crochets, vous pouvez utiliser la notation par points pour accéder à l'objet de configuration. Ces espaces réservés sont remplacés par les valeurs configurées lors d'une étape de précompilation.
Par exemple, vous pouvez créer un mappage pour inclure la valeur "Title" que nous avons définie dans l'exemple ci-dessus dans votre structure d'étape comme suit :
<mapping>
<ui_element name="Topic">
<param name="content">§{ tab1.title_text.value }§</param>
</ui_element>
</mapping>
Trucs et astuces: Une erreur typique lors de l'accès à la configuration est d'oublier la fermeture ".value".
Vous pouvez également utiliser des fonctions d'aide pour mettre en œuvre votre configuration. Ces fonctions vous aident à faire beaucoup de choses, comme par exemple :
- mettre en œuvre des règles qui ne sont ajoutées au flux de travail que de manière conditionnelle, en fonction de la configuration.
- boucler à travers les valeurs configurées et créer des règles/actions/... pour chaque valeur
- manipuler les valeurs configurées en vue d'un traitement ultérieur
Prenons également un exemple d'utilisation de l'aide :
§{#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}§
Il y a quelques points importants à noter ici :
- Le
#
devant les fonctions d'aide est obligatoire et signale qu'il encadre et fait référence à un bloc de code. - Vous pouvez imbriquer les fonctions d'aide et elles sont évaluées de l'intérieur vers l'extérieur. Dans cet exemple, la fonction d'aide
collection
renvoie une taille de 3 qui est ensuite comparée
à 1, ce qui donne true. Après avoir appliqué un "et logique" à ce résultat et à la valeur de l'entrée de la case à cocher, nous obtenons une valeur booléenne finale pour notre fonction d'aide if. - Il existe un certain nombre de variables automatiquement définies à l'intérieur de certains assistants de bloc et qui vous aident à gérer votre position dans les tableaux/cartes de données. Il s'agit de
@first
, @last
, @index
et @key
.
Après la précompilation avec l'exemple de configuration ci-dessus, le résultat serait le suivant :
<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>
Exemples
Les deux exemples suivants montrent des moyens avancés de rendre la configuration de vos composants plus facile à comprendre.
Regroupement des champs de saisie
Vous pouvez regrouper plusieurs champs de saisie en utilisant le type de saisie "container". Cela permet une certaine clarté visuelle et permet également des fonctionnalités telles que la duplication d'un groupe d'éléments.
Voici quelques attributs spécifiques et leur description.
containerGroup : Différencie les différents types de groupes. Il peut être utilisé pour passer en revue les conteneurs dans le balisage du flux de travail.
répétable : Permet à l'utilisateur de créer des copies d'un groupe. Celles-ci peuvent être modifiées séparément, ce qui permet la mise en œuvre d'éléments répétables.
repliable : Cette option permet de réduire le groupe et de n'afficher que le titre.
supprimable : Supprime un conteneur de la configuration. Il est défini automatiquement pour les conteneurs copiés et ne doit pas être utilisé pour les conteneurs de base.
modifiable : Permet à l'utilisateur de modifier le titre du conteneur.
"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
}
Affichage conditionnel des champs de saisie
Il est possible d'utiliser l'attribut "showIf" pour définir une condition d'affichage ou de masquage d'un champ de saisie. Supposons, par exemple, que votre composant dispose d'une fonction optionnelle qui peut être configurée en détail. Si la fonction n'est pas utilisée, vous ne voudrez pas afficher les paramètres de configuration détaillés.
Prenons un exemple :
{
"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
}
}
}
Vous pouvez voir ci-dessous le résultat attendu.
Comme la case à cocher est réglée sur false, tous les autres champs de saisie ne sont pas affichés. Si elle était vraie, tous les champs s'afficheraient sauf la case à cocher "Afficher le niveau de zoom", puisque celle-ci n'apparaît que si le niveau de zoom est supérieur à un :
Affectation
Affectation 1 :
- Ajoutez des champs de configuration pour le titre, ainsi que pour l'image et le texte des deux boutons.
- N'affichez les images que s'il existe une image pour les deux boutons. Si une image n'est configurée que pour un seul bouton, seul le texte est affiché.
Composant de téléchargement (mission préalable)
Aide et ressources
Accès à la configuration dans les fichiers de présentation :
Dans les attributs des balises de mise en page, vous pouvez accéder à la configuration comme toujours :
<Button Name="§{ ... }§" .../>
Pour les fonctions d'aide au blocage, cependant, vous devrez utiliser un <Script>
comme celui-ci :
<Script>§{#if ...}§</Script>
<Button .../>
<Script>§{/if}§</Script>
Il est également important de noter que vous ne pourrez pas utiliser la balise <Script>
à l'intérieur de toutes les autres balises. L'exemple ci-dessous n'est pas valable:
<Button>
<Script>§{#...}§</Script>
...
<Script>§{/...}§</Script>
</Button>
Enfin, si vos conditionnelles entraînent une erreur de syntaxe Duplicate unique value
, parce qu'un nom d'élément existe deux fois mais que vous êtes sûr qu'un seul d'entre eux existera à la fois après la précompilation, vous pouvez ignorer l'erreur de syntaxe.
Solution
Vous trouverez ci-dessous une solution exemplaire pour la mise en page :
<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>
Composant de téléchargement (après l'affectation)
L'accès à la configuration à partir de fichiers de mise en page comme celui-ci peut s'avérer compliqué. Si vous vous retrouvez à nouveau dans une situation similaire, il peut être intéressant de jeter un coup d'œil à notre élément d'interface utilisateur "widget". Cet élément peut être modifié dynamiquement au cours de l'exécution.
Une solution avec le widget de caractères génériques ressemblerait à ceci :
Tout d'abord, vous devez créer deux PartTemplates
avec les variantes à deux boutons.
<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>
Votre LayoutModel
ne comprend que le "WildcardWidget".
<LayoutModel Name="ChoiceScreen" Page="DefaultMaster" Orientation="Vertical">
<Content PlaceHolder="Content" Weight="1" Orientation="Horizontal">
<WildcardWidget Name="Options" PartTemplateName="OptionButtonWithImage" Weight="1"/>
</Content>
</LayoutModel>
Enfin, dans le flux de travail, vous définissez le PartTemplate que vous souhaitez utiliser en fonction de la configuration :
<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>
Vous éviterez ainsi les erreurs de syntaxe et les cas particuliers lorsque vous accéderez à la configuration à partir des fichiers de mise en page.
Télécharger la solution avec le widget Wildcard (après la mission).