Custom operations: como usá-lo para manusear dados da interface

Neste artigo você vai ver:

Quando falamos de desenvolvimento front-end, um dos principais desafios que devs enfrentam é o de garantir que componentes e/ou telas tenham uma comunicação entre si. Isso também inclui tipos de operações, sejam elas padrão (também chamadas de default operations) ou customizadas (as custom operations). 

Se tivermos, por exemplo, um campo de CPF inserido em uma tela de login, é necessário que haja um recurso que encaminhe o dado inserido no campo correspondente (no caso, o número de CPF) para ser autenticado.

Neste artigo, vamos te apresentar recursos para você criar e configurar suas próprias operações customizadas utilizando um framework de desenvolvimento multiplataforma, ou seja, que em uma só ferramenta há suporte para aplicações Android, iOS e Web.

Artigo escrito pelos zuppers: Joao Jaco Santos Abreu, Jamesson Miranda da Penha e Vinicius Guardieiro Sousa.

Contextualizando sobre operações no Beagle

No nosso caso, iremos utilizar o Beagle framework como ferramenta de exemplo. Para quem não conhece, o Beagle é nosso projeto open source de desenvolvimento cross-platform pautado no paradigma de implementação Server Driven UI. 

Para que seja possível a comunicação entre componentes, nós temos no Beagle o recurso de Context (ou Contexto). Essa feature é responsável por armazenar valores de qualquer tipo que podem ser recuperados por widgets de uma parte da tela, por toda a tela ou por qualquer tela do ciclo de vida do aplicativo. Com isso, temos um ganho enorme para nossa aplicação.

Dentro do contexto do Beagle, as operações entram como uma maneira de manipular os contextos pelo BFF (sigla para back-end for front-end). Em outras palavras, é por meio da configuração das operações que você pode alterar a forma como os dados são validados pelo contexto ou realizar alguma operação aritmética ou lógica específicas.

Tipos de operações no Beagle 

Operações Padrão (Default Operations)

É o conjunto de operações que, de tão recorrentes no desenvolvimento de aplicações, são disponibilizadas de forma default na biblioteca do Beagle. São elas: 

  • Operações aritméticas (soma, subtração, divisão e multiplicação); 
  • Operações lógicas; 
  • Operações de comparação; 
  • Manipulação de strings;
  • Manipulação de arrays; 
  • Algumas operações específicas, como “isNull”, “isEmpty” e “lenght”;

Operações Customizadas (Custom operations)

As Custom operations são as operações que permitem ações customizadas, criando desde operações simples como funções aritméticas, até mais complexas como a validação de um conteúdo ou a ordenação de uma lista.

Usar Custom operations é muito útil nas rotinas de desenvolvimento e é bem tranquilo de fazer uma. Acompanhe:

Como criar Custom operations

Para você desenvolver uma Custom operations, basta criar as expressões no nosso front-end, independente de estarmos falando de uma aplicação Android, iOS ou Web.

