简体   繁体   中英

angular 9 execute subscribe in code behind synchronously

I need to run a method with 2 parameters, each parameter is gotten through some form of subscribe function. the first is the collection which is gotten through the url from angular's page routing. The second is the dokument, this is the firebase's firestore document.

export class FirebaseDocument implements OnInit {
   collection: string;
   dokument: any;

   //== CONSTRUCTORS
  constructor(
    private route: ActivatedRoute,
    private _db: AngularFirestore
  ) {}

  //== Initialize
  ngOnInit() {
    console.log("__loading page component");
    this.route.params.subscribe(params => {
      this.collection = params["collection"];
    });
    console.log(this.collection);//collection populated correctly

    //load the document from AngularFirestore
    console.log("loading the document from firebase");
    let itemsCollection = this._db.collection(url).valueChanges();
    
    //subscribe to get the dok of the first document in the collection
    itemsCollection.subscribe(docArr => {
        this.dokument = docArr[0];
        console.log(this.dokument);//dokument is populated
    });
    
    console.log(this.dokument);//dokument is undefined
    this.doMultiParameterMethod(this.collection, this.dokument);
  }
}

this.collection populates perfectly fine; this.dokument is only populated inside the subscribe method

I need this to be populated by the time the next line is run. the console.log(this.dokument);

I have been dumbstruck by this because essentially the same code is used by the 2 subscribe methods but they don't behave the same way.

Sometimes a subscribe can be synchronous. This happens when the Observable is a ReplaySubject a BehaviorSubject or an Observable which has a shareReplay() pipe. (probably other options as well.

This will make the observable immediately fire on subscription. However, you should never count on this behavior, and always continue within your subscribe.. Or use pipes like mergeMap and create other observables which you can access in your template using the async pipe.

In your case. The this.route.params is obviously a 'replaying' Observable from which you get the latest value after subscribing. Otherwise you would have to wait for the params to change again until you get a value.

Your Database call cannot return an immediate response, because it's essentially a network request.


In your example code, you can update it to this, and use the async pipe in your template

export class FirebaseDocument implements OnInit {
   readonly collection$: Observable<string> = this.route.params.pipe(
     map((params) => params.collection)
   );

   readonly doc$: Observable<any[]> = this.db.collection(this.url).valueChanges().pipe(
     shareReplay({ refCount: true, bufferSize: 1 })
   );

   constructor(private route: ActivatedRoute, private db: AngularFirestore) {}

  ngOnInit() {
    // don't forget to unsubscribe
    combineLatest([
      this.collection$,
      this.doc$
    ]).subscribe((collection, document) => {
      this.doMultiParameterMethod(collection, document);
    }); 
  }
}

Maybe you should make the Observable a Promise, in your case would be the following :

export class FirebaseDocument implements OnInit {
 collection: string;
 dokument: any;

 //== CONSTRUCTORS
 constructor(
  private route: ActivatedRoute,
  private _db: AngularFirestore
 ) {}

 //== Initialize
 ngOnInit() {
  console.log("__loading page component");
  this.route.params.subscribe(params => {
   this.collection = params["collection"];
  });

  console.log(this.collection); //collection populated correctly

  this.getDokument().then(docArr => {
   this.dokument = docArr[0];
   this.doMultiParameterMethod(this.collection, this.dokument);
  });

 }

 getDokument(): Promise<any> {
  let itemsCollection = this._db.collection(url).valueChanges();
  return new Promise((resolve, reject) => {
   itemsCollection.subscribe((response: any) => {
    resolve(response);
   }, reject);
  });
 }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM