web-dev-qa-db-fra.com

Définir la forme réactive après le chargement des données (async) - Angular 5

J'essaie de mettre à jour les valeurs du formulaire après avoir chargé des valeurs à partir d'une API. J'ai essayé d'utiliser le *ngIf technique mais le formulaire n'est pas visible même lorsque le formulaire est défini.

Je ne suis pas en mesure de partager le projet complet, mais voici le modèle de composant et le contrôleur

modèle

<div class="page-title">
  <h3> Edit news </h3>
</div>
<div class="partner-add-form">
  <ng-container *ngIf='newsForm'>
    <form action="" [formGroup]='newsForm' (ngSubmit)="onSubmit()">
      <div class="row  ">
        <div class="input-field col s12 tooltip">
          <input formControlName='title' [id]="'title'" [type]="'text'">
          <label [for]="'title'">Title</label>
          <validator-errors [control]='newsForm.get("title")'></validator-errors>
        </div>
        <div class="input-field col s12 tooltip">
          <textarea class="materialize-textarea" formControlName='content' [id]="'content'"></textarea>
          <label [for]="'content'">Content</label>
          <validator-errors [control]='newsForm.get("content")'></validator-errors>
        </div>
        <div class="input-field col s12 margin-reset">
          <mat-form-field class="full-width">
            <mat-select [formControl]='newsForm.controls["partner_id"]'>
              <mat-option disabled selected>Categories</mat-option>
              <mat-option *ngFor="let partner of partners.data" [value]="partner.id">
              {{ partner.name }} </mat-option>
            </mat-select>
          </mat-form-field>
          <validator-errors [control]='newsForm.controls["partner_id"]'></validator-errors>
        </div>
        <div class="file-field col s12 input-field">
          <div class="btn">
            <span>File</span>
            <input (change)="fileChangeListener($event)" type="file"> </div>
          <div class="file-path-wrapper">
            <input class="file-path validate" type="text" placeholder="Upload one or more files"> </div>
        </div>
        <div class="col s12">
          <div class="flex flex-middle flex-center crop-area">
            <img-cropper #cropper [image]="data" [settings]="cropperSettings"></img-cropper>
            <i class="material-icons">arrow_forward</i>
            <div class="result rounded z-depth-1">
              <img [src]="data.image " *ngIf="data.image " [width]="cropperSettings.croppedWidth"
                [height]="cropperSettings.croppedHeight"> </div>
          </div>
        </div>
        <div class="col s12 form-bottom">
          <div class="left">
            <button type="button" onclick='window.history.back()' class='btn btn-large waves-effect waves-light '>
              <i class="material-icons">keyboard_arrow_left</i>
              <span>Back</span>
            </button>
          </div>
          <div class="right">
            <button [ngClass]="{'disabled':(newsForm['invalid']) || isSubmitting || !data.image }" type="submit" class='btn btn-large waves-effect waves-light '>
            Submit </button>
          </div>
        </div>
      </div>
    </form>
  </ng-container>
</div>

Contrôleur

  partners;

  news = {};
  newsForm: FormGroup;

  ngOnInit() {
    setTimeout(() => {
      this._dashboardService.routeChangeStarted();
    }, 0);
    this._activatedRoute.params.subscribe(params => {
      this.news["id"] = params["id"];
      this.getPartners().then(data => {
        this.getNews().then(data=>{
          this.setForm();
        })
      });
    });
  }

  setForm() {
    this.newsForm = this._formBuilder.group({
     title: [this.news['title'], [Validators.required]],
     content: [this.news['content'], [Validators.required]],
     partner_id: [this.news['partner']['id'], [Validators.required]]
    });
    console.log(new Boolean(this.newsForm));
  }

  getPartners() {
    return Promise((res, rej) => {
      setTimeout(() => {
        this._dashboardService.progressStarted();
        this._dashboardService.routeChangeStarted();
      }, 0);
      this._partnerService.getPartners().subscribe(
        partners => {
          if (partners.status == 200) {
            this.partners = partners;
            res(partners.data);
          } else {
            this._errorActions.errorHandler(partners.status);
          }
          setTimeout(() => {
            this._dashboardService.progressFinished();
            this._dashboardService.routeChangeFinished();
          }, 0);
        },
        error => {
          this._notificationService.warning("Ups, something went wrong");
        }
      );
    });
  }

  getNews() {
    setTimeout(() => {
      this._dashboardService.routeChangeStarted();
      this._dashboardService.progressStarted();
    }, 0);

    return Promise((res, rej) => {
      this._newsService.getNews(this.news["id"]).subscribe(
        data => {
          if (data["status"] == 200) {
            Object.assign(this.news, data["data"]);
            res(this.news);
          } else {
            this._errorActions.errorHandler(data["status"]);
          }
          setTimeout(() => {
            this._dashboardService.progressFinished();
            this._dashboardService.routeChangeFinished();
          }, 0);
        },
        error => {
          this._notificationService.error("Ups, something went wrong");
        }
      );
    });
  }