No exemplo a seguir, que desenvolvemos para cada sistema operacional, vamos simular a criação de uma operação customizada que valida uma senha digitada pelo usuário que contém como requisito ser composta por, ao menos, seis caracteres, sendo eles:

  • Um número (1-9);
  • Uma letra (a-z);
  • Um caractere especial (exemplo: !@#$).

Android

Para criar uma Custom operations no Android, precisamos criar uma classe que implemente a interface Operation e anotá-la com “RegisterOperation”, passando um campo name, que indicará o nome da operação.

Ao fazer isso, iremos sobrescrever a função execute, que recebe como parâmetro um vararg do tipo OperationType.

Podemos recuperar o valor passado fazendo “params[0]?.value”. Por isso, manipulamos esse valor (no caso do exemplo, recebemos uma string e verificamos se ela coincide com um Regex) e retornamos um resultado do tipo OperationType, como podemos ver no exemplo abaixo:

@RegisterOperation(name = "isValidPassword")
class IsValidPassword : Operation {
   override fun execute(vararg params: OperationType?): OperationType {
       var isValidPassword: Boolean
       try {
           isValidPassword = (params[0]?.value as String)
               .matches(
                   Regex(
                       "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{6,}$"
                   )
               )

       } catch (e: Exception) {
           isValidPassword = false
       }

       return OperationType.TypeBoolean(isValidPassword)
   }
}

iOS

Para criarmos uma Custom operations no iOS, podemos criar uma função estática responsável por registrar toda e qualquer operação customizada que quisermos criar.

Essa função terá um parâmetro dependencies do tipo BeagleDependencies, que contém todas as dependências do framework. Isso permite que ela possa ser chamada posteriormente pela classe de configuração do Beagle criada pelo usuário.

Vamos ver em mais detalhes a seguir:

static func registerOperations(dependencies: BeagleDependencies) { 
 // register your operations here
  }

O registro da operação é feito por meio da função operationsProvider.register(operationId, operationHandler), que pode ser acessada pelas dependências. Por isso, a variável dependencies. O id da operação, ou OperationId, e operationHandler é a operação em si, uma função que pode ser passada como parâmetro.

static func registerOperations(dependencies: BeagleDependencies) {
        dependencies.operationsProvider.register(operationId: "isValidPassword", handler: isValidPassword())
    }

Vamos criar, então, o método chamado isValidPassword, que vai conter a lógica da nossa operação. 

Para isso, podemos retornar um bloco de código que utiliza um argumento chamado parameters, e que será a senha enviada pelo back-end através do contexto. Esse argumento parameters é uma lista de valores do tipo DynamicObject, um tipo de dados específico do Beagle que representa um json.

Para nós, o que importa é o tipo string, por esse motivo que devemos checar se o valor do enum é um .string(String). Dessa forma, esse valor será compatível com a expressão regular que iremos criar.

guard parameters.count == 1,
                  case .string(let password) = parameters[0]
            else { return nil }

Uma vez feito isso, agora devemos criar nossa expressão regular, que irá buscar por um determinado padrão na string, garantindo que a senha atenda aos requisitos que estipulamos para o exemplo (no caso, o mínimo de seis caracteres, contendo pelo menos um número, uma letra e um caracter especial):

let regex = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{6,}$"

Depois fazemos a busca pela regex que criamos, utilizando a função range() do Swift e um if ternário para retornar como verdadeiro caso encontremos o padrão e falso caso ele não seja encontrado. 

Tendo esse resultado booleano em mãos, conseguimos utilizar a operação default condition lá no BFF e dizer se a senha digitada é válida ou não. No final o código ficaria assim:

static func isValidPassword() -> OperationHandler {
        return { parameters in
            guard parameters.count == 1,
                  case .string(let password) = parameters[0]
            else { return nil }
            let regex = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{6,}$"
            return password.range(of: regex, options: .regularExpression) != nil ? true : false
        }
    }

Web

Para criarmos uma Custom operations na Web, primeiro vamos abrir uma nova pasta chamada Operations. Dentro dela, iremos criar um novo arquivo chamado validPassword.ts, no qual devemos adicionar uma function com o nome IsValidPassword.

O código ficará assim:

export function isValidPassword(value: string) {
    try {
      const rgx = new RegExp("^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{6,}$")
      return rgx.test(value)
    } catch (e) {
      return false
    }
  }

Essa função (function) será responsável por criar uma operação (no caso, operation) que irá executar a validação toda vez que for alterado o value do input de senha. Legal, né?

Operação customizada para Angular

No caso do framework para Angular, vamos adicionar nossa operação dentro de customOperations no arquivo beagle.module.ts. Seu conteúdo ficará assim:

import { BeagleModule } from '@zup-it/beagle-angular'
import { isValidPassword } from './operations/validPassword'
// import all the components you wish to use with Beagle.

@BeagleModule({
  baseUrl: 'http://localhost:8080/home',
  module: {
    path: './beagle-components.module',
    name: 'BeagleComponentsModule',
  },
  components: {
    // Associate every beagle component to your angular component.
  },
  customOperations: {
    isValidPassword
  }
})
export class Beagle { }

Operação customizada para React

No caso do framework para React, vamos adicionar nossa operação dentro de customOperations no arquivo beagle-service.ts. Seu conteúdo ficará assim:

import { createBeagleUIService } from '@zup-it/beagle-react'
import { isValidPassword } from '../operations/validPassWord'

export default createBeagleUIService({
  baseUrl: 'http://localhost:8080/home',
  components: {},
  customOperations: {
    isValidPassword
  }
})

Como usar o Custom operations no back-end

Depois de registrarmos as Custom operations no front-end, já podemos utilizá-las no back-end. Para isso, basta que seu componente esteja preparado para utilizar o Contexto, já que é o local onde nossas operações serão utilizadas, além do componente ter um atributo do tipo Bind.

Veja o exemplo do componente Text do Beagle:

data class Text(
   val text: Bind<String>,
   val styleId: String? = null,
   val textColor: Bind<String>? = null,
   val alignment: Bind<TextAlignment>? = null
) : Widget()


Nesse caso, tanto o atributo text quando o textColor e o alignment aceitam contexto. Agora, no momento de criarmos nossa tela, só precisamos “chamar” nosso Custom operations. Para isso, basta fazermos:

“@{isValidPassword(password)}”

Obs: password é o contexto que estamos armazenando a senha digitada pelo usuário em um TextInput, que será utilizado como parâmetro na nossa operação.

Podemos combinar diferentes operações para termos um resultado final. No exemplo de tela que criamos para validação de senha, combinamos a operação default de lógica “condition” com “isValidPassword”, tendo como resultado:

“@{condition(isValidPassword(password), 'Senha válida.', 'Senha inválida.')}”


Com isso, se a senha do usuário for válida, “isValidPassword” retornará verdadeiro e consequentemente “condition” irá retornar “Senha válida”. Caso for falso, “condition” irá retornar “Senha inválida”.

O exemplo completo da tela é:

object IsValidPasswordBuilder : ScreenBuilder {
   override fun build() = Screen(
           navigationBar = NavigationBar(
                   title = "IsValidPassword Operation"
           ),
           child = Container(
                   children = listOf(
                           TextInput(
                                   placeholder = "senha",
                                   type = TextInputType.PASSWORD,
                                   onChange = listOf(
                                           SetContext(
                                                   contextId = "password",
                                                   value = "@{onChange.value}"
                                           )
                                   )
                           ).setStyle { margin = EdgeValue.only(bottom = 10) },
                           Text(text = "Informe uma senha de no mínimo 6 caracteres com pelo menos uma letra," +
                                   " um número e um caractere especial (@$!%*#?&)."
                           ).setStyle { margin = EdgeValue.only(bottom = 10) },
                           Text(text = "@{condition(isValidPassword(password), 'Senha válida.', 'Senha inválida.')}")
                   ),
                   context = ContextData(
                           id = "password",
                           value = ""
                   )
           ).setStyle {
               margin = EdgeValue.only(left = 20, right = 20, top = 10)
           }.setFlex {
               alignContent = AlignContent.CENTER
           }
   )
}

Custom operations – mais praticidade no desenvolvimento

Depois de todas essas configurações, você já pode criar suas próprias Custom operations e, assim, aumentar o poder de suas aplicações! 

O que achou dessa ideia do nosso tutorial de operações customizadas? Se quiser conhecer outras funcionalidades do nosso projeto open source, fica aqui o convite para você dar uma olhada no nosso fórum ou conferir o projeto do Beagle pelo GitHub.

Referências:

Capa do artigo operações customizadas (Custom operations) no Beagle que contem um homem negro codando.
avatarblog
Conteúdo
Equipe de conteúdo da Zup, focada em promover o compartilhamento de conhecimento.

Artigos relacionados

Capa do artigo sobre Docker, com um homem negro programando.
DevOps
Postado em:
Capa do artigo sobre Tech Radar, onde vemos 3 circulos, um dentro do outro, dando a ideia de aneis, no canto esquerdo. A imagem é toda na cor verde claro, mas é possível ver as sombras que formam os círculos..
Desenvolvimento
Postado em:

Este site utiliza cookies para proporcionar uma experiência de navegação melhor. Consulte nossa Política de Privacidade.