Angular - obiekt ze subskrybcji jest undefined w ngAfterViewInit

0

Witam.
Potrzebuje nakierowania jak ogarnąć temat subskrypcji, aby obiekt nie był undefined w ngAfterViewInit(). Korzystam z Angular material. Pobieram z API aktualne filtry usera i jeden z elementów tego filtru to sortowanie. Pobrane sortowanie chce przypisać do MatSort, które jako @ChildView() jest widoczne dopiero w AfterViewInit(), a tam filtr jest undefined, ponieważ jeszcze nie skończył "subskrybować"

  @ViewChild(MatSort, { static: true }) matSort: MatSort;

  constructor(
    public dialog: MatDialog,
    _matDialogRef: MatDialogRef<ProductsSelectionComponent>,
    @Inject(MAT_DIALOG_DATA) data: { trigger: ElementRef, documentType: number },
    private queryParameters: QueryParametersService,
    private cdr: ChangeDetectorRef
  ) {

    this.subcription.add(this.queryParameters.load(`products:${this.documentType}`, 1).subscribe(result => {
      this.queryParameters.set(result);
    }));

}

 // W TYM MIEJSCU this.parameters JEST undefined
  ngAfterViewInit() {
    if (this.parameters.orderBy) {
      this.matSort.sort({ id: this.parameters.orderBy.split(' ')[0], start: this.parameters.orderBy.split(' ')[1], disableClear: false } as MatSortable);
    }
    this.cdr.detectChanges();
  }

Da się zrobić jakiegoś await na subskrybcji żeby poczekać z AfterViewInit() aż obiekt nie będzie undefined?

0

Nie, nie da się zrobić awaita na subskrypcji żeby poczekać (nie w takim znaczeniu w jakim myślę, że mówisz). Natomiast możesz przenieść subskrypcję do ngAfterViewInit i tam ustawić ViewChildem to co potrzebujesz. Sposobów jest jeszcze kilka:

  1. Przenieść subscribe do miejsca w którym odpalasz Dialog i przekazać do MAT_DIALOG_DATA.
  2. W dialogu zrobić ngIfa i pokazać MatSorta wtedy kiedy dane będą już dostarczone
  3. Jeżeli już bardzo chcesz się bawić z asyncami i awaitami, to Observable ma metodę toPromise która zwraca Promisa.

Takie małe uwagi:
W konstruktorze nia masz dostępu do pól bindowanych @Input. Zakładam, że documentType właśnie w taki sposób jest dostarczany. Subskrypcję przenieś najwcześniej do ngOnInit w component lifecycle. 5 linijka jest kompletnie niepotrzebna. Taki już totalny offtop, bo sam kiedyś dużo dodawałem do subskrypcji - unikałbym zbierania wszystkich subskrypcji w jednym miejscu i, albo wykorzystywałbym async pipe tam gdzie się da, albo takeUnil / take (operatory)

0

No właśnie, czy to nie jest tak, że jeśli przeniosę subskrypcję do ngAfterViewInit to ona nie będzie się odświeżać pod czas zmiany? To jest dialog, documentType jest podawane w @Inject(MAT_DIALOG_DATA) data: { trigger: ElementRef, documentType: number },. Ten trigger to element DOM koło którego ten dialog ma się pojawić.

Punkt 1. wydaje się być najlepszym rozwiązaniem ale co w kwestii gdy obiekt się zmieni (ktoś pofiltruje inaczej), odświeżą się dane? Będę musiał zrobić dwie subskrypcje? Jedną na wywołanie dialogu, a drugą w samym dialogu, aby nasłuchiwać zmiany obiektu? 🤔

0

No to w sumie w czym problem, żeby zasubskrybować w ngAfterViewInit? To się różni "tylko" tym, że ngAfterViewInit jest odpalany jak już widok się wyrenderuje. Czemu miałaby się nie "zmieniać" podczas zmiany? Obsevable dostarcza Ci value stream - który ma gdzieś, gdzie subksrybujesz, będzie dostarczał Ci wartości, do czasu aż nie odsubskrybujesz / źródło będzie istnieć. Przez to, czasami można się nadziać na wycieki pamięci.

Jeżeli rzeczywiście z punktu widzenia np. UX lepiej to przenieść przed wywołanie Dialogu, to zawsze możesz state jakoś przekazać do Dialogu (coś jak @Input) a w samym Dialogu zrobić subscribe w afterViewInit z skip(1) żeby pominąć pierwszą wartość. Wtedy ta pierwsza będzie pipe'owana take(1). Wszystko zależy od tego, co chcesz osiągnąć i jak ma to działać. Też z tego powodu, że jeżeli nie dasz take(1) to każda nowa wartość spowoduje Ci otwarcie nowego Dialogu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1