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


Ich versuche, ein Ereignis von meiner API zu laden, bevor die Komponente gerendert wird. Zurzeit verwende ich meinen API-Service, den ich von der ngOnInit-Funktion der Komponente aus anrufe.

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-Dienst

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 vor dem API-Aufruf in der API gerendert ngOnInit Funktion ist fertig mit dem Abrufen der Daten. Daher kann ich die Ereignis-ID in meiner Ansichtsvorlage nicht sehen. Es sieht also so aus, als wäre das ein ASYNC-Problem. Ich erwartete die Bindung der ev (EventRegister Komponente), um nach der ev Eigenschaft wurde festgelegt. Leider zeigt es nicht die div markiert mit *ngIf="ev" wenn die Eigenschaft festgelegt wird.

Frage: Benütze ich einen guten Ansatz? Wenn nicht; Was ist der beste Weg, um Daten zu laden, bevor die Komponente zu rendern beginnt?

HINWEIS: Das ngOnInit Ansatz wird dabei verwendet angular2 Tutorial.

BEARBEITEN:

Zwei mögliche Lösungen. Zuerst war es, die fetchEvent und verwende einfach den API-Dienst in der ngOnInit Funktion.

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);
}

76
2018-02-26 15:17


Ursprung


Antworten:


aktualisieren

Original

Wann console.log(this.ev) wird nach ausgeführt this.fetchEvent();das heißt nicht das fetchEvent() Aufruf ist getan, dies bedeutet nur, dass es geplant ist. Wann console.log(this.ev) Wird ausgeführt, wird der Aufruf zum Server nicht einmal gemacht und hat natürlich noch keinen Wert zurückgegeben.

Veränderung fetchEvent() zurückgeben Promise

 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
    });
 }

Veränderung ngOnInit() auf das warten Promise fertigstellen

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

Dies wird Sie nicht wirklich 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;
}

82
2018-02-26 15:22



Eine nette 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.


24
2017-07-26 09:31



Sie können Vorabholen Ihre Daten mithilfe von Resolvern in Angular2 +Resolver verarbeiten Ihre Daten, bevor Ihre Komponente vollständig geladen wird.

Es gibt viele Fälle, in denen Sie Ihre Komponente nur dann laden möchten, wenn bestimmte Dinge passieren, zum Beispiel nur dann zu Dashboard navigieren, wenn die Person, die bereits angemeldet ist, in diesem Fall Resolver so praktisch sind.

Sehen Sie sich das einfache Diagramm an, das ich für Sie erstellt habe, damit Sie mit dem Resolver die Daten an Ihre Komponente senden können.

enter image description here

Bewirbt sich Resolver zu Ihrem Code ist ziemlich einfach, ich habe die Schnipsel für Sie erstellt, um zu 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 in der 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 Ihrem zugreifen Komponente so was:

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

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

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
////

21
2018-06-08 14:00