angular - ngrx: โครงสร้างการทำให้เป็นมาตรฐานเทียบกับเอนทิตีแบบสังเกตได้
ฉันกำลังพยายามหาวิธีจัดการกับ "อาร์เรย์เอนทิตี้" ใน Store ของ ngrx ของฉัน
ฉันหมายถึง ลองเดาว่าฉันมีคอลเลกชันของ PlanDTO ที่ฉันกำลังได้จากเซิร์ฟเวอร์ API ของฉัน ดังนั้น ตามเอกสารหลายฉบับที่ฉันได้อ่าน ฉันจำเป็นต้องสร้างบางประเภทของ "ตาราง" เพื่อเก็บพวกมัน:
export interface IPlan {
id?: string;
name?: string;
...
}
export interface IPlanRedux {
byId: { [key: string]: IPlan };
allIds: Array<string>;
}
ดังนั้น เมื่อมีการส่ง action 'LOAD_PLANS' ผลกระทบก็จะเริ่มทำงานด้วย:
@Injectable()
export class PlanEffects {
constructor(
private actions$: Actions,
private store$: Store<IStore>,
private planService: PlansService,
) { }
@Effect({ dispatch: true })
loadPlans$: Observable<Action> = this.actions$
.ofType('LOAD_PLANS')
.switchMap((action: Action) =>
this.planService.list()
.map((plans: Array<PlanDTO>) => {
return { type: 'LOAD_PLANS_SUCCESS', payload: plans };
})
.catch((err: ApiError) => {
return Observable.of({ type: 'LOAD_PLANS_FAILED', payload: { code: err.code, msg: err.message } });
})
);
}
อย่างที่คุณเห็น ถ้าทุกอย่างเรียบร้อย การกระทำอีกอย่างหนึ่งได้ถูก "ส่ง" แล้ว: 'LOAD_PLANS_SUCCESS' ดังนั้นตัวลดสำหรับการกระทำนี้คือ:
private static loadPlansSuccess(plansRdx: IPlanRedux, type, payload: Array<PlanDTO>) {
const plans = payload;
const newPlans = plans.filter(plan => !plansRdx.byId[plan.id]);
const newPlanIds = newPlans.map(plan => plan.id);
const newPlanEntities = plans.reduce((entities: { [id: string]: IPlan }, plan: IPlan) => {
return Object.assign(entities, {
[plan.id]: plan
});
},
{});
return {
ids: [ ...plansRdx.allIds, ...newPlanIds ],
byId: Object.assign({}, plansRdx.byId, newPlanEntities),
};
}
ทุกอย่างดูเหมือนจะทำงานได้ดี อย่างไรก็ตาม ฉันยังไม่ค่อยเข้าใจนัก ฉันคิดว่ามีแนวคิด 2 อย่างที่ฉันต้องเข้าใจที่นี่
- ตารางภายในเพื่อจัดเก็บโครงสร้าง redux
- ส่วนประกอบด้านหน้า
ปัญหาใหญ่ที่ผมมีคือผมต้องจัดการกับ Observable<IPlanRedux> แทนที่จะเป็น Observable<IPlan> ในคอมโพเนนต์ของผม ผมหมายถึง ในคอมโพเนนต์ของผม ผมต้องการจัดการกับ IPlan แทนที่จะเป็นตารางที่มีฟิลด์ allIds หรือ ById
ดังนั้น ภายในผมกำลังดูแลตาราง IPlanRedux ของผมเอง แต่ในอีกด้านหนึ่ง ผมอยากจะ "สื่อออก" ว่าแผนไหนถูกลบออกและแผนไหนถูกเพิ่มเข้าไป
ฉันไม่รู้ว่าฉันอธิบายได้ดีหรือเปล่า
แก้ไข
ฉันได้ลองใช้ตัวเลือกแล้ว:
export interface IPlanRedux {
entities: { [key: string]: IPlan };
ids: Array<string>;
}
export const getPlans = (planRdx: IPlanRedux) => planRdx.entities;
const getPlansState = (state: IStore) => state.plans;
export const getPlanEntities = createSelector(getPlansState, getPlans);
เข้าไปในส่วนประกอบของฉัน:
this.plans$ = this.store$.select(fromRoot.getPlanEntities)
.map(plan => {
if (plan.id != null)
คำตอบ #1
ก่อนอื่นเลย: "กฎและแนวคิด" เป็นเพียงแนวทางที่ควรช่วยคุณค้นหาวิธีแก้ปัญหาที่ดีสำหรับแอปพลิเคชันของคุณ - ฉันไม่รู้จักใครที่นำกฎเหล่านั้นไปใช้ครบ 100% ทุกแอปพลิเคชันมีลักษณะเฉพาะตัวบางอย่างและไม่มีวิธีแก้ปัญหาแบบ "หนึ่งวิธีใช้ได้กับทุกคน"
ดังนั้น แค่เพราะมีใครบอกให้คุณใช้โครงสร้างแบบตาราง ไม่ได้หมายความว่านี่คือทางแก้ปัญหาที่ดีที่สุดสำหรับปัญหาของคุณ (ในทางกลับกันก็ไม่ได้หมายความว่ามันจะเป็นทางแก้ปัญหาที่แย่) เนื่องจากคุณเป็นคนเดียวที่นี่ที่รู้ข้อมูลและความต้องการของคุณ คุณน่าจะเป็นคนเดียวที่สามารถตัดสินได้ ดังนั้นอย่าเชื่อกฎใด ๆ แบบตาบอดและคาดหวังว่าจะไม่มีข้อเสียใด ๆ
สำหรับคำถามของคุณ:
1. ตารางภายในเพื่อเก็บโครงสร้าง redux
redux หรือ ngrx-store มักใช้ร่วมกับอ็อบเจ็กต์ที่ไม่เปลี่ยนแปลง (ซึ่งทำให้การตรวจจับการเปลี่ยนแปลงทำงานได้ดียิ่งขึ้น) - ในกรณีนี้ การจัดการข้อมูลของคุณในโครงสร้างแบบเรียบง่ายนั้นง่ายกว่าการใช้โครงสร้างแบบซ้อน เพราะในโครงสร้างแบบเรียบ (ตาราง) คุณเพียงแค่ต้องสร้างข้อมูลใหม่ไม่เกิน 2 ชั้นเท่านั้น แต่เมื่อคุณมีอ็อบเจ็กต์ซ้อนลึก คุณจะต้องสร้างชั้นข้อมูลใหม่มากขึ้น (ขึ้นอยู่กับความลึกของการซ้อน) เมื่อทำการเปลี่ยนแปลงข้อมูล
ดังนั้น: เมื่อคุณไม่ได้ทำงานกับค่าคงที่ คุณจะไม่ได้ประโยชน์จริง ๆ จากโครงสร้างข้อมูลแบบนี้หรือแบบนั้น (ฉันไม่ได้ให้คำแนะนำใด ๆ ที่นี่ นี่คือการตัดสินใจทางเทคนิค ซึ่งขึ้นอยู่กับคุณ ส่วนตัวฉันชอบทำงานกับค่าคงที่ แต่ฉันไม่ได้เริ่มต้นด้วยสิ่งนั้น)
2. ส่วนประกอบด้านหน้า
ที่นี่ อีกครั้ง มี "กฎ" บางอย่างเช่น "ส่วนประกอบฉลาดและโง่", "คำสั่ง select เทียบกับฟังก์ชัน let" เป็นต้น - อีกครั้ง: จริง ๆ แล้วไม่มีวิธีแก้ปัญหาที่สมบูรณ์แบบหรือทรงพลังอย่างแท้จริง
สำหรับ Observable<IPlanRedux> - คุณไม่จำเป็นต้องทำแบบนั้นทุกที่ - ถ้าคุณไม่ถนัดกับฟังก์ชัน let คุณก็สามารถสร้างคลาสบริการบางอย่างและใช้คำสั่ง select ง่ายๆ ที่นั่นได้:
class StoreService {
iPlans$ = this.store.select(state => state.planRedux)
.map(planRedux => planRedux.allIds.map(id => planRedux.byId[id]));
}
คำแนะนำ ส่วนตัว บางอย่าง ขึ้นอยู่กับกรณีของคุณ:
ถ้าคุณทำโปรเจกต์นี้เป็นงานอดิเรก & เพื่อการเรียนรู้: ทำให้เต็มที่ ลองทำทุกอย่างที่คุณทำได้และเรียนรู้ให้มากที่สุด ทดสอบให้สุดพลัง - คุณ จะเจออุปสรรค คุณ จะต้องทำการปรับปรุงโค้ดเมื่อคุณเรียนรู้ แต่สิ่งนั้นคุ้มค่าแน่นอน NGRX และรูปแบบ redux โดยทั่วไปเป็นเครื่องมือที่ ดีมาก แต่ก็เข้าใจยาก โดยเฉพาะถ้าคุณคุ้นเคยกับรูปแบบอื่นๆ
หากคุณกำลังทำโปรเจคนี้ให้ลูกค้าและเป็นแอปพลิเคชันพร้อมใช้งานจริง: ยึดถือสิ่งที่คุณรู้แล้วหรือยอมรับความจริงว่า จะมีผลกระทบหลีกเลี่ยงไม่ได้ในด้านคุณภาพและเวลา หากนี่เป็นโปรเจค ngrx และ/หรือ angular2 ครั้งแรกของคุณ (เว้นแต่คุณจะเป็นอัจฉริยะบ้าๆ ;-) ).
