Angular2: Як завантажити дані перед наданням компонента?


143

Я намагаюся завантажити подію з мого 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 підручнику .

Редагувати:

Два можливих рішення. Спочатку було викопати функцію 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);
}

Відповіді:


127

оновлення

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

Коли 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;
}

3
Власне, ваш перший коментар спрацював прекрасно. Я лише видалив всю fetchEventфункцію і просто поставив apiService.get.eventфункцію в ngOnInitі він працював так, як я маю намір. Дякую! Я прийму вам відповідь, як тільки це дозволить і мені.
Том Оалберс

Я чомусь застосував цей підхід на Angular 1.x. ІМХО, це слід сприймати як злом замість належного рішення. Ми повинні робити краще в кутовій 5.
вітроммао

Що саме вам у цьому не подобається? Я думаю, що обидва підходи ідеально чудові.
Гюнтер Зехбауер

54

Приємне рішення, яке я знайшов, - це зробити в інтерфейсі щось подібне:

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

Тільки тоді, коли: isDataLoaded вірно, сторінка надається.


3
Я думаю, що це рішення призначене лише для Angular 1, а не Angular> = 2, оскільки правило однонаправленого потоку даних Angular забороняє оновлення подання після його складання. Обидва ці гачки вистрілюються після того, як складається компонент.
zt1983811

1
Дів взагалі не отримується без змін. Ми не маємо наміру зупинити його від надання, ми хочемо відкласти його. Причина цього не працює в тому, що у куті2 немає двостороннього прив'язки. @phil чи можете ви пояснити, як це працювало на вас?
ishandutta2007

1
ОК, я використовував ChangeDetectionStrategy.Push, змінивши його, щоб ChangeDetectionStrategy.Defaultвін був двостороннім пов'язаним з шаблоном.
ishandutta2007

кутовий 6. Роботи.
TDP

Який чудовий хак працює в кутовому 2х.
nishil bhave

48

Ви можете попередньо отримати свої дані, використовуючи 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;
      });
  }
/////

І в маршруті щось подібне (як правило, у файлі app.routing.ts):

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

1
Я думаю, ви пропустили запис конфігурації маршруту під Routesмасивом (як правило, у файлі app.routing.ts ):{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
Voicu

1
@Voicu, це не повинно охоплювати всі частини, але я згоден, це робить відповідь більш вичерпною, тому я додав ... Дякую
Аліреза

5
Тільки для виправлення останньої частини параметр resolve: { myData: MyResolver }
Кріс Хайнс

Це MyData модельний об'єкт, який визначається в MyService?
Isuru Madusanka

1
це рішення добре для отримання даних перед завантаженням сторінки, але не для ролей користувача, що зареєстровані, або ролей користувача. Для цього вам слід скористатися гвардії
Angel Q
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.