web-dev-qa-db-ja.com

ビュー/テンプレートをレンダリングする前に、Angular 2がモデルをロード/解決するのを待ちます

Angular 1.xでは、UI-Routerが私の主なツールでした。 「解決」値のプロミスを返すことにより、ルーターはディレクティブがレンダリングされる前にプロミスが完了するのを待つだけです。

または、Angular 1.xでは、nullオブジェクトはテンプレートをクラッシュさせません。したがって、一時的に不完全なレンダリングを気にしない場合は、$digestを使用してpromise.then()は、最初は空のモデルオブジェクトに入力します。

2つのアプローチのうち、可能であればビューのロードを待機し、リソースをロードできない場合はルートナビゲーションをキャンセルします。これにより、「移動しない」という作業を省くことができます。 編集:これは、この質問がこれを行うためにAngular 2の先物互換またはベストプラクティスメソッドを要求し、可能であれば「エルビス演算子」を避けるように要求することを意味することに注意してください!したがって、私はその答えを選択しませんでした。

ただし、これら2つの方法はいずれもAngular 2.0では機能しません。確かに、このために計画された、または利用可能な標準ソリューションがあります。誰もがそれが何であるか知っていますか?

@Component() {
    template: '{{cats.captchans.funniest}}'
}
export class CatsComponent {

    public cats: CatsModel;

    ngOnInit () {
        this._http.get('/api/v1/cats').subscribe(response => cats = response.json());
    }
}

次の質問は同じ問題を反映している可能性があります。 データを含むPROMISEがロードされた後のAngular 2レンダリングテンプレート 。質問にはコードや回答が含まれていないことに注意してください。

70
shannon

パッケージ@angular/routerには、ルート用のResolveプロパティがあります。そのため、ルートビューをレンダリングする前にデータを簡単に解決できます。

参照してください: https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html

2017年8月28日現在のドキュメントの例:

class Backend {
  fetchTeam(id: string) {
    return 'someTeam';
  }
}

@Injectable()
class TeamResolver implements Resolve<Team> {
  constructor(private backend: Backend) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<any>|Promise<any>|any {
    return this.backend.fetchTeam(route.params.id);
  }
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'team/:id',
        component: TeamCmp,
        resolve: {
          team: TeamResolver
        }
      }
    ])
  ],
  providers: [TeamResolver]
})
class AppModule {}

データが解決されて返されるまで、あなたのルートはアクティブになりません。

コンポーネント内の解決済みデータへのアクセス

実行時にコンポーネント内から解決されたデータにアクセスするには、2つの方法があります。だからあなたのニーズに応じて、どちらかを使うことができます。

  1. 文字列を返すroute.snapshot.paramMap、または
  2. Observableを返すroute.paramMap.subscribe()にアクセスできます。

例:

  // the no-observable method
  this.dataYouResolved= this.route.snapshot.paramMap.get('id');
  // console.debug(this.licenseNumber);

  // or the observable method
  this.route.paramMap
     .subscribe((params: ParamMap) => {
        // console.log(params);
        this.dataYouResolved= params.get('id');
        return params.get('dataYouResolved');
        // return null
     });
  console.debug(this.dataYouResolved);

それが役立つことを願っています。

34
TetraDev

{{model?.person.name}}を試してください。これはmodelがundefinedにならないように待ってからレンダリングする必要があります。

Angular 2では、この?.構文を Elvis演算子 と表しています。ドキュメント内でそれを参照するのは難しいので、変更や移動をした場合に備えて、そのコピーを次に示します。

エルビス演算子(?)およびnullプロパティパス

Angular“ Elvis”演算子(?。)は、プロパティパスのnull値および未定義値を防ぐための流暢で便利な方法です。これは、currentHeroがnullの場合にビューのレンダリング失敗から保護するためです。

The current hero's name is {{currentHero?.firstName}}

問題とこの特定の解決策について詳しく説明しましょう。

次のデータバインドタイトルプロパティがnullの場合はどうなりますか?

The title is {{ title }}

ビューは引き続きレンダリングされますが、表示される値は空白です。それ以降は何もない「タイトルは」です。それは合理的な振る舞いです。少なくともアプリはクラッシュしません。

この次の例のように、テンプレート式にプロパティパスが含まれているとします。この例では、nullヒーローのfirstNameを表示しています。

The null hero's name is {{nullHero.firstName}}

JavaScriptはnull参照エラーをスローし、Angularも同様にスローします。

TypeError: Cannot read property 'firstName' of null in [null]

さらに悪いことに、ビュー全体が消えます。

私たちは、主人公の財産が決してヌルであってはならないと私たちが信じているならば、これは合理的な行動であると主張することができます。決してnullであってはならず、それでもnullである場合は、捕捉して修正する必要があるプログラミングエラーが発生しています。例外をスローするのは正しいことです。

一方、プロパティパスのnull値は、時々、特にデータが最終的に到着することがわかっているときには、OKである可能性があります。

データを待つ間、ビューは文句なしにレンダリングし、nullプロパティのパスはtitleプロパティと同じように空白で表示されます。

