Padronizando o comportamento dos formulários no projeto (Angular)



Para criar uma interface amigável, você precisa garantir que todos os formulários em seu aplicativo se comportem de maneira consistente. O comportamento monótono é freqüentemente alcançado por código repetitivo, embora de forma implícita. Deixe-me compartilhar um esboço de um padrão que eu acho que simplifica o desenvolvimento e padroniza o comportamento do formulário.



Se o código para enviar formulários em seu projeto for semelhante a este, aconselho você a procurar em cat.



onSubmit (): void
// login.component.ts
// bad practices
onSubmit(): void {
  this.formSubmitted = true;
  this.isUnhandledServerError = false;
  if (!this.formGroup.valid) return;
  this.isLoading = true;
  const { username, password } = this.formGroup.value;
  this.login(username, password)
    .pipe(finalize(() => (this.isLoading = false)))
    .subscribe({ error: error => this.handleError(error) });
}




Para aqueles que adoram código:

Projete no stackblitz antes de refatorar.

Projeto Stackblitz após refatoração.



Descrição do problema



Os formulários requerem que muitas nuances sejam levadas em consideração. Do ponto de vista funcional, o formulário apenas envia as informações inseridas pelo usuário para o servidor. Mas para garantir uma UX de alta qualidade, além de tudo, você tem que fazer a validação, exibir os erros do servidor, um indicador de carregamento, etc. Na prática, esses detalhes são muitas vezes esquecidos pelos desenvolvedores, o que afeta negativamente a usabilidade do aplicativo ou resulta em duplicação de código e torna o desenvolvimento de formulários uma rotina intolerável.



Aqui está um exemplo de um manipulador de envio de formulário que é bom do ponto de vista da experiência do usuário, mas ruim do ponto de vista do desenvolvimento. Projeto Stackblitz antes da refatoração.



// login.component.ts
onSubmit(): void {
  this.formSubmitted = true; //   
  this.isUnhandledServerError = false; //       
  if (!this.formGroup.valid) return; //  
  this.isLoading = true; //   
  const { username, password } = this.formGroup.value;
  this.login(username, password) //    
    .pipe(finalize(() => (this.isLoading = false))) //   
    .subscribe({ error: error => this.handleError(error) });
}


Como você pode ver, esse manipulador leva em consideração muitos detalhes que compõem a UX. O único problema é que, com essa abordagem, essas nuances terão que ser escritas para cada formulário do aplicativo.



Decisão



Para simplificar o desenvolvimento e padronizar o comportamento dos formulários em seu aplicativo, você precisa mover o código do manipulador de envio do formulário para uma classe separada. Projeto Stackblitz após refatoração. (Eu simplifiquei intencionalmente o código do exemplo; em um projeto real, você precisa substituir todos os campos booleanos por Observáveis.)



class Form<T> {
    submitted = false;

    pending = false;

    hasUnhandledServerError = false;

    constructor(private formGroup: FormGroup, private action: (value: any) => Observable<T>) {}

    submit(): Observable<T> {
        if (this.pending) return EMPTY;
        this.submitted = true;
        this.hasUnhandledServerError = false;
        if (this.formGroup.valid) {
            this.pending = true;
            return this.action(this.formGroup.value).pipe(
                tap({ error: () => (this.hasUnhandledServerError = true) }),
                finalize(() => (this.pending = false)),
            );
        }
        return EMPTY;
    }
}


Assim, concentramos a maioria dos recursos de UX em uma classe e nos livramos da lógica duplicada. Agora, escrever um novo formulário levará menos tempo e você pode concluir o comportamento dos formulários em todo o aplicativo, alterando apenas a classe Form.



Por que não colocá-lo na biblioteca?



Os requisitos de UX para cada projeto são únicos e mais dependentes do designer. Já tive que substituir o comportamento dos elementos materiais padrão a pedido do cliente. Portanto, não vejo nenhuma maneira de padronizar o comportamento dos formulários em todos os aplicativos que usam uma biblioteca. Deixe o comportamento da interface ficar à mercê do designer e dos desenvolvedores. No entanto, acho uma boa ideia separar a lógica relacionada à UX em classes diferentes.



Espero que o exemplo tenha sido útil e que você tente usar a ideia em seus projetos. Enquanto!



All Articles