Angular2:コンポーネントをレンダリングする前にデータを読み込む方法は?


143

コンポーネントがレンダリングされる前に、APIからイベントをロードしようとしています。現在、コンポーネントのngOnInit関数から呼び出すAPIサービスを使用しています。

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

コンポーネントは、ngOnInit関数のAPI呼び出しがデータをフェッチする前にレンダリングされます。したがって、ビューテンプレートでイベントIDを確認することはできません。したがって、これはASYNCの問題のようです。evEventRegisterコンポーネント)のバインディングは、evプロパティが設定された後にいくつかの作業を行うと予想していました。悲しいことに、プロパティが設定されたときにdivマーク付きマークは表示されません*ngIf="ev"

質問:私は良いアプローチを使用していますか?そうでない場合; コンポーネントがレンダリングを開始する前にデータをロードする最良の方法は何ですか?

注:このngOnInitアプローチは、このangular2チュー​​トリアルで使用されます。

編集:

2つの可能なソリューション。最初は、を破棄fetchEventして、ngOnInit関数でAPIサービスを使用することでした。

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

2番目のソリューション。与えられた答えのように。

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意図したとおりに機能しました。ありがとう!私も許せばすぐに答えを受け入れます。
Tom Aalbers、2016

何らかの理由で、私はこのアプローチをAngular 1.xで使用しました。私見、これは適切な解決策の代わりにハックとして取られるべきです。角度5のほうがいいです
。– windmaomao

それについてあなたが正確に何が嫌いですか?どちらのアプローチも完全に問題ないと思います。
ギュンターZöchbauer

54

私が見つけた素晴らしい解決策は、次のようなUIで行うことです:

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

isDataLoadedがtrueの場合のみ、ページがレンダリングされます。


3
Angularの単方向データフロールールはビューの構成後の更新を禁止しているため、このソリューションはAngular 1以上の場合のみで、Angular 2以上ではないと思います。これらのフックは両方とも、コンポーネントのビューが構成された後に発生します。
zt1983811 2017年

1
divはまったくレンダリングされません。レンダリングを停止するつもりはありません。遅延させたいのです。これが機能しない理由は、angular2には双方向のバインディングがないためです。@philは、それがどのように機能したかを説明できますか?
ishandutta2007

1
OK、私はを使用ChangeDetectionStrategy.PushしていましたChangeDetectionStrategy.Defaultが、テンプレートに双方向バインドするように変更しています
ishandutta2007 2017年

角度6.動作します。
TDP

Angular 2xで素晴らしいハックが機能すること。
nishil bhave

48

Angular2 +のリゾルバーを使用してデータをプリフェッチできます。リゾルバーはコンポーネントが完全にロードされる前にデータを処理します。

特定のことが起こった場合にのみコンポーネントをロードしたい場合が多くあります。たとえば、ユーザーがすでにログインしている場合にのみダッシュボードに移動します。この場合、リゾルバーは非常に便利です。

リゾルバーを使用してコンポーネントにデータを送信する方法の1つとして、私が作成した簡単な図を見てください。

ここに画像の説明を入力してください

コードにリゾルバーを適用するのは非常に簡単です。リゾルバーを作成する方法を確認するためのスニペットを作成しました。

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、それはすべての部分をカバーすることになっているわけではありませんが、私は同意します、それは答えをより包括的にするので、私は付け加えました...ありがとう
Alireza

5
ただ、最後の部分を修正するために、ルートで解決パラメータはリゾルバ自体ではなく、データタイプ、すなわちにポイントする必要があるresolve: { myData: MyResolver }
クリス・ヘインズ

そのMyDataはMyServiceで定義するモデルオブジェクトですか?
Isuru Madusanka 2018

1
このソリューションは、ページが読み込まれる前にデータをフェッチするのに適していますが、ログに記録されたユーザーやユーザーロールをチェックするのには適していません。そのためにはガードを使用する必要があります
エンジェルQ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.