2016-12-19 2 views
5

Ich habe etwas den folgende Application State Baum:NGRX - update einzelnes Element in einer Liste von Elementen

AppState 
{ 
    MyPage1 - MyList - MyItem[] 
    MyPage2 - MySubPage - MyList - MyItem[] 
    MyPage3 - MyItem //can be stand-alone as well 
} 

Also, ich habe eine Liste von Elementen auf einer Seite. Ich lade diese Liste, indem ich eine Aktion an den MyList Reducer übergebe. Sobald Elemente geladen sind, werden sie als Eingabe an die MyListComponent übergeben, die entsprechend MyItemComponents mit den entsprechenden Eingaben erstellt. MyItemComponent kann auch eigenständig sein. Es hängt also nicht immer von der MyList ab.

Die Sache ist, dass jeder der MyItems seine eigenen Handlungen hat (zum Beispiel können gemocht oder bearbeitet werden). Und ich bin derzeit fest, wo und welche Aktion sollte ich in diesem Fall versenden. Wie kann ich feststellen, welcher Artikel aktualisiert wurde und welche Liste ich aktualisieren soll?

Was wäre der Aktionsfluss, wenn der Benutzer eines der Elemente in einer Liste aktualisiert?

Und auch - würde der Statuswechsel von MyList erkannt werden, wenn ich eines der MyItem direkt ändern?

Szenario Ich denke, könnte der ist (während alle geeigneten Maßnahmen, die auf MyItem selbst) zusätzliche Maßnahmen zum MyList hinzuzufügen - zB:

updateItem(payload: 
    { 
     updateType, //e.g. like, edit, delete 
     data,  //data needed to make an update 
     itemId  //index in the MyItem array 
    }) 

Dann irgendwie Feuer ich eine andere Aktion von MyList an die angegebene MyItem aus dem Array, feuert Element entsprechende Aktion seinen eigenen Zustand (aktualisiert über @Effect) zu aktualisieren Wenn ein Update erfolgreich war, wird eine neue Aktion gefeuert die Liste zu aktualisieren.

Um ehrlich zu sein, es scheint alles ziemlich ausführlich und vage. Nicht einmal sicher, wie es in der Praxis umgesetzt werden würde. Wirklich brauchen Anregung von jemandem mit Erfahrung.

p.s. Ich sollte erwähnen, dass alle Daten abgerufen und von/an die REST API gesendet werden.

Bearbeiten: Wenn Sie den Ansatz von Comprehensive Introduction to @ngrx/store folgen, würde die App Töne von doppelten Aktionen haben (jeder Container - MyList - hätte alle Aktionen von MyItem wiederholt). Würde es einen geeigneteren Ansatz für meinen Fall geben?

+0

upvoted, gute Frage! –

Antwort

11

Die Art und Weise, es zu tun ist, einen Untersetzungs zu haben, die einen Unter Teil des Staat Baumes behandelt.

In dem Fall, in dem Sie Aktionen an ein einzelnes Element im Array senden müssen, übergeben Sie alle diese Aktionen an den Unterreduzierer, der ein einzelnes Element verarbeitet.

Hier ist ein Beispiel einer Nachricht, die Kommentare. Ich hoffe, es klärt die Dinge ein wenig auf.

import { Action } from '@ngrx/store'; 
import { AppState } from '../reducer'; 
import { NewsItem } from './news.model'; 
import * as newsActions from './news.actions'; 

export interface NewsState { 
    readonly loading: boolean; 
    readonly entities: NewsItem[]; 
} 

export interface AppStateWithNews extends AppState { 
    readonly news: NewsState; 
} 

const initialState: NewsState = { 
    loading: false, 
    entities: [], 
}; 

