简体   繁体   中英

How do you handle the order of functions in Angular?

So I have an Angular component.

With some array objects containing data I want to work with:

  books: Book[] = [];
  reviews: Review[] = [];

This is what my ngOnInit() looks like:

  ngOnInit(): void {
    this.retrieveBooks();
    this.retrieveReviews();
    this.setRatingToTen();
  }

With this I write Books, and Reviews to object arrays. This is done through a "subscription" to data through services:

  retrieveBooks(): void {
    this.bookService.getAll()
      .subscribe(
        data => {
          this.books = data;
        },
        error => {
          console.log(error);
        }
      );
  }

  retrieveReviews(): void {
    this.reviewService.getAll()
      .subscribe(
        data => {
          this.reviews = data;
        },
        error => {
          console.log(error);
        });
  }

So this next function I have is just an example of "working with the data".

In this example, I just want to change all of the totalratings to 10 for each Book:

  setRatingToTen(): void {
    this.books.forEach(element => {
      element.totalrating = 10;
    });
    console.log(this.books);
  }

The problem I have been trying to wrap my head around is this:

this.books is an empty array .

I THINK the reason is because this function is running before the data subscription.

IF this is the case, then my understanding of ngOnInit must not be right.

I thought it would call the function in order.

Maybe that's still the case, it's just that they don't complete in order.

So my questions are:

1. Why is it an empty array?

(was I right? or is there more to it?)

2. How do Angular developers write functions so they operate in a desired order?

(since the data needs to be there so I can work with it, how do I avoid this issue?)

(3.) BONUS question:

(if you have the time, please and thank you)

My goal is to pull this.reviews.rating for each book where this.reviews.title equals this.books.title, get an average score; and then overwrite the "0" placeholder of this.books.totalrating with the average. How could I re-write the setRatingToTen() function to accomplish this?

Here is one of solution using forkJoin method in rxjs.

you can check this for details https://medium.com/@swarnakishore/performing-multiple-http-requests-in-angular-4-5-with-forkjoin-74f3ac166d61

Working demo: enter link description here

ngOnInit:

ngOnInit(){
    this.requestDataFromMultipleSources().subscribe(resList => {
        this.books = resList[0];
        this.reviews = resList[1];
        this.setRatingToTen(this.books,this.reviews);
    })
}

forkJoin method:

public requestDataFromMultipleSources(): Observable<any[]> {
    let response1 = this.retrieveBooks();
    let response2 = this.retrieveReviews();
    // Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
    return forkJoin([response1, response2]);
  }

Other methods:

retrieveBooks(): void {
    this.bookService.getAll()
      .subscribe(
        data => {
          this.books = data;
        },
        error => {
          console.log(error);
        }
      );
  }

  retrieveReviews(): void {
    this.reviewService.getAll()
      .subscribe(
        data => {
          this.reviews = data;
        },
        error => {
          console.log(error);
        });
  }
  
  setRatingToTen(books, reviews): void {
    this.books.forEach(element => {
      element.totalrating = 10;
    });
    console.log(this.books);
  }

Angular makes heavy use of observables to handle variety of asynchronous operations. Making server side requests (through HTTP) is one of those.

Your first two questions clearly reflect you are ignoring the asynchronous nature of observables .

Observables are lazy Push collections of multiple values . detailed link

Means observable response would be pushed over time in an asynchronous way. You can not guarantee which of the two distinct functions would return its response first.

Having said that, rxjs library (Observables are also part of this library and angular borrowed them from here) provides a rich collection of operators that you can use to manipulate observables .

With the above explanation, here is one by one answer to your questions.

1. Why is it an empty array?

Because you are thinking in terms of synchronous sequential flow of code, where one method would get called only after the other has finished with its working. But here retrieveBooks and retrieveReviews both are making asynchronous (observable) calls and then subscribing to it. This means there is no guarantee when their response would be received. Meanwhile the hit to setRatingToTen had already been made, at that point in time books array was empty.

2. How do Angular developers write functions so they operate in a desired order?

Angular developer would understand the nature of asynchronous observable calls, and would pipe the operators in an order so that they are sure they have the response in hand before performing any further operation on the observable stream.

(3.) BONUS question:

Your requirement specifies that you must first have the response of both observables at hand before performing any action. For that forkJoin rxjs operator suits your need. Documentation for this operator say

One common use case for this is if you wish to issue multiple requests on page load (or some other event) and only want to take action when a response has been received for all. detailed link

Not sure about your average score strategy, but here is an example code how you would achieve your purpose.

 ngOnInit(){ let req1$ = this.bookService.getAll(); let req2$ = this.reviewService.getAll(); forkJoin(req1$, req2$).subscribe(([response1, response2])=>{ for(let book of response1) //loop through book array first { for(let review of response2) //inner loop of reviews { if(book.title == review.title) { //complete your logic here.. } } } }); }

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