Implémentation des boîtes de dialogue de formulaire Upsert dans Angular

Dec 06 2022
De nombreuses applications nécessitent la possibilité de créer et de mettre à jour des enregistrements via l'interface utilisateur. Habituellement, nous voudrons utiliser le même composant de dialogue pour les deux opérations.

De nombreuses applications nécessitent la possibilité de créer et de mettre à jour des enregistrements via l'interface utilisateur. Habituellement, nous voudrons utiliser le même composant de dialogue pour les deux opérations. Le terme «upsert» est une combinaison des mots «update» et «insert», et il fait référence au fait que la boîte de dialogue peut être utilisée pour créer un nouvel enregistrement ou mettre à jour un enregistrement existant.

Voyons comment nous pouvons implémenter cette fonctionnalité. Je vais utiliser la ngneat/dialogbibliothèque, mais vous pouvez utiliser ce que vous préférez. Tout d'abord, nous allons créer une interface partagée pour le dataque nous devons transmettre à notre boîte de dialogue :

export interface UpsertDialogData<T> {
  action: 'add' | 'edit';
  actions$: Record<
    UpsertDialogData['type'],
    (value: T) => Observable<any>
  >;
  currentValue: T | undefined;
}

Créons maintenant le upsertcomposant de base de la boîte de dialogue :

import { DialogRef } from '@ngneat/dialog';
import { loadingFor } from '@ngneat/loadoff';
import { HotToastService } from '@ngneat/hot-toast';

@Directive()
export abstract class UpsertDialogComponent<Entity extends Record<string, any>>
  implements OnInit
{
  private toast = inject(HotToastService);
  loader = loadingFor('upsert');
  ref: DialogRef<UpsertDialogData<Entity>> = inject(DialogRef);

  abstract get form(): FormGroup;

  abstract getMessages(): Record<UpsertDialogData['action'], string>;

  get action() {
    return this.ref.data.action;
  }

  ngOnInit() {
    if (this.ref.data.action === 'edit') {
      this.form.patchValue(this.ref.data.currentValue);
    }
  }

  upsert() {
    if (this.form.invalid) return;

    this.ref.data.actions$[this.action](this.form.getRawValue())
      .pipe(this.loader.upsert.track())
      .subscribe(() => {
        this.ref.close();
        this.toast.success(this.getMessages()[this.action]);
      });
  }
}

Utilisons notre classe de base et créons une boîte de dialogue pour mettre à jour un utilisateur :

import { errorTailorImports } from '@ngneat/error-tailor';

@Component({
  standalone: true,
  imports: [errorTailorImports],
  templateUrl: `./upsert-user-dialog.component.html`
})
export class UpsertUserDialogComponent extends UpsertDialogComponent<User> {

  form = inject(FormBuilder).nonNullable.group({
    name: ['', Validators.required],
  });

  getMessages() {
    return {
      add: `User was added successfully`,
      edit: `User was updated successfully`,
    };
  }
}
<header>
  <h3>{{ action === 'edit' ? 'Edit User' : 'New User' }}</h3>
</header>

<div>
  <form [formGroup]="form" errorTailor>
     <input
        placeholder="Name"
        formControlName="name" />
  </form>
</div>

<footer>
  <button dialogClose>Cancel</button>
  <button
    [loading]="loader.upsert.inProgress$ | async"
    (click)="upsert()"
  >
    {{ action === 'edit' ? 'Save' : 'Create' }}
  </button>
</footer>

@Component({
  template: `
    <button (click)="openUpsertUserDialog('add')">Add User</button>

     <-- ngFor -->
    <button (click)="openUpsertUserDialog('edit', user)">Edit</button>
  `
})
export class UsersPageComponent {

  openUpsertUserDialog(
    action: UpsertDialogData['action'],
    currentValue?: UpsertDialogData<User>['currentValue']
  ) {
    this.dialogService.open(UpsertUserDialogComponent, {
      data: {
        action,
        currentValue,
        actions$: {
          add: (user) => this.usersService.addUser({ user}),
          edit: (user) => this.usersService.updateUser({ user }),
        },
      },
    });
  }

}