const newsItemReducer = (newsItem: NewsItem, action: newsActions.NewsActionTypes): NewsItem => { 

    switch (action.type) { 
    case newsActions.Types.UPDATE: 
    case newsActions.Types.REMOVE: 
    case newsActions.Types.COMMENT_CREATE: 
    case newsActions.Types.COMMENT_REMOVE: 
     return Object.assign({}, newsItem, { 
     action: true 
     }); 

    case newsActions.Types.UPDATE_SUCCESS: 
     return Object.assign({}, action.payload, { 
     action: false 
     }); 

    case newsActions.Types.COMMENT_CREATE_SUCCESS: 
     return Object.assign({}, newsItem, { 
     action: false, 
     comments: [action.payload.comment, ...newsItem.comments] 
     }); 

    case newsActions.Types.COMMENT_REMOVE_SUCCESS: 
     return Object.assign({}, newsItem, { 
     action: false, 
     comments: newsItem.comments.filter(i => i.id !== action.payload.commentId) 
     }); 

    default: 
     return newsItem; 
    } 
}; 

export const newsReducer = (state: NewsState = initialState, action: newsActions.NewsActionTypes): NewsState => { 

    switch (action.type) { 
    case newsActions.Types.LIST: 
     return Object.assign({}, state, { loading: true }); 

    case newsActions.Types.LIST_SUCCESS: 
     return { 
     loading: false, 
     entities: action.payload 
     }; 

    case newsActions.Types.CREATE_SUCCESS: 
     return Object.assign({ 
     loading: false, 
     entities: [action.payload, ...state.entities] 
     }); 

    case newsActions.Types.REMOVE_SUCCESS: 
     return Object.assign({ 
     loading: false, 
     entities: state.entities.filter(newsItem => newsItem !== action.payload) 
     }); 

    // Delegate to newsItemReducer 
    case newsActions.Types.UPDATE: 
    case newsActions.Types.UPDATE_SUCCESS: 
    case newsActions.Types.COMMENT_CREATE: 
    case newsActions.Types.COMMENT_CREATE_SUCCESS: 
    case newsActions.Types.COMMENT_REMOVE: 
    case newsActions.Types.COMMENT_REMOVE_SUCCESS: 
     return Object.assign({}, state, { 
     entities: state.entities.map(newsItem => { 
      // Only call sub reducer if the incoming actions id matches 
      if (newsItem.id === (action.payload.newsItem || action.payload).id) { 
      return newsItemReducer(newsItem, action); 
      } 
      return newsItem; 
     }) 
     }); 

    default: 
     return state; 
    } 

}; 

Und um zu sehen, wie diese hier aufgerufen werden, ist die news.actions.

import { Action } from '@ngrx/Store'; 
import { NewsItem } from './news.model'; 
import { Response } from '@angular/http'; 

export const Types = { 
    LIST: '[News] List', 
    LIST_SUCCESS: '[News] List Success', 
    LIST_ERROR: '[News] List ERROR', 

    CREATE: '[News] Create', 
    CREATE_SUCCESS: '[News] Create Success', 
    CREATE_ERROR: '[News] Create Error', 

    UPDATE: '[News] Update', 
    UPDATE_SUCCESS: '[News] Update Success', 
    UPDATE_ERROR: '[News] Update Error', 

    REMOVE: '[News] Remove', 
    REMOVE_SUCCESS: '[News] Remove Success', 
    REMOVE_ERROR: '[News] Remove Error', 

    COMMENT_CREATE: '[News] Comment Create', 
    COMMENT_CREATE_SUCCESS: '[News] Comment Create Success', 
    COMMENT_CREATE_ERROR: '[News] Comment Create Error', 

    COMMENT_REMOVE: '[News] Comment Remove', 
    COMMENT_REMOVE_SUCCESS: '[News] Comment Remove Success', 
    COMMENT_REMOVE_ERROR: '[News] Comment Remove Error', 
}; 

/** 
* List 
*/ 
export class List implements Action { 
    type = Types.LIST; 
    constructor(public payload?: any) {} 
} 
export class ListSuccess implements Action { 
    type = Types.LIST_SUCCESS; 
    constructor(public payload: NewsItem[]) {} 
} 
export class ListError implements Action { 
    type = Types.LIST_ERROR; 
    constructor(public payload: Response) {} 
} 

/** 
* Create 
*/ 
export class Create implements Action { 
    type = Types.CREATE; 
    constructor(public payload: NewsItem) {} 
} 
export class CreateSuccess implements Action { 
    type = Types.CREATE_SUCCESS; 
    constructor(public payload: NewsItem) {} 
} 
export class CreateError implements Action { 
    type = Types.CREATE_ERROR; 
    constructor(public payload: Response) {} 
} 

