2017-05-30 20 views
2

Angular 4アプリでngrxを実装しています。 redux関連部分のコード構造は、ngrx repo(https://github.com/ngrx/example-app)のサンプルアプリケーションに基づいています。今私はそのようなものを実装する方法を考えています:Angular/w ngrx - 連続APIコール

  1. 私はある種のエンティティのためのフォームを持っています。
  2. 送信時に、そのエンティティの名前だけでAPIにPOSTリクエストを送信します。
  3. 私は、新しく作成されたエンティティのIDを取得します。
  4. 直後には、残りのフォーム値と2番目のリクエストを送信したいと思います。

どのように2番目のリクエストを入力する必要がありますか?連続したAPI呼び出しを実装する方法

答えて

3

は、呼び出しがどうあるべきか粘着性に依存します。
この2つの呼び出しを単一の「トランザクション」として表示するかどうかは、両方の要求が成功して状態を変更する必要があるかどうかです。明らかに

最初の要求が失敗した場合、それは最初の要求からのデータに依存しているため、2番目の要求を開始することはできません。 しかし...

最初のリクエストが成功し、2番目のリクエストが失敗するとどうなるでしょうか?

あなたのアプリは、それが唯一のid最初の要求からと、第2の要求なしで仕事だ、またはそれは矛盾した状態になってしまいます続けることはできますか?

  1. シナリオ1:


    は、私は2つのシナリオをカバーするつもりですはリクエストのどちらかは、あなたが全体の「トランザクション」として扱いに失敗すると「失敗したため、ドンましたその要求は失敗した。

  2. シナリオ2:要求1に障害が発生すると、要求2が実行されることはありません。要求2が失敗すると、要求1は引き続き正常とみなされます。

シナリオ1

両方の要求は、彼らが唯一の要求であるかのようにあなたが両方の要求を表示することができ、成功しなければならないので。

@Injectable() 
export class PostService { 
    private API_URL1 = 'http://your.api.com/resource1'; 
    private API_URL2 = 'http://your.api.com/resource2'; 

    constructor(private http: Http) { } 

    postCombined(formValues: { name: string, age: number }): Observable<any> {  
     return this.http.post(this.API_URL1, { name: formValues.name }) 
      .map(res => res.json()) 
      .switchMap(post1result => 
       this.http.post(this.API_URL2, { 
       /* access to post1result and formValues */ 
        id: post1result.id, 
        age: formValues.age, 
        timestamp: new Date() 
       }) 
       .map(res => res.json()) 
       .mergeMap(post2result => Observable.of({ 
        /* access to post1result and post2result */ 
        id: post1result.id, 
        name: post1result.name, 
        age: post2result.age, 
        timestamp: post2result.timestamp 
       }) 
      ); 
    } 
} 

今、あなたが有効にpostCombined -methodを使用することができます。私は(このアプローチはngrx/Reduxのに固有のものではない、それは単なるRxJsある)サービス内の連続した呼び出しを非表示にするにはお勧め。この場合 ngrx-example-appに紹介されている他のサービス方法と同様です。

  • いずれかのリクエストが失敗した場合、サービスはエラーをスローします。これを捕捉してエフェクトで処理できます。
  • 両方の要求が成功した場合は、mergeMapの内部で定義されたデータが返されます。ご覧のように、両方のリクエストレスポンスからマージされたデータを返すことができます。このアプローチで

シナリオ2

次の2つの要求の結果を区別し、どちらか一方に障害が発生した場合に異なった反応をすることができます。 2つの呼び出しを独立したアクションに分割して、それぞれのケースを個別に減らすことをお勧めします。次の両方のために要求 - 、success-および障害アクションを定義

post.service.ts

@Injectable() 
export class PostService { 
    private API_URL1 = 'http://your.api.com/resource1'; 
    private API_URL2 = 'http://your.api.com/resource2'; 

    constructor(private http: Http) { } 

    post1(formValues: { name: string }): Observable<{ id: number }> { 
     return this.http.post(this.API_URL1, formValues).map(res => res.json()); 
    } 

    post2(receivedId: number, formValues: { age: number }): Observable<any> { 
     return this.http.post(this.API_URL2, { 
      id: receivedId, 
      age: formValues.age, 
      timestamp: new Date() 
     }) 
     .map(res => res.json()); 
    } 
} 

まず、サービスは現在、2つの独立した方法(ここでは何も特別な)を持っていますリクエスト:

post.actions.ts

import { Action } from '@ngrx/store'; 

export const POST1_REQUEST = 'POST1_REQUEST'; 
export const POST1_SUCCESS = 'POST1_SUCCESS'; 
export const POST1_FAILURE = 'POST1_FAILURE'; 
export const POST2_REQUEST = 'POST2_REQUEST'; 
export const POST2_SUCCESS = 'POST2_SUCCESS'; 
export const POST2_FAILURE = 'POST2_FAILURE'; 

export class Post1RequestAction implements Action { 
    readonly type = POST1_REQUEST; 
    constructor(public payload: { name: string, age: number }) { } 
} 

export class Post1SuccessAction implements Action { 
    readonly type = POST1_SUCCESS; 
    constructor(public payload: { id: number }) { } 
} 

export class Post1FailureAction implements Action { 
    readonly type = POST1_FAILURE; 
    constructor(public error: any) { } 
} 

export class Post2RequestAction implements Action { 
    readonly type = POST2_REQUEST; 
    constructor(public payload: { id: number, name: string, age: number}) { } 
} 

export class Post2SuccessAction implements Action { 
    readonly type = POST2_SUCCESS; 
    constructor(public payload: any) { } 
} 

export class Post2FailureAction implements Action { 
    readonly type = POST2_FAILURE; 
    constructor(public error: any) { } 
} 

export type Actions 
    = Post1RequestAction 
    | Post1SuccessAction 
    | Post1FailureAction 
    | Post2RequestAction 
    | Post2SuccessAction 
    | Post2FailureAction 

そして今、我々は、要求アクションがディスパッチされたときに実行されると、今度は、サービスコールの結果に依存success-または障害アクションのいずれかを派遣する二つの効果を定義することができます

post.effectsを第1の効果でObservable.from([..])と.TS

import { PostService } from '../services/post.service'; 
import * as post from '../actions/post'; 

@Injectable() 
export class PostEffects { 
    @Effect() 
    post1$: Observable<Action> = this.actions$ 
     .ofType(post.POST1_REQUEST) 
     .map(toPayload) 
     .switchMap(formValues => this.postService.post1(formValues) 
      .mergeMap(post1Result => 
       Observable.from([ 
        /* 
        * dispatch an action that signals that 
        * the first request was successful 
        */ 
        new post.Post1SuccessAction(post1Result), 

        /* 
        * dispatch an action that triggers the second effect 
        * as payload we deliver the id we received from the first call 
        * and any other values the second request needs 
        */ 
        new post.Post2RequestAction({ 
         id: post1Result.id, 
         name: formValues.name, 
         age: formValues.age 
        }) 
       ]) 
      ) 
      .catch(err => Observable.of(new post.Post1FailureAction(err))) 
     ); 

    @Effect() 
    post2$: Observable<Action> = this.actions$ 
     /* 
     * this effect will only run if the first was successful 
     * since it depends on the id being returned from the first request 
     */ 
     .ofType(post.POST2_REQUEST) 
     .map(toPayload) 
     .switchMap(formValuesAndId => 
      this.postService.post2(
       /* we have access to the id of the first request */ 
       formValuesAndId.id, 
       /* the rest of the form values we need for the second request */ 
       { age: formValuesAndId.age } 
      ) 
      .map(post2Result => new post.Post2SuccessAction(post2Result)) 
      .catch(err => Observable.of(new post.Post2FailureAction(err))) 
     ); 

    constructor(private actions$: Actions, private postService: PostService) { } 
} 

注意組み合わせでmergeMap。これは、還元剤で減らすことができるPost1SuccessActionと、実行する第2の効果を引き起こすPost2RequestActionを発送することを可能にする。最初のリクエストが失敗した場合は、Post2RequestActionがディスパッチされないため、2番目のリクエストは実行されません。

このようにアクションとエフェクトを設定すると、他のリクエストとは独立して、失敗したリクエストに反応することができます。

最初のリクエストを開始するには、フォームを送信するときにPost1RequestActionを送信するだけです。例えばthis.store.dispatch(new post.Post1RequestAction({ name: 'Bob', age: 45 }))のように。