En este punto, tenemos un componente decente. Sin embargo, si queremos compartirlo con otros (especialmente con los no desarrolladores), su utilidad es limitada. Solo ofrecerá la posibilidad de elegir entre manzanas y peras. Si bien esto fue perfecto para nuestro primer cliente (probablemente una fábrica de pasteles de frutas), la empresa automotriz en la que estamos trabajando a continuación probablemente no utilizará la realidad aumentada para ayudar a los trabajadores a elegir frutas en la sala de descanso. Es hora de hacer que nuestros componentes sean más reutilizables y configurables.
Ya sabes cómo configurar los componentes. Al seleccionar un componente en el diagrama de flujo de flujo de trabajo, aparecerá el panel de configuración de componentes en el lado derecho del diagrama de flujo de flujo de trabajo con las opciones de configuración.
Hacer que los componentes sean configurables consta de dos partes, definir lo que el usuario puede configurar y procesar los valores seleccionados.
Las opciones de configuración que desea ofrecer se definen en formato JSON. Los usuarios pueden elegir entre una variedadde tipos de entrada, como la entrada de texto simple, la entrada de casillas de verificación y la entrada de carga de archivos.
A continuación se muestra un ejemplo de configuración.
{ "tab1": { "title_text": { "title": "Título", "inputType": "textinput", "value": "Por favor, seleccione" } }, "tab2": { "options": { "title": "Opciones", "inputType": "map-input", "placeholder": { "key": "Clave de opción", "value": "Título de la opción" }, "value": [ { "key": "option1", "value": "Opción 1" }, { "key": "option2", "value": "Opción 2" }, { "key": "option3", "value": "Opción 3" } ] }, "use_all": { "title": "Ofrecer la última opción", "inputType": "checkbox-input", "value": "true" } } }
Los objetos en el nivel raíz del archivo JSON son las pestañas del panel de configuración (su clave se mostrará como el título de la pestaña). Cada objeto de campo de entrada tiene al menos tres atributos:
title
que es el título que se muestra encima del campo de entrada en el panel de configuracióninputType
que es el tipo de campo de entrada que desea que se muestrevalue
: que es lo que el usuario ingresó en el campo de entrada (o un valor predeterminado)También puede agregar un info
atributo, que se representará como información sobre herramientas y se puede usar para una explicación más detallada del propósito del campo de entrada.
Además, muchos atributos específicos son posibles según el tipo de entrada utilizado.
Existen técnicas avanzadas disponibles que le permiten mejorar la interfaz de usuario del panel de configuración. Por ejemplo, es posible agrupar varios campos de entrada en contenedores repetibles y contraíbles, o mostrar ciertos campos de entrada solo condicionalmente. Estos se mostrarán en los siguientes ejemplos.
Una vez definida la configuración, es el momento de incrustar los valores configurados en el componente. Puede acceder a los valores del archivo de configuración mediante §{ ... }§
. Dentro de los corchetes, puede usar la notación de puntos para acceder al objeto de configuración. Estos marcadores de posición se sustituyen por los valores configurados en un paso previo a la compilación.
Por ejemplo, puede crear una asignación para incluir el valor "Título" que definimos en el ejemplo anterior en el diseño de su paso de la siguiente manera:
<mapping> <ui_element name="Topic"> <param name="content">§{ tab1.title_text.value }§</param> </ui_element> </mapping>
Consejos y trucos: Un error típico al acceder a la configuración es olvidar el ".value" de cierre.
También puede utilizar funciones auxiliares para implementar la configuración. Estas funciones te ayudan a hacer muchas cosas, como:
Veamos también un ejemplo de uso de ayudantes:
§{#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}§
Hay un par de puntos importantes a tener en cuenta aquí:
#
función delante de las auxiliares es obligatoria y señala que enmarca y hace referencia a un bloque de código.collection
aplicación auxiliar devuelve un tamaño de 3 que luego compare
es d a 1, lo que da como resultado true. Después de aplicar un "y lógico" en este resultado y el valor de la entrada de la casilla de verificación, obtenemos un valor booleano final para nuestra función if-helper.@first
, @last
y @index
@key
.Después de la precompilación con la configuración de ejemplo anterior, esto daría como resultado:
<rule id="opt_option1"> <expression><![ CDATA[ #{event:command} == 'Opción 1' ]]></expression> <actions> <action ref="set_command"/> </actions> </rule> <rule id="opt_option2"> <expression><![ CDATA[ #{event:command} == 'Opción 2' ]]></expression> <actions> <action ref="set_command"/> </actions> </rule> <rule id="special_opt_option3"> <expression><![ CDATA[ #{event:command} == 'Opción 3' ]]></expression> <actions> <action ref="special_action"/> </actions>< /rule>
Los dos ejemplos siguientes muestran formas avanzadas de facilitar la comprensión de la configuración de los componentes.
Puede agrupar varios campos de entrada utilizando el "contenedor" inputType. Esto permite cierta claridad visual y también permite funcionalidades como la duplicación de un grupo de elementos.
Estos son algunos atributos específicos y sus descripciones.
"base_sensor": { "title": "Sensor 1", "inputType": "container", "containerGroup": "sensors", "repeatable": true, "collapsible": true, "deleteable": false, "editable": true, "value": { "sensor_shown": { "title": "Valor mostrado", "inputType": "checkbox-input", "value": false, "showIfComputed": true }, "sensor_type": { "inputType": "file-upload", "title": "Icono", "accept": "image/png", "multiple": false, "value": "", "showIfComputed": true }, "sensor_unit": { "title": " Unit", "inputType": "textinput", "value": "rpm", "showIfComputed": true }, "sensor_json_path": { "title": "Ruta JSON", "inputType": "textinput", "value": "rpm", "showIfComputed": true } }, "showIfComputed": true, "container_editing": false, "container_opened": verdadero }
Es posible utilizar el atributo "showif" para definir una condición bajo la cual se debe mostrar u ocultar un campo de entrada. Supongamos, por ejemplo, que el componente tiene una función opcional que se puede configurar en detalle. Si la función no se usa en absoluto, no querrá mostrar los parámetros de configuración detallados.
Veamos un ejemplo:
{ "Camera":{ "use_camera":{ "title": "Usar la cámara del dispositivo", "inputType": "checkbox-input", "value": "false" }, "zoom_level":{ "title": "Nivel 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": "Mostrar nivel de zoom", "inputType": "checkbox-input", "showIf": "raíz. Camera.use_camera.value && root. Camera.zoom_level.value.name > 1", "value": "false" }, "timeout":{ "title": "Tiempo de espera de la cámara (ms)", "showIf": "root. Camera.use_camera.value", "inputType": "textinput", "value": 5000 } } }
A continuación puede ver el resultado esperado.
Dado que la casilla de verificación está establecida en false, no se muestran todos los demás campos de entrada. Si fuera cierto, aparecerían todas las casillas de verificación excepto "Mostrar nivel de zoom", ya que esa casilla de verificación solo aparece si el nivel de zoom es superior a uno:
Descargar componente (pre-asignación)
Dentro de los atributos de las etiquetas de diseño, puede acceder a la configuración como siempre:
<Nombre del botón="§{ ... } §" .../>
Sin embargo, para las funciones auxiliares de bloques, tendrá que usar un <Script>
archivo como este:
<Script>§{#if ...} §</Script> <Button .../> <Script>§{/if}§</Script>
También es importante tener en cuenta que no podrá usar la <Script>
etiqueta dentro de todas las demás etiquetas. A continuación se muestra un ejemplo no válido :
<Botón> <Script>§{#...} §</Guión> ... <Script>§{/...} §</Script> </Botón>
Por último, si los condicionales dan lugar a un error Duplicate unique value
de sintaxis, porque el nombre de un elemento existe dos veces, pero está seguro de que solo uno de ellos existirá a la vez después de la precompilación, puede ignorar el error de sintaxis.
A continuación se muestra una solución ejemplar para el diseño:
<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"/> <Nombre del texto="LEFT_TEXT" Estilo="FooterButtonTextStyle" Peso=".2" MaxSize="30" Content="§{configuración.leftImage.valor.texto.valor}§"/> <Eventos/> </Botón> <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 "!=" "") (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"/> <Nombre del texto="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>
Descargar componente (posterior a la asignación)
Acceder a la configuración desde archivos de diseño como este puede ser complicado. Si vuelve a encontrarse con una situación similar, podría valer la pena echar un vistazo a nuestro elemento de interfaz de usuario de widget comodín. Este elemento se puede editar dinámicamente durante el tiempo de ejecución.
Una solución con el widget comodín se vería así:
En primer lugar, crearía dos PartTemplate
s con las variantes de dos botones.
<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"> <Nombre de la imagen="LEFT_IMAGE" Peso="0.8" Margen="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}§"/> < Eventos/> </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>
Solo LayoutModel
incluye el "WildcardWidget".
<LayoutModel Name="ChoiceScreen" page="DefaultMaster" orientation="Vertical"> <Content PlaceHolder="Content" weight="1" orientation="Horizontal"> <WildcardWidget name="Opciones" PartTemplateName="OptionButtonWithImage" weight="1"/> </Content> </LayoutModel>
Por último, en el flujo de trabajo, establezca la PartTemplate que desea usar en función de la configuración:
<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 esta manera, evitaría errores de sintaxis y casos especiales al acceder a la configuración desde los archivos de diseño.
Descargar componente con widget de comodín (posterior a la asignación)