Search code examples
angularangularfire2

AngularFireDocument doesn't respond to route changes before route is accessed a second time


In this tiny sample app can anyone explain to me why the detail.component.ts doesn't get the correct/any data passed to the template before route is accessed a second time. On first try the component is blank, and afterwards the data from the previous route is passed.

I don't understand since the correct ID is passed to the getQuote() method and valueChanges() does return an Observable. So in my mind it should fetch a new Document every time a new ID is passed from the router to the method.

enter image description here

app.service.ts

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { Quote } from './quote';

@Injectable({
  providedIn: 'root'
})
export class AppService {
  quote$: Observable<Quote>;
  private quotesCollection: AngularFirestoreCollection<Quote> = this.afs.collection('quotes');
  quotes$: Observable<Quote[]> = this.quotesCollection.valueChanges({idField: 'id'});
  private quoteDocument: AngularFirestoreDocument<Quote>;

  constructor(private afs: AngularFirestore) { }

  getQuote(id) {
    this.quoteDocument = this.quotesCollection.doc(id);
    this.quote$ = this.quoteDocument.valueChanges();
  }

  save(quote: Quote) {
    this.quotesCollection.add(quote).then(r => console.log(r));
  }

  delete(id: string) {
    this.quotesCollection.doc(id).delete().then(r => console.log(r));
  }
}

list.component.ts

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { AppService } from '../app.service';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListComponent {
  quotes$ = this.appService.quotes$;

  constructor(private appService: AppService) { }

  delete(id: string) {
    this.appService.delete(id);
  }
}

detail.component.ts

import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AppService } from '../app.service';

@Component({
  selector: 'app-detail',
  templateUrl: './detail.component.html',
  styleUrls: ['./detail.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DetailComponent implements OnInit {
  quote$ = this.appService.quote$;

  constructor(private appService: AppService,
              private activatedRoute: ActivatedRoute) { }

  ngOnInit() {
    const id = this.activatedRoute.snapshot.paramMap.get('id');
    this.appService.getQuote(id);
  }
}

detail.component.html

<nav>
  <a [routerLink]="['/list']" routerLinkActive="active">Back to list</a>
</nav>

<div *ngIf="quote$ | async as quote">
  <h1>{{quote.quote}}</h1>
</div>

Solution

  • You use weird way to access quote$. I think better at:

    `

      getQuote(id) {
        this.quoteDocument = this.quotesCollection.doc(id);
        return this.quoteDocument.valueChanges();
      }
    

    `

    Then at the component: `

      ngOnInit() {
        const id = this.activatedRoute.snapshot.paramMap.get('id');
        this.quote$ = this.appService.getQuote(id);
      }
    

    `

    It's clearer for me and I think it solve your problem