/** 
* Update 
*/ 
export class Update implements Action { 
    type = Types.UPDATE; 
    constructor(public payload: NewsItem) {} 
} 
export class UpdateSuccess implements Action { 
    type = Types.UPDATE_SUCCESS; 
    constructor(public payload: NewsItem) {} 
} 
export class UpdateError implements Action { 
    type = Types.UPDATE_ERROR; 
    constructor(public payload: Response) {} 
} 

/** 
* Remove 
*/ 
export class Remove implements Action { 
    type = Types.REMOVE; 
    constructor(public payload: NewsItem) {} 
} 
export class RemoveSuccess implements Action { 
    type = Types.REMOVE_SUCCESS; 
    constructor(public payload: NewsItem) {} 
} 
export class RemoveError implements Action { 
    type = Types.REMOVE_ERROR; 
    constructor(public payload: Response) {} 
} 

/** 
* Create Comment 
*/ 
export class CommentCreate implements Action { 
    type = Types.COMMENT_CREATE; 
    payload: { 
    newsItem: NewsItem, 
    comment: string 
    }; 

    constructor(newsItem: NewsItem, comment: string) { 
    this.payload = { 
     newsItem, 
     comment 
    }; 
    } 
} 
export class CommentCreateSuccess implements Action { 
    type = Types.COMMENT_CREATE_SUCCESS; 
    payload: { 
    newsItem: NewsItem, 
    comment: string 
    }; 
    constructor(newsItem: NewsItem, comment: string) { 
    this.payload = { 
     newsItem, 
     comment 
    }; 
    } 
} 
export class CommentCreateError implements Action { 
    type = Types.COMMENT_CREATE_ERROR; 
    constructor(public payload: Response) {} 
} 

/** 
* Remove Comment 
*/ 
export class CommentRemove implements Action { 
    type = Types.COMMENT_REMOVE; 
    payload: { 
    newsItem: NewsItem, 
    commentId: string 
    }; 

    constructor(newsItem: NewsItem, commentId: string) { 
    this.payload = { 
     newsItem, 
     commentId 
    }; 
    } 
} 
export class CommentRemoveSuccess implements Action { 
    type = Types.COMMENT_REMOVE_SUCCESS; 
    payload: { 
    newsItem: NewsItem, 
    commentId: string 
    }; 
    constructor(newsItem: NewsItem, commentId: string) { 
    this.payload = { 
     newsItem, 
     commentId 
    }; 
    } 
} 
export class CommentRemoveError implements Action { 
    type = Types.COMMENT_REMOVE_ERROR; 
    constructor(public payload: Response) {} 
} 

export type NewsActionTypes = 
    List 
    | ListSuccess 
    | ListError 
    | Create 
    | CreateSuccess 
    | CreateError 
    | Update 
    | UpdateSuccess 
    | UpdateError 
    | Remove 
    | RemoveSuccess 
    | RemoveError 
    | CommentCreate 
    | CommentCreateSuccess 
    | CommentCreateError 
    | CommentRemove 
    | CommentRemoveSuccess 
    | CommentRemoveError; 
+1

Vielen Dank! Ihre Antwort hat mir sehr geholfen und Ich mag, wie sauber Ihr Code –

+0

ist der Unterschied zwischen all dies nur in der gerade Trennung von Bedenken + modularen Code newsReducer tun, oder ist es eine tatsächliche neccesity zu lassen 'NGRX/store' wissen über einzelne Item Reducer angewendet werden? I.E. Würde sich ngrx anders verhalten, wenn Sie alle 'newsItemReducer' im' newsReducer' kombiniert hätten und einfach einen '' '' '' '' '' gemacht hätten.map' und gibt einen aktualisierten 'newsState' zurück? –

+0

Es ist nur für die Trennung von Anliegen. Und es ist einfacher, über einen einzelnen Gegenstand nachzudenken als über eine Sammlung. –

Verwandte Themen