À ce stade-ci, nous avons une composante décente. Cependant, si nous voulons le partager avec d’autres (en particulier des non-développeurs), son utilité est limitée. Il n’offrira qu’un choix entre les pommes et les poires. Bien que cela ait été 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. Lors de la sélection d’un composant dans l’organigramme du flux de travail, le panneau de configuration du composant s’affiche sur le côté droit de l’organigramme du flux de travail avec les options de configuration.

La configuration des composants se compose de deux parties, la définition de ce que l’utilisateur peut configurer et le traitement des 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 plusieurs typesd’entrée, tels que la saisie de texte simple, la saisie de case à cocher et l’entrée de téléchargement de fichiers.

Voici un exemple de configuration.

{
  "tab1 » : {
        "title_text » : {
            "title » : « title »,  
            "inputType » : « textinput »,
            "value » : « Veuillez sélectionner"
        }
  },
  "tab2 » : {
        "options » : {
            "title » : « Options »,
            "inputType » : « map-input »,
            "placeholder » : {
                "key » : « Touche d’option »,
                "value » :  « Titre de l’option"},
            "value » : [
                {
                    "key » : « option1 »,
                    "value » : « Option 1"
                },
                {
                    "key » : « option2 »,
                    "value » : « Option 2"
                },
                {
                    "key » : « option3 »,
                    "value » : « Option 3"
                }
            ]
        },
         "use_all » : {
             "title » : « Proposer la dernière option »,
             
            « inputType » : « checkbox-input »,
             "value » : « true"
         }
  }
}

Les objets au niveau racine du fichier JSON sont les onglets du panneau de configuration (leur clé sera affichée comme titre de l’onglet). Chaque objet de champ de saisie possède au moins trois attributs :

  • a title qui est le titre affiché au-dessus du champ de saisie dans le panneau de configuration
  • Un inputType champ de saisie qui correspond au type de champ de saisie que vous souhaitez afficher
  • a value: ce que l’utilisateur a saisi dans le champ de saisie (ou une valeur par défaut)

Vous pouvez également ajouter un info attribut, qui sera affiché sous forme d’info-bulle et pourra être utilisé pour une explication plus détaillée de l’objectif du champ de saisie.

De plus, de nombreux attributs spécifiques sont possibles en fonction du type d’entrée utilisé.

Il existe des techniques avancées qui vous 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 reproductibles et réductibles, ou d’afficher certains champs de saisie uniquement de manière conditionnelle. Ceux-ci seront illustrés dans les exemples suivants.

Traitement des valeurs sélectionnées

Une fois que vous avez défini la configuration, il est temps d’intégrer les valeurs configurées dans votre composant. Vous pouvez accéder aux valeurs du fichier de configuration à l’aide de §{ ... }§. À l’intérieur des parenthèses, 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 « Titre » que nous avons définie dans l’exemple ci-dessus dans votre disposition d’étape comme ceci :

<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 le « .value » de fermeture.

Vous pouvez également utiliser des fonctions d’assistance pour implémenter votre configuration. Ces fonctions vous aident à faire beaucoup de choses, comme :

  • Implémenter des règles qui ne sont ajoutées au workflow que de manière conditionnelle en fonction de la configuration
  • Parcourez les valeurs configurées et créez des règles/actions/... pour chaque valeur
  • Manipuler les valeurs configurées pour un traitement ultérieur

Regardons également un exemple d’utilisation de l’assistant :

§{#each tab2.options.value}§
    §{#unless @last}§<
    rule id="opt_§{clé}§">
        <expression>< ![ CDATA[ #{event :command} == '§{valeur}§' ]]></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} == '§{valeur}§' ]]></expression>
            <actions>
                <action ref="special_action"/>
            </actions>
        </rule>    
    §§{/if}§
    §{/if}§
§{/each}§

Il y a quelques points importants à noter ici :

  • Le # devant de l’assistant est requis et signale qu’il encadre et fait référence à un bloc de code.
  • Vous pouvez imbriquer des fonctions d’assistance et elles sont évaluées de l’intérieur. Dans cet exemple, l’assistant collection renvoie une taille de 3 qui est ensuite compared à 1, ce qui donne true. Après avoir appliqué un « et logique » à ce résultat et à la valeur de la case à cocher, nous obtenons une valeur booléenne finale pour notre fonction if-helper.
  • Il y a plusieurs variables définies automatiquement à l’intérieur de certains assistants de bloc 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, cela se traduirait par :

    <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 méthodes avancées pour faciliter la compréhension de la configuration de vos composants.

Regroupement des champs de saisie

Vous pouvez regrouper plusieurs champs de saisie à l’aide du type d’entrée « 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 leurs descriptions.

  • container group : différencie les différents types de groupes. Cela peut être utilisé pour passer en revue les conteneurs dans le balisage du flux de travail.
  • repeatable : permet à l’utilisateur de créer des copies d’un groupe. Ceux-ci peuvent être modifiés séparément, ce qui permet la mise en œuvre d’éléments reproductibles.
  • réductible : permet de réduire le groupe, en ne laissant que le titre à afficher.
  • deletable : 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 » : « Capteur 1 »,
            "inputType » : « container »,
            "containerGroup » : « sensors »,
            "repeatable » : true,
            "collapsible » : true,
            "deleteable » : false,
            "editable » : true,
            "value » : {
                "sensor_shown » : {
                    "title » : « Valeur affichée »,
                    "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 » : « Chemin JSON « ,"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 sous laquelle un champ de saisie doit être affiché ou masqué. Supposons, par exemple, que votre composant dispose d’une fonction facultative qui peut être configurée en détail. Si la fonctionnalité n’est pas utilisée du tout, vous ne souhaitez pas afficher les paramètres de configuration détaillés.

Prenons un exemple :

{
  "Camera » :{
    "use_camera » :{
      "title » : « Utiliser l’appareil photo de l’appareil »,"inputType » :
      « checkbox-input »,
      "value » : « false"
    },
    "zoom_level » :{
      "title » : « Niveau de zoom »,
      "inputType » : « dropdown-input »,
      "showIf » : « root. Camera.use_camera.value »,
      "value » : { « name » : 1 },
      "elements » : [
        {
          "name » : 1
        },
        {
          "name » : 2
        },
        {
          "name » : 3
        }
      ]
    },
    "show_zoom_level » : {
      "title » : « Afficher le niveau de zoom »,
        "inputType » : « checkbox-input »,
        « showIf » : « racine. Camera.use_camera.value && root. Camera.zoom_level.value.name > 1 »,
        "value » : « false"
    },
    "timeout » :{
      "title » : « Délai d’expiration de la caméra (ms) »,
      "showIf » : « root. Camera.use_camera.value »,
      "inputType » : « textinput »,
      "value » : 5000
    }
  }
}

