Saya mencoba mengembalikan sekelompok forkjoin bersarang dan langganan normal di resolver saya. Untuk ini saya mencoba menggunakan peta, tetapi saya pikir saya belum sepenuhnya memahami konsep peta/switchMaps/mergeMaps. Saya tahu kode tersebut belum mengembalikan UserResult, ini karena saya belum tahu bagaimana saya akan menambahkan questionAnswers ke UserResult, tetapi ini seharusnya tidak terlalu berbeda untuk masalah saya saat ini.

Tujuan saya adalah menulis ulang ini, sehingga mengembalikan yang dapat diamati.

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<UserResult> {
    const questionAnswers = Array<QuestionAnswer>();

    this.rs.getResult(this.auth.token, route.params['id']).subscribe(res => {
      forkJoin(
        this.quizs.getCategoriesQuiz(this.auth.token, res.quizId),
        this.accs.getAccount(res.userId)
      ).subscribe(results => {
        forkJoin(
          this.accs.getUserDetails(results[1].access_token),
          this.as.getAnswers(this.auth.token)
        ).subscribe(results2 => {
          results[0].forEach(cat => {
            this.cs
              .getQuestionsCategory(this.auth.token, cat.id)
              .subscribe(questions => {
                results2[1]
                  .filter(ans => ans.userId === results[1].uid)
                  .forEach(a => {
                    const question = questions.find(q => q.id === a.questionId);
                    if (!isNullOrUndefined(question)) {
                      const category = results[0].find(
                        c => c.id === a.categoryId
                      );
                      const qa = new QuestionAnswer(question, a);
                      qa.category = category.name;
                      questionAnswers.push(qa);
                    }
                  });
              });
          });
        });
      });
    });
}

Saya mencoba menulis ulang seperti ini, tetapi tidak berhasil sama sekali. Saya mendapatkan beberapa kesalahan yang tidak terdefinisi tetapi semuanya mengarah ke awal pipa, tidak ada yang spesifik.

    const questionAnswers = Array<QuestionAnswer>();
    let res;
    let res2;

    return this.rs.getResult(this.auth.token, route.params['id']).pipe(
      map((res: Result) =>
        forkJoin(
          this.quizs.getCategoriesQuiz(this.auth.token, res.quizId),
          this.accs.getAccount(res.userId)
        )
      ),
      tap(results => (res = results)),
      map(results =>
        forkJoin(
          this.accs.getUserDetails(results[1].access_token),
          this.as.getAnswers(this.auth.token)
        )
      ),
      tap(results2 => (res2 = results2)),
      map(
        res[0]
          .forEach(cat => {
            this.cs.getQuestionsCategory(this.auth.token, cat.id);
          })
          .map(questions =>
            res2[1]
              .filter(ans => ans.userId === res[1].uid)
              .forEach(a => {
                const question = questions.find(q => q.id === a.questionId);
                if (!isNullOrUndefined(question)) {
                  const category = res[0].find(c => c.id === a.categoryId);
                  const qa = new QuestionAnswer(question, a);
                  qa.category = category.name;
                  questionAnswers.push(qa);
                }
              })
          )
      )
    );

EDIT

Baru saja saya perhatikan bahwa res[0] setelah ketukan hasil2 menyebabkan a

Tidak dapat membaca properti '0' dari undefined

Saya berasumsi ini ada hubungannya dengan penggunaan ketukan saya yang buruk, karena ini berfungsi dengan baik di langganan yang saya coba ubah.

EDIT2

