2016-12-19 10 views
5

列表更新单个项目我有几分以下申请国树:NGRX - 在项目

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

所以,我有一个页面上的项目列表。我通过将操作分派给MyList减速器来加载此列表。加载项目后,它们将作为输入传递给MyListComponent,从而创建带有相应输入的MyItemComponents。 MyItemComponent也可以是独立的。所以,它并不总是依赖于MyList。

事情是,每个MyItems都有自己的行为(例如可以被喜欢或编辑)。而且我现在还在哪里,在那种情况下应该派遣什么样的行动。如何识别哪些项目已更新以及应更新哪个列表。

当用户更新列表中的某个项目时,操作流程是什么?

而且 - 如果我直接更改MyItem之一,是否会检测MyList的状态更改?

情景我想象的是(同时对MyItem本身一切适当的行动),以额外的操作添加到MYLIST - 如:

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

然后,不知何故,我火从MYLIST另一个动作到指定MyItem从数组中,item触发相应的操作以更新其自己的状态(通过@Effect更新) 如果更新成功,将触发新操作以更新列表。

说实话,这一切似乎相当冗长和模糊。甚至不知道如何在实践中实施。真的需要有经验的人的建议。

p.s. 我应该提到,所有数据都是从REST API获取并发回的。

编辑: 如果遵循Comprehensive Introduction to @ngrx/store的方法,应用程序将会有重复动作的声调(每个容器 - MyList - 将重复执行MyItem中的所有动作)。对我的情况会有更合适的方法吗?

+0

upvoted,好问题! –

回答

11

要做到这一点的方法是有一个子减速器处理状态树的子部分。

如果您需要将动作发送到数组中的单个项目,则会将所有这些动作传递给处理单个项目的子缩减器。

下面是一个新闻项目的例子,有评论。我希望它能清理一些东西。

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

}; 

而在这里看到如何调用这些是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

谢谢!你的回答有很大的帮助,我喜欢你的代码是多么的干净, –

+0

就是这样做的,只是在newsReducer中分离所有关注点+模块化代码,或者让'ngrx/store'知道关于个体的实际必要性项目减速器被应用? I.E.如果你已经将'newsReducer'中的所有'newsItemReducer'组合起来并且只是做了一个状态,ngrx会有不同的表现。地图“并返回更新的'newsState'? –

+0

这只是为了分离担忧。关于单个物品的推理比收集更容易。 –