Quel est le problème? Il n'affiche même pas le formulaire lui-même après avoir défini le formulaire. Existe-t-il une autre façon de définir les valeurs du formulaire après le chargement des données à partir d'une API?

7
Jamil Alisgenderov

Vous pouvez créer une fonction distincte pour remplir votre formulaire avec des données. Vous pouvez appeler cette fonction après avoir obtenu des données d'une API.

Variante 1: setValue

Documentation officielle angular: setValue

Avec setValue, vous affectez chaque valeur de contrôle de formulaire à la fois en passant un objet de données dont les propriétés correspondent exactement au modèle de formulaire derrière le FormGroup.

Exemple:

updateValues(dataObject: any) {
  this.heroForm.setValue({
    name:    this.hero.name,
    address: this.hero.addresses[0] || new Address()
  });
}

Variante 2: patchValue

Documentation officielle angular: patchValue

Avec patchValue, vous pouvez affecter des valeurs à des contrôles spécifiques dans un FormGroup en fournissant un objet de paires clé/valeur uniquement pour les contrôles d'intérêt.

Exemple:

updateValues(dataObject: any) {
  this.heroForm.patchValue({
    name: this.hero.name
  });
}
17
Gregor Doroschenko

j'ai été confronté à ce problème, je ne sais pas si ma solution est la meilleure, mais ça marche. La technique consiste à utiliser un loaded: boolean que vous initiez à false et une fois que vos données sont entièrement reçues dans votre composant, vous les définissez sur true

en voici un exemple:

.html:

<div *ngIf="loaded == false">
    <h2>loading ...</h2>
</div>
<div *ngIf="loaded == true">
   // your template goes here
</div>

et dans vos .ts:

loaded: boolean = false;

// your code ....

ngOnInit() {
  setTimeout(() => {
    this._dashboardService.routeChangeStarted();
  }, 0);
  this._activatedRoute.params.subscribe(params => {
    this.news["id"] = params["id"];
    this.getPartners().then(data => {
      this.getNews().then(data=>{
        this.setForm();

        // here is the important part!
        this.loaded = true
      })
    });
  });
}
2
Flow

Vous pouvez utiliser setValue ou patchValue quelles données sont asynchrones pour vous FormGroup.

private initForm(): void {
    // For update product
    if (this.isUpdate) {
      this.productService.getProduct(this.id)
        .subscribe((product: Product) => {
          this.productForm.patchValue({
            name: product.name,
            price: product.price,
            description: product.description,
            image: product.image
          });
        });
    }
    // For create product
    this.productForm = new FormGroup({
      name: new FormControl(null, [
        Validators.required,
        Validators.minLength(8),
        Validators.maxLength(35)
      ]),
      price: new FormControl(null, [
        Validators.required,
        Validators.min(5000),
        Validators.max(50000000),
      ]),
      description: new FormControl(null, [
        Validators.required,
        Validators.minLength(8),
        Validators.maxLength(500)
      ]),
      image: new FormControl(null, Validators.required)
    });
  }
0
Nguyễn Lam Bửu