ค้นหาเว็บไซต์

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 อย่างที่ฉันต้องเข้าใจที่นี่

  1. ตารางภายในเพื่อจัดเก็บโครงสร้าง redux
  2. ส่วนประกอบด้านหน้า

ปัญหาใหญ่ที่ผมมีคือผมต้องจัดการกับ 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 ครั้งแรกของคุณ (เว้นแต่คุณจะเป็นอัจฉริยะบ้าๆ ;-) ).

บทความที่เกี่ยวข้อง