Search code examples
angularangular5ngforobservers

Get Count of Item from Object in Observer Angular 5


This question is reference to my previous query: ngFor length in Angular 5

MyService:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { environment } from '../../../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { FeedsService } from './feeds.service';
import { filter } from 'rxjs/operators/filter';
import { Feed } from './Feed';

@Injectable()
export class MyService {
  feeds: Observable<Feed[]>;
  private _feeds : BehaviorSubject<Feed[]>;
  private baseUrl: string;
  Fakes: any;
  private dataStore : {
    feeds: any
  };
  Reals: number;

    // Using Angular DI we use the HTTP service
    constructor(private http: HttpClient) {
      this.baseUrl  = environment.API_ENDPOINT + 'feeds';
      this.dataStore = { feeds: [] };
      this._feeds = <BehaviorSubject<Feed[]>>new BehaviorSubject([]);
      this.feeds = this._feeds.asObservable();
    }
  
    loadAll() {
      this.http.get(this.baseUrl).subscribe(feeds => {
        this.dataStore.feeds = feeds;
        console.log(feeds.length);
        this.Reals = feeds.filter(feed => feed.feed_type !== '').length;
        console.log(this.Reals);
        this.Fakes = feeds.length - this.Reals;
        console.log(this.Fakes);
        this._feeds.next(Object.assign({}, this.dataStore).feeds);
      }, error => console.log('Could not load feeds.'));
    }    
      
    change(feeds) {
      this._feeds.next(feeds);
    }

}

Here in myService, I am able to get the total no. feeds and pass it to my component.

Now within my feeds, I have a field/item called feed_type so I am trying to get the count of feed_type =="" and feed_type !="".

As you can see in the above code I have named them as Reals => feed_type != ' ' and Fakes => feed_type == ' '

I am not able to pass the Reals and Fakes value to my Component and then to the View.

Feeds Component:

import { Component, OnInit } from '@angular/core';
import { environment } from '../../environments/environment';
import { MyService } from '../shared/services/my-service.service';
import { FeedsService } from '../shared/services/feeds.service';
import { Feeds } from '../shared/services/feeds';
import { Feed } from '../shared/services/feed';
import { Observable } from 'rxjs/Observable';


@Component({
  selector: 'app-feeds',
  templateUrl: './feeds.component.html',
  styleUrls: ['./feeds.component.scss']
})

export class FeedsComponent implements OnInit {

  feeds: Observable<Feed[]>;
  Reals: boolean;
  Fakes: boolean;
  selectedFeedType = '';
  realcount: number;


  constructor(private myService: MyService, private feedsService: FeedsService) {
    this.selectedFeedType = 'All';
    this.Reals = true;
    this.Fakes = true;
  }

  ngOnInit() {
    this.feeds = this.myService.feeds;
    this.realcount = this.myService.Reals;
    console.log(this.myService.Reals);
    this.myService.loadAll();
  }


  SelectedFeedsType(event: any) {
    this.selectedFeedType = event.target.value;
    if (this.selectedFeedType === 'All') {
      this.Reals = true;
      this.Fakes = true;
    } else if (this.selectedFeedType === 'Reals') {
      this.Reals = true;
      this.Fakes = false;
    } else if (this.selectedFeedType === 'Fakes') {
      this.Reals = false;
      this.Fakes = true;
    }
  }

}

Help Appreciated, thanks.


Solution

  • As @alex commented the problem is loadAll method is asynchronous as it is using HttpClient.Get

    When you call loadAll() it executes and continues with the next line of code in your component, it does not wait for the HttpClient.Get method to complete.

    You need to update your service code to work with subjects(abservable)

    A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.

    • First you will have to update your Reals property to be of type Subject();

    • Then you need to emit the reals value.

    • You also need to subscribe to the Reals property so you can be notified when the value changes:

    Service:

    import { Subject } from "rxjs/Subject";
    
    public Reals = new Subject<number>();
    
    loadAll() {
          this.http.get(this.baseUrl).subscribe(feeds => {
            this.dataStore.feeds = feeds;
            console.log(feeds.length);
            this.Reals.next(feeds.filter(feed => feed.feed_type !== '').length);       
          }, error => console.log('Could not load feeds.'));
        } 
    

    Component:

    ...
    this.myService.Reals.subscribe((reals)=>
    {
        console.log(reals);
    });
    

    Note the this.Reals.next(feeds.filter(feed => feed.feed_type !== '').length); line.

    For further reading you can search online for "Managing State in Angular Applications" for some more examples.