Pour créer une interface conviviale, vous devez vous assurer que tous les formulaires de votre application se comportent de manière cohérente. Un comportement monotone est souvent obtenu par un code répétitif, quoique implicitement. Permettez-moi de partager une esquisse d'un modèle qui, selon moi, simplifie le développement et normalise le comportement du formulaire.
Si le code de soumission des formulaires dans votre projet est similaire à celui-ci, je vous conseille de regarder sous 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) });
}
Pour ceux qui aiment juste le code:
Projetez sur stackblitz avant de refactoriser.
Projet Stackblitz après refactoring.
description du problème
Les formes nécessitent de nombreuses nuances pour être prises en compte. D'un point de vue fonctionnel, le formulaire envoie simplement les informations saisies par l'utilisateur au serveur. Mais pour assurer une UX de qualité, en plus de tout, il faut faire une validation, afficher les erreurs du serveur, un indicateur de chargement, etc. Dans la pratique, ces détails sont souvent négligés par les développeurs, ce qui affecte négativement la convivialité de l'application ou entraîne une duplication de code et transforme le développement de formulaires en une routine intolérable.
Voici un exemple de gestionnaire de soumission de formulaire qui est bon du point de vue UX, mais mauvais du point de vue du développement. Projet Stackblitz avant la refactorisation.
// 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) });
}
Comme vous pouvez le voir, ce gestionnaire prend en compte de nombreux détails qui composent l'UX. Le seul problème est qu'avec cette approche, ces nuances devront être écrites pour chaque formulaire de l'application.
Décision
Pour simplifier le développement et normaliser le comportement des formulaires dans votre application, vous devez déplacer le code du gestionnaire de soumission de formulaire dans une classe distincte. Projet Stackblitz après refactoring. (J'ai intentionnellement simplifié le code de l'exemple; dans un vrai projet, vous devez remplacer tous les champs booléens par Observable.)
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;
}
}
Ainsi, nous concentrons la plupart des fonctionnalités UX dans une seule classe et éliminons la logique dupliquée. Maintenant, l'écriture d'un nouveau formulaire prendra moins de temps et vous pouvez compléter le comportement des formulaires dans toute l'application en modifiant uniquement la classe Form.
Pourquoi ne pas le mettre dans la bibliothèque?
Les exigences UX pour chaque projet sont uniques et dépendent davantage du concepteur. J'ai déjà dû remplacer le comportement des éléments Material standard à la demande du client. Par conséquent, je ne vois aucun moyen de standardiser le comportement des formulaires dans toutes les applications utilisant une seule bibliothèque. Laissez le comportement de l'interface rester à la merci du concepteur et des développeurs. Cependant, je pense que c'est une bonne idée de séparer la logique liée à l'UX en classes distinctes.
J'espère que l'exemple vous a été utile et que vous essaierez d'utiliser l'idée dans vos projets. Tandis que!