Às vezes, durante o desenvolvimento do fluxo de trabalho, as ferramentas que discutimos até este ponto não serão suficientes para implementar a lógica necessária. Normalmente, isso ocorre sempre que é necessário manipular os dados brutos de entrada, por exemplo, antes de compará-los em uma expressão ou antes de salvá-los em uma variável de contexto. Nesses casos, a opção de usar nosso mecanismo JavaScript (JS) incorporado na marcação do fluxo de trabalho deve oferecer uma solução viável.
Qualquer uso de JavaScript em um fluxo de trabalho deve estar sempre dentro de uma tag <![CDATA[ ... ]]>
para evitar erros do analisador XML. Um escopo JS é aberto e fechado usando ?{ ...
}?
. Você pode acessar variáveis de contexto de dentro do escopo JS usando context.variable_name
. Para acessar os dados do evento, você usaria da mesma forma, por exemplo, event.field
:
<setvar id="add_digit_from_speech_command">
<context_of>workflow</context_of>
<context_update>
<param name="number" type="long"><![CDATA[ ?{ context.number + Number(event.command.substr(event.command.length - 1, event.command.length)) }? ]]></param>
</context_update>
</setvar>
Como você pode ver no exemplo acima, é possível escrever JS nativo arbitrário. Isso também pode abranger várias linhas e um tratamento mais complexo. Você verá mais alguns exemplos na próxima página.
Funções incorporadas
Há algumas funções incorporadas que você pode usar diretamente no XML do fluxo de trabalho sem abrir um contexto JS. Alguns desses exemplos são usados regularmente em casos de uso específicos:
- exists: exists(#{shelve})
- toUppercase: toUppercase( '#{material_name} ' )
- toLowercase: toLowercase( '#{material_name} ' )
- trim: trim( # {material_name} )
- contains: contains( # {first_code}, #{second_code})
- startsWith: startsWith( # {first_code}, #{second_code})
- endsWith: endsWith( # {first_code}, #{second_code})
- equals: equals( # {first_code}, #{second_code})
- substring: substring( # {material_name}, 0, 3)
O exemplo abaixo contém as funções toUpperCase
, trim
e substring
.
<rule id="speak_material">
<expression><![CDATA[ #{event:command} == toUpperCase('#{material_name}') ]]></expression>
<actions>
<setvar id="set_material_type">
<context_update>
<param name="material_type" type="string">trim(substring(#{material_name}, 0, 3))</param>
</context_update>
</setvar>
</actions>
</rule>
O exemplo acima é típico, pois os comandos de fala são sempre emitidos em letras maiúsculas. Se quiser comparar um comando de fala com algum conteúdo em seu fluxo de trabalho, você deve sempre transformá-lo em letras maiúsculas também.
Dicas e truques: Não use espaço em branco para melhorar a legibilidade do código em uma tag param
do tipo string
, pois ele se tornaria parte da string que você está manipulando. Por exemplo, <param name="material_type" type="string">trim(substring(#{material_name}, 0, 3)) </param>
resultaria em, por exemplo, MAT_
(onde _ é espaço em branco), mesmo que você tenha usado trim.
Exemplo
Vamos dar uma olhada em mais alguns exemplos de casos de uso em que o mecanismo JS é útil:
Acesso dinâmico aos dados
Às vezes, é necessário acessar dados dinamicamente (com base no conteúdo de outra variável de contexto):
<setvar id="set_step">
<context_update>
<param name="step" type="object"><![CDATA[ ?{context.steps[context.current_step_index]}? ]]></param>
</context_update>
</setvar>
Manipulação condicional de dados
Embora você também possa criar várias ações e regras para implementar isso, o JS permite a brevidade:
<setvar id="set_text_color">
<context_update>
<param name="label_text_color" type="string"><![CDATA[ ?{ (context.urgent)? 'red' : 'black' }? ]]></param>
</context_update>
</setvar>
Geração e manipulação de objetos
O JavaScript também ajuda você a gerar ou manipular objetos. No exemplo abaixo, queremos permitir que o trabalhador use comandos de fala para expressar um valor de 0 a 99. Cada comando de fala precisa ser adicionado à gramática de fala separadamente, mas usando JavaScript podemos encurtar isso para:
<action id="add_amount_speech_commands" type="speech_modify_commands_in_grammar">
<param name="slot">wf_editor_slot</param>
<param name="commands"><![CDATA[ ?{
Array.apply(null, {length: 100})
.map(Number.call, Number)
.map(function(e) {
return { 'name': 'AMOUNT ' + (e), 'description': `AMOUNT [0-99]`}
}
)}? ]]></param>
<param name="modification">add_commands</param>
</action>
Em expressões
Há também casos de uso em que o JS ajuda nas expressões. No exemplo abaixo, processamos um evento que é emitido quando um dispositivo de terceiros é desconectado. Usamos o JS para pesquisar uma cadeia de endereços MAC em busca do MAC do dispositivo desconectado.
<rule id="device_vanished">
<expression><![CDATA[ #{event:command}== 'DEVICEVANISHED' && ?{context.mac.search(event.mac)>-1}?]]></expression>
<actions>
<action ref="back_to_start"/>
</actions>
</rule>
Observe que o uso do JavaScript é limitado a expressões: O conteúdo de todas as expressões será cortado para remover todas as quebras de linha e espaços em branco que possam quebrar a lógica. Portanto, você não pode usar várias linhas de JavaScript em expressões.
Widget curinga
Outro caso de uso típico do JS é o widget curinga. Como ainda não introduzimos o layout, não veremos um exemplo aqui, mas a documentação do widget tem um.
Atribuição
Atribuição 1:
Na versão atual do nosso componente, o resultado da nossa escolha está todo em maiúsculas e é mostrado como tal na interface do usuário do próximo componente. Vamos deixar a aparência um pouco mais agradável:
- Use JavaScript ou funções incorporadas para salvar a opção resultante em uma capitalização mais natural.
Componente de download
Solução
Abaixo está uma solução exemplar:
<rule id="choice_made">
<expression><![CDATA[ #{event:command} == 'APPLE' || #{event:command} == 'PEAR' ]]></expression>
<actions>
<setvar id="save_choice">
<context_of>workflow</context_of>
<context_update>
<param name="choice" type="string"><![CDATA[ ?{ event.command.slice(0,1) + event.command.slice(1).toLowerCase() }? ]]></param>
</context_update>
</setvar>
<action ref="show_confirmation_dialog"/>
</actions>
</rule>
Componente de download (pós-atribuição)
Na próxima lição, finalmente veremos como projetar a interface de usuário de um componente.