Sergi
Sergi

Reputation: 793

Angular Observable, sharing data within components

I'm making my first App on Angular. I trying to filter a list of book objects by the gender attribute. I am having difficulty sharing data between components: the filteredData variable and the list of books FireBaseList.

I am trying to pass the filteredData variable from fixed-nav.component to books-grid.component. From my research, I know I need to make the books-grid.component "bookList" observable of any changes and have the bookList on fixed-nav.component.ts emit an event on any changes on this.bookList

However, I am unable to achieve this. I know there are many questions about observables already but none use Firebase. Hope anyone out there can help me, THANKS!!

fixed-nav.component that I use as filter

    import { Component, OnInit } from '@angular/core';
    import { BookService } from '../../../services/book.service';
    import { Book } from '../../../models/book';

    @Component({
      selector: 'fixed-nav',
      templateUrl: './fixed-nav.component.html',
      styleUrls: ['./fixed-nav.component.css']
    })
    export class FixedNavComponent implements OnInit {

      Genders: string[];
      bookList: Book[];

      constructor(private bookService: BookService) {

        this.Genders = ["Historia","Literatura", "Infantil", "Juvenil","Poesía","Narrativa"];      
      }

      filterByGender(gender: string){

        this.bookService.getBooks() // FIREBASE
        .snapshotChanges()
        .subscribe(item => {
          this.bookList = [];
          item.forEach(element => {
            let x = element.payload.toJSON();
            x["$key"] = element.key;
            this.bookList.push(x as Book)
          })
            const filteredData = this.bookList.filter( function(element){
            return element.gender == gender;
            });
            return filteredData; // I WANT TO SHARE THIS DATA
        })
      }

      ngOnInit() {
      }

    }

books-grid.component.ts

import { Component, OnInit } from '@angular/core';

import { BookService } from '../../services/book.service';
import { Book } from '../../models/book';


@Component({
 templateUrl: './books-grid.component.html',
 styleUrls: ['./books-grid.component.css']
  })

 export class BooksGridComponent implements OnInit {

  bookList: Book[];

   constructor(private bookService: BookService) {}

ngOnInit() {
  this.bookService.getBooks()  // FIREBASE
  .snapshotChanges()
  .subscribe(item => {
    this.bookList = [];
      item.forEach(element => {
      let x = element.payload.toJSON();
      x["$key"] = element.key;
      this.bookList.push(x as Book)
        })
               // SHARED DATA HERE
    })
  }
}

book.service.ts

import { Injectable } from '@angular/core';

import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';

import { Book } from '../models/book';  // Model


@Injectable({
  providedIn: 'root'
})

export class BookService {

 bookList: AngularFireList<any>;
 selectedBook: Book = new Book(); // Temporally save selected Book

constructor(private firebase: AngularFireDatabase) { }

 getBooks(){
    return this.bookList = this.firebase.list('books');
  }
}

books.model.ts

    export class Book {
      $key: string;
      title: string;
      author: string;
      gender: string;
      language: string;
      image: string;
      likes: number;
      price: number;
      new: boolean;
    }

Upvotes: 5

Views: 6124

Answers (2)

sagat
sagat

Reputation: 1406

you could utilize another shared service, where you update the booklist and then subscribe to any change event of this filtered booklist from your BooksGridComponent or any other component in need. I edited your code a little bit.

Something like this:

Create a new Service, where you return the filtered BooksList as an Observable, so any component can subscribe to it

import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs'; // For rxjs 6

@Injectable({
   providedIn: 'root'
})
export class SharedDataService {

   private filteredBookList: Subject<Book[]> = new Subject<Book[]>();

   constructor() {}

   public getFilteredBookList(): Observable<Book[]> {
     return this.filteredBookList.asObservable();
   }

   public setFilteredBookList(books: Book[]): void {
     this.filteredBookList.next(books);
   }
}

fixed-nav.component.ts:

Here you simply call the SharedDataService and set the filteredBookList.

this.sharedDataService.setFilteredBookList(filteredData); 

And now in your books-grid.component.ts:

Here you are going to subscribe to any changes of the filteredBookList

this.sharedDataService.getFilteredBookList().subscribe((books: Book[]) => {
  // Do Something with the filtered BookList from SharedDataService
})

Upvotes: 2

coder
coder

Reputation: 8712

In your book.service.ts create a filterdData variable and function to set filterdData like below. Import Subject using import { Subject } from 'rxjs';

 filteredData  = new Subject<any>();

  setFilteredData(data) {
    this.filteredData.next(data);
  }

And then in your fixed-nav.component.ts after done filtration set that filtered data like below

 this.bookService.setFilteredData(filteredData);

Finally subscribe to the filteredData in book.service.ts from books-grid.component.ts like below and assign the data into variable that you want.

 constructor(private bookService: BookService) {
     bookService.filteredData.subscribe((data)=>{
      // Assign the data into variable that you want e.g this.filteredData = data;
    })
 }

Hope this will helps you!

Upvotes: 7

Related Questions