2017-05-30 18 views
2

我在Angular 4應用中實現ngrx。 REDX相關部分的代碼結構基於來自ngrx repo的示例應用程序(https://github.com/ngrx/example-app)。現在我想知道如何實現這樣的事情:Angular/w ngrx - 連續API調用

  1. 我已經有一些形式的實體。
  2. 在提交時,我只向該API發送POST請求,並且只有該實體的名稱。
  3. 作爲迴應,我得到新創建實體的ID。
  4. 緊接着,我想發送第二個請求,其餘的表單值和我剛剛得到的ID。

我應該在哪裏以及如何提出第二個請求?

回答

3

如何實現連續的API調用取決於調用應該如何一致。
我的意思是,您是否將這兩個電話視爲單個'交易',其中兩個請求都必須成功才能成功更改您的狀態。

顯然,如果第一個請求失敗,第二個請求將無法啓動,因爲它取決於來自第一個請求的數據。 但是...

當第一個請求成功並且第二個請求失敗時會發生什麼?

您的應用能否繼續使用第一個請求中的id而沒有第二個請求,還是最終處於不一致的狀態?


我將涵蓋兩種情況:

  1. 方案1:當任請求失敗,你把它當作整個「交易」已經失敗,因此不要不關心哪個請求失敗。
  2. 場景2:當請求1失敗時,請求2將不會執行。當請求2失敗時,請求1仍然會被視爲成功。

方案1

由於兩個請求必須成功,如果他們只有一個要求,你可以查看這兩個請求。 在這種情況下,我建議隱藏服務中的連續調用(這種做法是不特定於NGRX /終極版,它只是普通的RxJs):

@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 - 方法中的作用就像在ngrx-example-app中展示的任何其他服務方法一樣。

  • 如果任一請求失敗,服務會拋出一個錯誤,您可以在該錯誤中捕獲並處理該錯誤。
  • 如果兩個請求都成功,您將收回在mergeMap內定義的數據。如您所見,可以從兩個請求響應中返回合併數據。

方案2

通過這種方法可以區分這兩個請求的結果,如果任何一個失敗,做出不同的反應。 我建議將這兩個調用分解爲獨立的操作,以便您可以獨立地減少每個案例。

第一,服務現在有兩個獨立的方法(這裏沒有什麼特別):

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

下一個定義兩種請求 - ,成功 - 和失敗,行動要求:

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 

現在,我們可以定義當請求行動出動,將運行並反過來將派遣要麼成功 - 或者失敗,行動依賴於服務調用的結果,兩種效應:

post.effects .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結合在第一效Observable.from([..])。它允許你發送Post1SuccessAction,可以減少(通過reducer)以及Post2RequestAction,這會觸發第二個效果運行。如果第一個請求失敗,第二個請求將不會運行,因爲Post2RequestAction未被分派。

正如您所看到的,通過這種方式設置動作和效果,您可以獨立於其他請求來響應失敗的請求。

要開始第一個請求,您需要做的就是在您提交表單時發送Post1RequestAction。例如,像this.store.dispatch(new post.Post1RequestAction({ name: 'Bob', age: 45 }))一樣。