web-dev-qa-db-fra.com

Utilisation de Angular Material mat-autocomplete without Reactive Form

J'essaie d'utiliser <mat-autocomplete> from Angular Material (not AngularJS) without using a reactive form. Mais tous leurs exemples utilisent des formes réactives ...

Ce que j'essaye de faire:
1. quand quelque chose est tapé dans le mat-input, faites un appel Ajax pour récupérer une liste d'utilisateurs
2. Afficher la liste des utilisateurs dans la saisie semi-automatique (afficher le nom de l'utilisateur), mais stocker l'utilisateur en tant que modèle
3. Lorsque le modèle change, appelez une fonction de mon choix

Pour l'instant je fais ces choses folles (je dis folles parce que ça semble trop).

<mat-form-field fxLayout>
  <input type="text"
             matInput fxFlex="100"
             [(ngModel)]="listFilterValue"
             (keyup)="memberInputChanged(input.value)"
             (change)="memberChanged()"
             *ngIf="isAutocompleteNeeded()"
             #input
             [matAutocomplete]="auto">
</mat-form-field>

<mat-autocomplete #auto="matAutocomplete" [displayWith]="getMemberAsStr">
       <mat-option *ngFor="let member of members | async" [value]="member">
          {{ getMemberAsStr(member) }}
       </mat-option>
</mat-autocomplete>

Pour l'instant, il n'y a que console.log dans mon JS pour voir ce qui est appelé, avec quelle valeur donc je ne le partage pas ici. Suis-je en train d'utiliser les bons attributs, la bonne logique?

(la propriété members dans mon composant est un Rxjs BehaviourSubject)

Ce que je fais pour l'instant ne fonctionne pas car listFilterValue n'est jamais défini sur rien.

9
Valentin Coudert

Vous devez éviter d'appeler des méthodes dans le modèle, cela pourrait potentiellement planter votre navigateur lorsqu'elles sont appelées à chaque détection de changement, voir: * ngPour exécuter une boucle infinie dans angular2 Techniquement, ce n'est pas une boucle infinie, mais vous obtenir le point :)

Comme pour ne pas avoir de contrôle de formulaire avec la saisie semi-automatique n'est pas très différent, vous échangez simplement le contrôle de formulaire à une variable à la place, vous pouvez utiliser un formulaire piloté par modèle si vous le souhaitez, ou pas du tout un formulaire. Voici avec le formulaire piloté par modèle:

La démo JSON utilisée ici ressemble à ceci:

"value": [
  {
    "id": 409,
    "joke": "some joke here",
    "categories": []
  }
]
<form #f="ngForm">
  <mat-form-field>
    <input matInput [matAutocomplete]="auto" 
           name="joke" #jokeField="ngModel" 
           [(ngModel)]="currentJoke" (ngModelChange)="doFilter()">
  </mat-form-field>

  <mat-autocomplete #auto="matAutocomplete">
    <mat-option *ngFor="let joke of jokes | async" [value]="joke.joke">
        {{joke.joke}}
    </mat-option>
  </mat-autocomplete>
</form>

Et votre TS pourrait ressembler à:

doFilter() {
  this.jokes = this.service.getData()
    .pipe(
      map(jokes => this.filter(jokes.value)),
  )
}

filter(values) {
  return values.filter(joke => 
    // used 'includes' here for demo, you'd want to probably use 'indexOf'
    joke.joke.toLowerCase().includes(this.currentJoke))
}

Le service aurait alors une variable, où nous stockons les données api après la première extraction, de sorte que nous n'appellerons pas l'api à chaque coup de clé. Lorsque les données sont retournées, nous vérifions si la variable a été remplie, si c'est le cas, nous retournons un observable de cela:

jokes = [];

getData() {
  return this.jokes.length ? of(this.jokes)
    : this.httpClient.get<any>('https://api.icndb.com/jokes/random/5').pipe(
      map((data) => {
        this.jokes = data.value;
        return this.jokes;
      })
    )
}

N'oubliez pas d'effacer le tableau jokes si vous vous éloignez.

Démo StackBlitz

17
AJT82