Saya membagi kode dalam fungsi yang lebih kecil seperti yang direkomendasikan Kurt, namun saya tidak begitu yakin bagaimana menggunakannya dengan forEach yang saya gunakan untuk kategori. Saya juga tidak tahu di mana saya harus membuat objek terakhir saya yang akan saya kembalikan sebagai yang bisa diamati


 getResultByRouteParamId(route: ActivatedRouteSnapshot): Observable<Result> {
    return this.rs.getResult(this.auth.token, route.params['id']);
  }

  forkJoinQuizCategoriesAndAccount(
    result: Result
  ): Observable<[Category[], Account]> {
    return forkJoin(
      this.quizs.getCategoriesQuiz(this.auth.token, result.quizId),
      this.accs.getAccount(result.userId)
    );
  }

  forkJoinUserDetailsAndAnswers(results: [Category[], Account]) {
    return forkJoin(
      this.accs.getUserDetails(results[1].access_token),
      this.as.getAnswers(this.auth.token)
    );
  }

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<UserResult> {

    const questionAnswers = Array<QuestionAnswer>();
    let result: Result;
    let res: [Category[], Account];
    let res2: [User, Answer[]];

    return this.getResultByRouteParamId(route).pipe(
      tap(resu => result = resu),
      switchMap((result: Result) => this.forkJoinQuizCategoriesAndAccount(result)),
      tap(results => (res = results)),
      switchMap(results => this.forkJoinUserDetailsAndAnswers(results)),
      tap(results2 => (res2 = results2)),
      switchMap(
          // Stuck here!
        res[0]
          .forEach(cat => {
            this.cs.getQuestionsCategory(this.auth.token, cat.id);
          })
          .map(questions =>
            res2[1]
              .filter(ans => ans.userId === res[1].uid)
              .forEach(a => {
                const question = questions.find(
                  (q: Question) => q.id === a.questionId
                );
                if (!isNullOrUndefined(question)) {
                  const category = res[0].find(
                    (c: Category) => c.id === a.categoryId
                  );
                  const qa = new QuestionAnswer(question, a);
                  qa.category = category.name;
                  questionAnswers.push(qa);
                }
              }
              // let ur = new UserResult(res2[1], result)
              // ur.questionAnswers = questionAnswers;
              // return ur;

              )
          )
      )
    );

0
iCV 5 Maret 2020, 16:18

1 menjawab

Jawaban Terbaik

Jadi... itu cukup banyak RxJS yang Anda dapatkan di sana.

Hal pertama yang pertama - Anda tidak berlangganan di dalam operator RxJS - Anda menyatukan yang dapat diamati.

Beberapa definisi

switchMap dan concatMap digunakan untuk menyalurkan hasil dari satu pengamatan ke pengamatan lainnya.

map adalah untuk mengubah nilai dari satu struktur ke struktur lainnya (mirip dengan konsep fungsi array dengan nama yang sama).

forkJoin menggabungkan beberapa yang dapat diamati dan mengembalikan satu hasil ketika semuanya selesai.

Kode Anda

Bahkan sebelum Anda mulai mencoba meluruskan kode Anda, saya akan merekomendasikan untuk berpikir tentang membagi setiap langkah menjadi fungsinya sendiri. Mudah-mudahan ini akan membantu Anda melihat aliran data dan memikirkan di mana dependensi Anda.

Saya telah mencoba mengonversi contoh asli Anda ke RxJS, tetapi sedikit tersesat ketika memikirkan apa yang sebenarnya ingin dicapai oleh setiap langkah.

Apa yang saya pastikan adalah bahwa Anda akan berakhir dengan pola yang sedikit seperti ini (saya berlangganan untuk keperluan demo ini - Anda akan mengembalikan yang dapat diamati):

result: string;

ngOnInit() {
  this.initialValue().pipe(
    switchMap(result => this.forkJoinOne(result)),
    switchMap(result => this.forkJoinTwo(result)),
    switchMap(result => this.forkJoinThree(result)),
    map(result => this.mapFour(result))
  ).subscribe(result => {
    this.result = result;
  });
}

private initialValue(): Observable<string> {
  return of('zero');
}

private forkJoinOne(result: string): Observable<string[]> {
  return forkJoin([
    of(`${result} one`),
    of('four')
  ]);
}

private forkJoinTwo(results: string[]): Observable<string[]> {
  return forkJoin([
    of(`${results[0]} two`),
    of(`${results[1]} five`)
  ]);
}

private forkJoinThree(results: string[]): Observable<string[]> {
  return forkJoin([
    of(`${results[0]} three`),
    of(`${results[1]} six`)
  ]);
}

private mapFour(results: string[]): string {
  return results.join(' ');
}

Setiap langkah yang dapat diamati telah dipindahkan ke dalamnya fungsinya sendiri - ini membantu Anda memikirkan tentang data apa yang perlu masuk dan apa yang keluar - Anda secara efektif membuat kontrak di antara setiap langkah.

switchMap hanya mengambil satu yang dapat diamati dan menyiapkan yang lain. map terakhir mengambil output dari observasi sebelumnya dan mengubahnya menjadi nilai yang berbeda.

Saya telah menggunakan string di sini semoga memudahkan untuk mengikuti aliran data. Saya akan merekomendasikan memulai dengan mencoba memahami contoh sederhana saya, dan kemudian membangun kembali fungsi Anda menggunakan prinsip-prinsip.

DEMO: https://stackblitz.com/edit/angular-eedbqg

Versi saya kira-kira sejalan dengan versi Anda dengan cara berikut:

nilai awal

this.rs.getResult(this.auth.token, route.params['id'])

forkJoinOne

Semua fork join harus melewati array atau objek. Saya lebih suka cara yang relatif baru untuk melewatkan objek, yang menentukan struktur nilai yang dipancarkan. (forkJoin({ a: myObs }) mengembalikan { a: value }).

forkJoin(
  this.quizs.getCategoriesQuiz(this.auth.token, res.quizId),
  this.accs.getAccount(res.userId)
)

forkGabungDua

forkJoin(
  this.accs.getUserDetails(results[1].access_token),
  this.as.getAnswers(this.auth.token)
)

forkJoinThree

Anda perlu mengonversi loop ini ke array yang dapat diamati, dan meneruskannya ke forkJoin.

results[0].forEach(cat => {
  this.cs.getQuestionsCategory(this.auth.token, cat.id)

petaEmpat

Anda perlu memilah peta Anda. Daripada forEach di sini, pilih filter dan map (fungsi array).

map(questions =>
  res2[1]
    .filter(ans => ans.userId === res[1].uid)
    .forEach(a => {
      const question = questions.find(q => q.id === a.questionId);
      if (!isNullOrUndefined(question)) {
        const category = res[0].find(c => c.id === a.categoryId);
        const qa = new QuestionAnswer(question, a);
        qa.category = category.name;
        questionAnswers.push(qa);
      }
    })
2
Kurt Hamilton 5 Maret 2020, 13:56