Ci-dessous, vous pouvez voir le résultat attendu.

Étant donné que la case à cocher est définie sur false, tous les autres champs de saisie ne sont pas affichés. Si c’était vrai, toutes les cases à cocher sauf la case « Afficher le niveau de zoom » apparaîtraient, car cette case à cocher n’apparaît que si le niveau de zoom est supérieur à un :

📌Mission

  • Ajoutez des champs de configuration pour le titre, ainsi que l’image et le texte sur les deux boutons.
  • Afficher les images uniquement s’il existe une image pour les deux boutons. Si une seule image est configurée sur un seul bouton, n’affichez que le texte.

 Télécharger le composant (pré-affectation)

Aide et ressources

Accès à la configuration dans les fichiers de mise en page :

Dans les attributs des balises de mise en page, vous pouvez accéder à la configuration comme toujours :

<Nom du bouton="§{ ... } §" .../>

Pour les fonctions d’aide aux blocs, cependant, vous devrez utiliser un <Script> objet comme celui-ci :

<Script>§{#if ...} §</Script>
<Bouton .../>
<Script>§{/if}§</Script>

Il est également important de noter que vous ne pourrez pas utiliser la <Script> balise dans toutes les autres balises. Vous trouverez ci-dessous un exemple non valide :

<Bouton>
<Script>§{#...} §</Script>
...
<Script>§{/...} §</Script>
</Bouton>

Enfin, si vos conditions entraînent une erreur Duplicate unique valuede syntaxe, 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 un exemple de solution pour la mise en page :

<LayoutModel Name="ChoiceScreen » Page="DefaultMaster » Orientation="Vertical">
  <Content PlaceHolder="Content » Weight="1 » Orientation="Horizontal">
    <Script>§{#if (et (comparer configuration.leftImage.value.image.value « != » «  ») (compare configuration.rightImage.value.image.value.value « != » «  »))}§</Script>
    <Nom du bouton="§{configuration.leftImage.value.text.value}§ » FocusOrder="0 » Weight="0.5 » Style="ImageButtonStyle">
      <Nom de l’image="LEFT_IMAGE » Weight="0.8 » Margin="0,0,0,0 » Content="§{configuration.leftImage.value.image.value}§ » ScaleType="CenterCrop"/>
      <Nom du texte="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">
      <Nom du texte="LEFT_TEXT » Style="FooterButtonTextStyle » Weight="1 » MaxSize="30 » Content="§{configuration.leftImage.value.text.value}§"/>
      <Events/>
    </Button>
    <Script>§{/if}§</Script>

    <Script>§{#if (et (comparer configuration.leftImage.value.image.value « != » «  ») (comparer configuration.rightImage.value.image.value « != » «  ») )}§</script>
    <nom du bouton="§{configuration.rightImage.value.text.value}§ » FocusOrder="1 » poids="0.5 » style="ImageButtonStyle">
      <nom de l’image="RIGHT_IMAGE » poids="0.8 » marge="0,0,0,0 » content="§{configuration.rightImage.value.image.valeur}§ » scaleType="Recadrage central"/>
      <Nom du texte="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">
      <Nom du texte="RIGHT_TEXT » Style="FooterButtonTextStyle » Weight="1 » MaxSize="30 » Content="§{configuration.rightImage.value.text.value}§"/>
      <Events/>
    </Button>
    <Script>§{/if}§</Script>

  </Content>
</LayoutModel>

 Télécharger le composant (post-affectation)

L’accès à la configuration à partir de fichiers de mise en page comme celui-ci peut être désordonné. Si vous rencontrez à nouveau une situation similaire, il peut être intéressant de jeter un coup d’œil à notre élément d’interface utilisateur de widget générique. Cet élément peut être modifié dynamiquement pendant l’exécution.

Une solution avec le widget générique 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">
    <Nom de l’image="RIGHT_IMAGE » Weight="0.8 » Margin="0,0,0,0 » Content="§{configuration.rightImage.value.image.value}§ » ScaleType="CenterCrop"/>
    <Nom du texte="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">
    <Nom de l’image="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>
</ Modèle de pièce >

Votre LayoutModel inclut simplement 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 workflow, 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>

De cette façon, vous éviterez les erreurs de syntaxe et les cas particuliers lors de l’accès à la configuration à partir des fichiers de mise en page.

 Télécharger le composant avec widget générique (post-affectation)