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

TypeScript - RxJS การแบ่งหน้าพร้อมการสมัครสมาชิกกับแต่ละหน้า


เรากำลังสอบถาม graphql ผ่าน websocket เพื่อมีการสมัครรับรายการของเอนทิตี เราใช้การแบ่งหน้าเนื่องจากรายการนี้ควรเลื่อนดูได้ไม่จำกัด เรายังต้องการเห็นการเปลี่ยนแปลงที่เกิดขึ้นในฐานข้อมูลแบบเรียลไทม์ในรายการที่ด้านหน้าด้วย

รายชื่อนี้สามารถ กรอง, เรียงลำดับ และ แบ่งหน้า ได้

เรามีคลาส SelectParams ที่ช่วยให้เราทำสิ่งนั้นได้:

export class SelectParams {
    page = 0;
    query = '';
    sort: Sort = { sortBy: 'creationDate', sortOrder: 'DESC' };
    take = 30;
}

แล้วเมื่อเราต้องการสมัครรับรายการ เราก็เพียงแค่ให้ selectParams$=Observable<SelectParams> เพื่อรับรายการที่เป็น "reactive" และตอบสนองต่อการเปลี่ยนแปลงใน selectParams$ ใช้งานแบบนี้:

    this.items$ = this.featureSrv.selectMany(this.selectParams$)

ใต้ฝากระโปรง selectMany ทำงานแบบนี้:

// when we call selectMany we just push the params to the pipeline
selectMany(params$: Observable<SelectParams> = of(new SelectParams())): Observable<T[]> {
    this.selectManyParams$.next(params$);
    return this.selectMany$;
}

// subject where we push params to sort, paginate and filter
selectManyParams$ = new ReplaySubject<Observable<SelectParams>>(1);
// when the params change then so does this observable, which is returned by the selectMany function
selectMany$ = this.selectManyParams$.asObservable().pipe(
    // retrieve params from their observable form
    flatMap(params$ => params$),
    // when the params haven't changed we shouldn't do anything
    distinctUntilChanged(),
    // then we query graphql to get a suscription to some part of the data
    switchMap(({ page, sort, query, take }: SelectParams) => {
    // the selectMany here is a subscription to some data on the server
        return this.apolloWrapper.selectMany(this.gql, page, sort, query, take ).pipe(
    // we add page data so we can use it in the scan
            map(data => ({ data, page }) as any)
        );
    }),
    // we append the result if page was incremented
    // else we just return the result
    scan((acc: any, curr: any) => curr.page === 0 ? curr.data : acc.concat(curr.data), [])
);

วิธี this.apolloWrapper.selectMany เป็นวิธีที่เราสร้างขึ้นที่ทำการสมัครสมาชิกผ่าน websocket ไปยังบางส่วนของข้อมูล สมมติว่าเรามี page=0 แล้ว this.apolloWrapper.selectMany จะส่งกลับการสมัครสมาชิกไปยังส่วนแรกของข้อมูล แล้วเมื่อ page=1 this.apolloWrapper.selectMany จะส่งกลับการสมัครสมาชิกไปยังส่วนที่สองของข้อมูล

ปัญหาเกิดขึ้นเมื่อเราโหลดหน้าที่สองและทำการแก้ไขรายการในสไลซ์แรก จากนั้น scan จะถูกเรียกใช้งานและรายการจะถูกเพิ่มกลับไปที่ท้ายรายการอีกครั้ง