typescript 从 observable 返回数据的 Angular forEach 循环(Firebase 驱动)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46698328/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 04:57:39  来源:igfitidea点击:

Angular forEach Loop on returned data from observable (Firebase driven)

angulartypescriptfirebasefirebase-realtime-databaseobservable

提问by Xazzo

I have an observable from a service that returns an array of Payment objects, once I subscribe to my observable I perform a loop to add the amounts of each of the objects in the array. the problem I'm having that on the first load the loop is not being executed for what I assume is because the data has not yet arrived, however, if I trigger .next on the subject of the observable it workes fine.

我有一个来自返回 Payment 对象数组的服务的 observable,一旦我订阅了我的 observable,我就会执行一个循环来添加数组中每个对象的数量。我在第一次加载时遇到的问题是,我假设的循环没有被执行是因为数据尚未到达,但是,如果我触发 .next 对 observable 的主题,它可以正常工作。

The function of my component is like so:

我的组件的功能是这样的:

paymentsSource = new Subject;
payments$ = this.paymentsSource.asObservable();
paymetsData:Payment[] = new Array();
totalPaymentsAmoutn:number = 0;

ngOnInit() {
  this.payments$
    .subscribe(
     (dates:{startDate:number, endDate:number}) => {
     //Here I need to performe some UTC and Locale conversion to dates thet are being passed on          
      };
      //Here I subscribe to my service          
      this.paymentService.getPaymentsByDateRange(dates.startDate, dates.endDate)
       .subscribe(
         payments => {
          this.totalPaymentsAmoutn = 0;
          this.paymetsData = payments;
          this.paymetsData.forEach( payment => {
            this.totalPaymentsAmoutn += payment.amount;
           })
          });      
       });


//this.paymentsStartDate is declear some where else but it does exist previous to this point
this.paymentsSource.next({startDate:this.paymentsStartDate, endDate:this.paymentsStartDate});

}

//This function I can triger manualy on the view
onPaymentDateRangeChanged($event){
  this.paymentsSource.next({startDate:$event.startDate, endDate:$event.endDate});
}

My Service function looks like this:

我的服务功能如下所示:

  getPaymentsByDateRange(startDate:number, endDate:number):Observable<Payment[]>{
    let paymentsArray:Payment[] = [];
    this.af.list('/bookings')
      .subscribe(
        (bookings) => {
          for(let booking of bookings){
            if(booking.payments){
              for(let payment of booking.payments){
                //condition for date range
                if(startDate <= payment.date && payment.date <= endDate){
                  payment.booking_key = booking.$key; 
                  paymentsArray.push(payment);
                }
              }
            }
          }
          //Sorting payments cronologicaly
          paymentsArray.sort(function(paymentA, paymentB){
            return paymentA.date - paymentB.date
          });
        }
      );
    return Observable.of(paymentsArray);
  }

When the page loads I do get the array back and the view gets populated but the value for this.totalPaymentsAmoutn remains 0, if I trigger the function manually it returns the array and updates the this.totalPaymentsAmoutn perfectly.

当页面加载时,我确实取回了数组并填充了视图,但 this.totalPaymentsAmoutn 的值仍然为 0,如果我手动触发该函数,它将返回数组并完美地更新 this.totalPaymentsAmoutn。

I am a bit new with Observables and I thought that once I subscribe to it if new data gets emitted It should run the script and update the data, I don't understand why on the first load is not working. I really think it has to do with not being able to perform the forEach loop since the array is still empty and I think that once the web sockets get connected consequent updates get push fast enough?

我对 Observables 有点陌生,我认为一旦我订阅了它,如果发出新数据它应该运行脚本并更新数据,我不明白为什么在第一次加载时不起作用。我真的认为这与无法执行 forEach 循环有关,因为数组仍然是空的,我认为一旦 Web 套接字连接,后续更新的推送速度是否足够快?

采纳答案by LLai

There is a disconnect in your getPaymentsByDateRange()method. You are returning the paymentsArray before it is populated.

您的getPaymentsByDateRange()方法存在脱节。您在填充之前返回 PaymentsArray。

getPaymentsByDateRange(startDate: number, endDate: number): Observable<Payment[]>{
    let paymentsArray: Payment[] = [];
    this.af.list('/bookings')
        .subscribe(...); // asynchronous
    // since this.af.list.subscribe is asynchronous, this returns before that call is finished.
    return Observable.of(paymentsArray);
}

You should instead return the af.listobservable and subscribe only in the component. If you need to preprocess the data before the component uses it, you can use the rxjs .map operator

您应该改为af.list仅在组件中返回observable 和 subscribe 。如果需要在组件使用之前对数据进行预处理,可以使用 rxjs .map 操作符

import 'rxjs/add/operator/map'; // import map operator

getPaymentsByDateRange(startDate: number, endDate: number): Observable<Payment[]>{
    let paymentsArray: Payment[] = []; // you may want to move this inside the map. If you don't you may get duplicates everytime firebase emits the bookings
    return this.af.list('/bookings')
        .map(bookings => {
            for(let booking of bookings){
                if(booking.payments){
                    for(let payment of booking.payments){
                        //condition for date range
                        if(startDate <= payment.date && payment.date <= endDate){
                            payment.booking_key = booking.$key; 
                            paymentsArray.push(payment);
                        }
                    }
                }
            }
            //Sorting payments cronologicaly
            paymentsArray.sort(function(paymentA, paymentB){
                return paymentA.date - paymentB.date
            });
            return paymentsArray;
        });
}

This ensures that when you subscribe to this method, the onNext callback will only fire once the af.listobservable emits.

这确保当你订阅这个方法时,onNext 回调只会在af.listobservable 发出时触发。

 this.paymentService.getPaymentsByDateRange(dates.startDate, dates.endDate)
     .subscribe(payments => {
         // fires when af.list observable emits
     });