Подтвердить что ты не робот

Angular2: Как загрузить данные перед рендерингом компонента?

Я пытаюсь загрузить событие из моего API до того, как компонент получит визуализацию. В настоящее время я использую свою службу API, которую я вызываю из функции ngOnInit компонента.

Мой EventRegister компонент:

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

Мой шаблон EventRegister

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

Мой API-сервис

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

Компонент получает визуализацию до того, как вызов API в функции ngOnInit будет выполнен с извлечением данных. Поэтому я никогда не увижу идентификатор события в моем шаблоне просмотра. Таким образом, похоже, что это проблема ASYNC. Я ожидал привязки компонента ev (EventRegister) для выполнения некоторой работы после того, как было установлено свойство ev. К сожалению, он не показывает div, отмеченный *ngIf="ev", когда свойство будет установлено.

Вопрос: Я использую хороший подход? Если не; Каков наилучший способ загрузки данных до того, как компонент начнет рендеринг?

ПРИМЕЧАНИЕ. Подход ngOnInit используется в этом angular2 учебнике.

EDIT:

Два возможных решения. Сначала нужно было вырезать fetchEvent и просто использовать службу API в функции ngOnInit.

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

Второе решение. Как и ответ.

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

ngOnInit() {
    this.fetchEvent().then(event => this.ev = event);
}
4b9b3361

Ответ 1

Обновление

оригинальный

Когда console.log(this.ev) выполняется после this.fetchEvent();, это не означает, что вызов fetchEvent() завершен, это означает только то, что он запланирован. Когда выполняется console.log(this.ev), вызов на сервер даже не производится и, конечно же, еще не возвращает значение.

Измените fetchEvent(), чтобы вернуть 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
    });
 }

измените ngOnInit(), чтобы дождаться завершения Promise

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

Это фактически не будет покупать вас много для вашего случая использования.

Мое предложение: оберните весь шаблон в <div *ngIf="isDataAvailable"> (template content) </div>

и в ngOnInit()

isDataAvailable:boolean = false;

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

Ответ 2

Хорошее решение, которое я нашел, - это сделать на UI что-то вроде:

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

Только когда: isDataLoaded истинно, страница отображается.

Ответ 3

Вы можете выполнить предварительную выборку своих данных с помощью Resolvers в Angular2 +, Resolvers обрабатывают ваши данные до того, как ваш компонент будет полностью загружен.

Есть много случаев, когда вы хотите загрузить свой компонент только в том случае, если что-то происходит, например, перейдите в Личный кабинет только в том случае, если человек уже вошел в систему, в этом случае Resolvers являются такими удобными.

Посмотрите на простую диаграмму, которую я создал для вас одним из способов использования распознавателя для отправки данных на ваш компонент.

введите описание изображения здесь

Применение Resolver к вашему коду довольно просто, я создал для вас фрагменты, чтобы увидеть, как можно создать Resolver:

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

и в модуле:

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

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

и вы можете получить к нему доступ в Компонент следующим образом:

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