Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use snapshotChanges() method to get both key value and filter the data?

I'm working on an Angular Firebase Project, where I need to filter my database as well get the key values. Currently I'm using valueChanges() method in my service code (inside getUnreadBooks and getFavoriteBooks methods, as shown below) to get the data and filter it. But it gives me key value as 'undefined', when I try to get key value inside my template file. I tried to go with snapshotChanges() method, but can't work around how to use it to get key values along with filtering the data . Below are my Angular FirebaseService, home.component.ts (in which I am injecting my service code) and home.component.html (template file) Code respectively:

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class FirebaseService {

  books: Observable<any[]>;
  unreadBooks;
  favoriteBooks;

  constructor(private db: AngularFireDatabase) {}

  getBooks(){
        this.books = this.db.list('/books').valueChanges() as Observable<any[]>;
        return this.books;
    }       

  getFavoriteBooks(){
    this.favoriteBooks = this.db.list('/books').valueChanges() as Observable<any[]>;
    this.favoriteBooks = this.favoriteBooks.map(books => {
        const topRatedBooks = books.filter(item =>  item.rate>4);
        return topRatedBooks;
    })
    return this.favoriteBooks;
  }

  getUnreadBooks(){
    this.unreadBooks = this.db.list('/books').valueChanges() as Observable<any[]>;
    this.unreadBooks = this.unreadBooks.map(books => {
        const ub = books.filter(item => item.dateread == null);
        return ub;
    })
    return this.unreadBooks;
  }
}

Home.Component.ts file =>

import { Component, OnInit } from '@angular/core';
import { FirebaseService } from '../../services/firebase.service';

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

  //favorite Books
  favoriteBooks: any;
  unreadBooks: any;

  constructor(private firebaseService: FirebaseService) { }

  ngOnInit() {
    this.firebaseService.getFavoriteBooks()
        .subscribe(favBooks => {
            this.favoriteBooks = favBooks;
            console.log(this.favoriteBooks);
        })
    this.firebaseService.getUnreadBooks()
        .subscribe(ubBooks => {
            this.unreadBooks = ubBooks;
            console.log('Unread Books:', this.unreadBooks);
        })
  }

}

Home.component.html file =>

<mat-toolbar>
    My Top Rated Books
</mat-toolbar>
<mat-grid-list cols="3">
    <mat-grid-tile *ngFor="let book of favoriteBooks">
        <mat-card>
            <mat-card-header>
                <mat-card-title>
                    <h4>{{book.title}}</h4>
                </mat-card-title>
            </mat-card-header>
            <img mat-card-image src="{{book.imageUrl}}" alt="{{book.title}}">
            <mat-card-actions>
                <button mat-button mat-raised-button class="detailsButton" [routerLink]="['/book/'+book.$key]">
                    <i class="material-icons">visibility</i>Book Details</button>
                <button mat-button mat-raised-button class="editButton" [routerLink]="['/editbook/'+book.$key]">
                    <i class="material-icons">mode_edit</i>Edit Book</button>
            </mat-card-actions>
        </mat-card>     
    </mat-grid-tile>
</mat-grid-list>

<mat-toolbar>
    Books I have not read yet
</mat-toolbar>
<mat-grid-list cols="3">
    <mat-grid-tile *ngFor="let book of unreadBooks">
        <mat-card>
            <mat-card-header>
                <mat-card-title>
                    <h4>{{book.title}}</h4>
                </mat-card-title>
            </mat-card-header>
            <img mat-card-image src="{{book.imageUrl}}" alt="{{book.title}}">
            <mat-card-actions>
                <button mat-button mat-raised-button class="detailsButton" [routerLink]="['/book/'+book.$key]">
                    <i class="material-icons">visibility</i>Book Details</button>
                <button mat-button mat-raised-button class="editButton" [routerLink]="['/editbook/'+book.$key]">
                    <i class="material-icons">mode_edit</i>Edit Book</button>
            </mat-card-actions>
        </mat-card>     
    </mat-grid-tile>
</mat-grid-list>
like image 728
user3760959 Avatar asked Mar 06 '18 13:03

user3760959


5 Answers

This is with angular 9 and it's working fine.this is set up in the service.ts and get the data from particular .ts file and just normally reader the data into template

 getAll() {
    return this.database.list('/products').snapshotChanges()
    .pipe(map( action => action
      .map(a => {
        const key = a.payload.key;
        const data = a.payload.val();
        return  data;
      })));
  }

particular .ts

constructor(private productService: ProductService) {
    this.products$ = this.productService.getAll();
   }

particular .html

<tr *ngFor="let product of products$ | async" >
    <td> {{ product.title }}</td>
    <td> {{ product.price }}</td>
like image 58
Neelaka Rupun Avatar answered Nov 03 '22 13:11

Neelaka Rupun


Declare a function to add id to your Object :

documentToDomainObject = _ => {
    const object = _.payload.doc.data();
    object.id = _.payload.doc.id;
    return object;
}

And use it in your getBooks method :

getBooks(){
  this.books = this.db.list('/books').snapshotChanges()
  .pipe(map(actions => actions.map(this.documentToDomainObject)));

  return this.books;
}
like image 21
ibenjelloun Avatar answered Nov 03 '22 13:11

ibenjelloun


In my case I solved it by importing rxjs operator map, combined by .pipe

import { map } from 'rxjs/operators';

Example:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AngularFirestore } from 'angularfire2/firestore';
import { IdeaService } from './app.service';

import { config } from './app.config';
import { Idea } from './app.model';
import {
  AngularFirestoreDocument,
  AngularFirestoreCollection
} from 'angularfire2/firestore';

@Component({
selector: 'app-root',
template: `
    <ul>
        <li *ngFor="let idea of ideas | async">
            <pre>{{ idea | json }}</pre>
        </li>
    </ul>
`
})
export class AppComponent {
  public ideasCollection: AngularFirestoreCollection<Idea>;
  public ideas: Observable<any[]>;

  constructor(db: AngularFirestore, private ideaService: IdeaService) {
    this.ideasCollection = db.collection<Idea>(config.collection_endpoint);
    this.ideas = this.ideasCollection.snapshotChanges().pipe(
        map(actions => {
          return actions.map(a => {
            const data = a.payload.doc.data() as Idea;
            const id = a.payload.doc.id;
            return { id, ...data };
          });
        }));
    }
}
like image 4
Ruben Decuypere Avatar answered Nov 03 '22 14:11

Ruben Decuypere


You need to import the rxjs operator map import { map } from 'rxjs/operators'; // in rxjs 6 version

like image 3
Dilip Das Avatar answered Nov 03 '22 14:11

Dilip Das


I imported this import { map } from 'rxjs/operators' and this solved my problem

like image 1
Md Shadman Avatar answered Nov 03 '22 14:11

Md Shadman