it-swarm.com.de

Angular2: Wie lade ich Daten vor dem Rendern der Komponente?

Ich versuche, ein Ereignis aus meiner API zu laden, bevor die Komponente gerendert wird. Derzeit verwende ich meinen API-Service, den ich über die Funktion ngOnInit der Komponente aufrufe.

Meine EventRegister Komponente:

import {Component, OnInit, ElementRef} from "angular2/core";
import {ApiService} from "../../services/api.service";
import {EventModel} from '../../models/EventModel';
import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig, RouteParams, RouterLink} from 'angular2/router';
import {FORM_PROVIDERS, FORM_DIRECTIVES, Control} from 'angular2/common';

@Component({
    selector: "register",
    templateUrl: "/events/register"
    // provider array is added in parent component
})

export class EventRegister implements OnInit {
    eventId: string;
    ev: EventModel;

    constructor(private _apiService: ApiService, 
                        params: RouteParams) {
        this.eventId = params.get('id');
    }

    fetchEvent(): void {
        this._apiService.get.event(this.eventId).then(event => {
            this.ev = event;
            console.log(event); // Has a value
            console.log(this.ev); // Has a value
        });
    }

    ngOnInit() {
        this.fetchEvent();
        console.log(this.ev); // Has NO value
    }
}

Meine EventRegister Vorlage

<register>
    Hi this sentence is always visible, even if `ev property` is not loaded yet    
    <div *ngIf="ev">
        I should be visible as soon the `ev property` is loaded. Currently I am never shown.
        <span>{{event.id }}</span>
    </div>
</register>

Mein API-Service

import "rxjs/Rx"
import {Http} from "angular2/http";
import {Injectable} from "angular2/core";
import {EventModel} from '../models/EventModel';

@Injectable()
export class ApiService {
    constructor(private http: Http) { }
    get = {
        event: (eventId: string): Promise<EventModel> => {
            return this.http.get("api/events/" + eventId).map(response => {
                return response.json(); // Has a value
            }).toPromise();
        }     
    }     
}

Die Komponente wird gerendert, bevor der API-Aufruf in der Funktion ngOnInit das Abrufen der Daten abgeschlossen hat. Daher sehe ich die Ereignis-ID nie in meiner Ansichtsvorlage. Es sieht also so aus, als wäre dies ein ASYNC-Problem. Ich habe erwartet, dass die Bindung der Komponente ev (EventRegister) nach dem Festlegen der Eigenschaft ev funktioniert. Leider wird das mit *ngIf="ev" markierte div nicht angezeigt, wenn die Eigenschaft festgelegt wird.

Frage: Wende ich einen guten Ansatz an? Wenn nicht; Was ist der beste Weg, um Daten zu laden, bevor mit dem Rendern der Komponente begonnen wird?

HINWEIS: In diesem angle2 tutorial wird der Ansatz ngOnInit verwendet.

BEARBEITEN:

Zwei mögliche Lösungen. Zuerst musste ich fetchEvent loswerden und nur den API-Dienst in der Funktion ngOnInit verwenden.

ngOnInit() {
    this._apiService.get.event(this.eventId).then(event => this.ev = event);
}

Zweite Lösung. Wie die Antwort gegeben.

fetchEvent(): Promise<EventModel> {
    return this._apiService.get.event(this.eventId);
}

ngOnInit() {
    this.fetchEvent().then(event => this.ev = event);
}
114
Tom Aalbers

update

Original

Wenn console.log(this.ev) nach this.fetchEvent(); ausgeführt wird, bedeutet dies nicht, dass der fetchEvent() -Aufruf abgeschlossen ist, sondern nur, dass er geplant ist. Wenn console.log(this.ev) ausgeführt wird, wird der Aufruf an den Server nicht einmal ausgeführt und hat natürlich noch keinen Wert zurückgegeben.

Ändern Sie fetchEvent(), um Promise zurückzugeben.

 fetchEvent(){
    return  this._apiService.get.event(this.eventId).then(event => {
        this.ev = event;
        console.log(event); // Has a value
        console.log(this.ev); // Has a value
    });
 }

ändern Sie ngOnInit(), um zu warten, bis Promise abgeschlossen ist

ngOnInit() {
    this.fetchEvent().then(() =>
    console.log(this.ev)); // Now has value;
}

Dies wird Ihnen eigentlich nicht viel für Ihren Anwendungsfall kaufen.

Mein Vorschlag: Wickeln Sie Ihre gesamte Vorlage in ein <div *ngIf="isDataAvailable"> (template content) </div>

und in ngOnInit()

isDataAvailable:boolean = false;

ngOnInit() {
    this.fetchEvent().then(() =>
    this.isDataAvailable = true); // Now has value;
}
110

Eine gute Lösung, die ich gefunden habe, ist auf der Benutzeroberfläche etwas zu tun wie:

<div *ngIf="isDataLoaded">
 ...Your page...
</div

Nur wenn: isDataLoaded wahr ist, wird die Seite gerendert.

44
user2965814

Sie können Ihre Daten vorab abrufen , indem Sie Resolver in Angular2 + , Resolver verwenden Verarbeiten Sie Ihre Daten, bevor Ihre Komponente vollständig geladen ist.

In vielen Fällen möchten Sie Ihre Komponente nur laden, wenn bestimmte Dinge geschehen. Navigieren Sie beispielsweise nur dann zum Dashboard, wenn die Person bereits angemeldet ist. In diesem Fall sind Resolver sehr praktisch.

Schauen Sie sich das einfache Diagramm an, das ich für Sie erstellt habe, um zu erfahren, wie Sie die Daten mit dem Resolver an Ihre Komponente senden können.

enter image description here

Das Anwenden von Resolver auf Ihren Code ist ziemlich einfach. Ich habe die Snippets erstellt, damit Sie sehen, wie der Resolver erstellt werden kann:

import { Injectable } from '@angular/core';
import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
import { MyData, MyService } from './my.service';

@Injectable()
export class MyResolver implements Resolve<MyData> {
  constructor(private ms: MyService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<MyData> {
    let id = route.params['id'];

    return this.ms.getId(id).then(data => {
      if (data) {
        return data;
      } else {
        this.router.navigate(['/login']);
        return;
      }
    });
  }
}

und im Modul :

import { MyResolver } from './my-resolver.service';

@NgModule({
  imports: [
    RouterModule.forChild(myRoutes)
  ],
  exports: [
    RouterModule
  ],
  providers: [
    MyResolver
  ]
})
export class MyModule { }

und Sie können darauf in Ihrer Komponente wie folgt zugreifen:

/////
 ngOnInit() {
    this.route.data
      .subscribe((data: { mydata: myData }) => {
        this.id = data.mydata.id;
      });
  }
/////

Und in der Route etwa so (normalerweise in der Datei app.routing.ts):

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyResolver}}
////
37
Alireza