残念ながら、currentHeroがnullの場合、我々のアプリはクラッシュします。

その問題をNgIfで回避できます。

<!--No hero, div not displayed, no error --><div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>

あるいは、式が最初のNULLに遭遇したときにその式が無効になることを知っているので、&pathを使ってプロパティパスの一部を連結することもできます。

The null hero's name is {{nullHero && nullHero.firstName}}

これらのアプローチにはメリットがありますが、特にプロパティパスが長い場合は面倒な場合があります。 a.b.c.dのような長いプロパティパスのどこかでnullに対する保護を想像してみてください。

Angular“ Elvis”演算子(?。)は、プロパティパスのnullを防ぐためのより流暢で便利な方法です。最初のNULL値に到達すると、式は無効になります。表示は空白ですが、アプリは動き続け、エラーはありません。

<!-- No hero, no problem! -->The null hero's name is {{nullHero?.firstName}}

長いプロパティパスでも完璧に機能します。

a?.b?.c?.d

85
tehaaron

編集:アンギュラチームは@Resolveデコレータをリリースしました。それはそれがどのように機能するか、まだいくらかの明確化を必要とします、しかしそれまで私はここで他の誰かの関連する答えを取り、そして他のソースへのリンクを提供します:


編集:この答えはAngular 2 BETAにのみ有効です。この編集の時点では、ルーターはAngular 2 RC用にリリースされていません。代わりにAngular 2 RCを使用する場合は、ベータルーターを引き続き使用するためにrouterへの参照をrouter-deprecatedに置き換えてください。

これを実装するAngular 2-futureの方法は@Resolveデコレータを使うことです。それまでは、Brandon Robertsによると、最も近いファクシミリはCanActivate Componentデコレータです。 https://github.com/angular/angular/issues/6611 を参照してください。

Beta 0はComponentへの解決された値の提供をサポートしていませんが、計画されており、ここで説明されている回避策もあります。 Resolve In Angular 2 Routesの使用

ベータ1の例はここで見つけることができます: http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved 。これは非常によく似た回避策を使用しますが、RouteDataではなくRouteParamsオブジェクトを少し正確に使用します。

@CanActivate((to) => {
    return new Promise((resolve) => {
        to.routeData.data.user = { name: 'John' }

また、ネストされた/親ルートの「解決された」値にアクセスするための回避策の例、および1.x UI-Routerを使用した場合に期待されるその他の機能もあります。

Angular Injector階層は現在CanActivateデコレータで使用できないため、これを達成するために必要なサービスを手動でインジェクトする必要もあります。インジェクタをインポートするだけで、bootstrap()からプロバイダにアクセスすることなく新しいインジェクタインスタンスが作成されるので、おそらくブートストラップされたインジェクタのアプリケーション全体のコピーを保存したいでしょう。このページのBrandonの2番目のPlunkリンクは良い出発点です。 https://github.com/angular/angular/issues/4112

6
shannon

オブザーバでローカル値を設定

また、uninitializedエラーを回避するためにダミーデータで値を初期化することを忘れないでください。

export class ModelService {
    constructor() {
      this.mode = new Model();

      this._http.get('/api/v1/cats')
      .map(res => res.json())
      .subscribe(
        json => {
          this.model = new Model(json);
        },
        error => console.log(error);
      );
    }
}

これはModelを仮定し、これはあなたのデータの構造を表すデータモデルです。

パラメータのないモデルは、すべての値が初期化された(ただし空の)新しいインスタンスを作成する必要があります。そうすれば、データを受け取る前にテンプレートがレンダリングされてもエラーにはなりません。

理想的には、不要なhttpリクエストを避けるためにデータを永続化したい場合は、購読できる独自のオブザーバを持つオブジェクトにこれを入れるべきです。

4
Evan Plaice

@ComponentrouterOnActivateを実装し、あなたの約束を返します。

https://angular.io/docs/ts/latest/api/router/OnActivate-interface.html

編集:現在のドキュメントはこのトピックについて解釈するのが少し難しいかもしれませんが、これははっきりと動作しません。詳細については、Brandonの最初のコメントを参照してください。 https://github.com/angular/angular /issues/6611

編集:それ以外の点では正確なAuth0サイトの関連情報が正しくない: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-深さのルーター/

編集:アンギュラチームはこの目的のために@Resolveデコレータを計画しています。

2
Langley

私が見つけた素敵な解決策は、UIで何かをすることです。

<div *ngIf="vendorServicePricing && quantityPricing && service">
 ...Your page...
</div

vendorServicePricingquantityPricingおよびserviceがロードされている場合にのみ、ページがレンダリングされます。

2
Linu Radu

私は、ユニバーサルアドオンでAngular 6を使っています。しかし私はまだその点を理解していませんでした。私はHttpClient XHRを呼び出してコンポーネントに送るリゾルバを持っており、ngOnInit上のコンポーネントはデータをビューにバインドします。しかし、問題はソース表示モードにあり、ビューに表示されるXHRレスポンスからのデータはありません。

0