Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase error when subscribing to ref.on(‘value’, callback); | Property 'subscribe' does not exist on type '(a: DataSnapshot, b?: string) => any'

I am using firebase realtime database in angular. I am trying to get some data from firebase server in realtime: (Code from a service)

getData(child){
        return firebase.database().ref(child).on('value', (snapshot) => {
            console.log(snapshot.val())
        })
    }

and subscribing to above function in my component:

this.examinerService.getData('batches/names').subscribe(
      (batches) => {
        this.batches = batches.val();
      }
    )

Which is giving me error:

Property 'subscribe' does not exist on type '(a: DataSnapshot, b?: string) => any'

I have tried using ref().once() which works fine but I want realtime behavior.

Update: Currently I am using database().ref().on('value', (snapshots) => { console.log(snapshots.val()); }); in my component which works fine but I want to do it inside my service and subscribe to it in my components. Someone told me that its not an observable so you can't subscribe to it. I am new to angular so I don't know how to make an observable and bind snapshots with it.

like image 464
Robert Williams Avatar asked Feb 05 '18 19:02

Robert Williams


People also ask

How can I check if a value exists already in a Firebase data class Android?

child(busNum). exists() tests for the existence of a value at location BusNumber/<busNum> . It will not be true unless busNum is one of the keys created by push() .

How do I get DataSnapshot data?

A DataSnapshot contains data from a Database location. Any time you read data from the Database, you receive the data as a DataSnapshot . A DataSnapshot is passed to the event callbacks you attach with on() or once() . You can extract the contents of the snapshot as a JavaScript object by calling the val() method.

What is function of child () used for while accessing database in JavaScript?

The API docs for child() says: Gets a Reference for the location at the specified relative path. The method is helping you build a path to a reference. Without the call to child() in your second example, the reference will be at the root of the database, rather than under "/posts".


2 Answers

The function getData is returning the passed callback instead of an Observable which is what the code seems to be expecting. You can modify the function as such in order to return an Observable that you can .subscribe() to.

import { Observable } from 'rxjs/Observable';

getData(child) {

  return Observable.create(subscriber => {
    const ref = firebase.database().ref(child);

    const callbackFn = ref.on('value',
      // emit a value from the Observable when firebase data changes
      (snapshot) => subscriber.next(snapshot.val()),

      // error out the Observable if there is an error
      // such as permission denied
      error => subscriber.error(error)
    );

    // The function passed to Observable.create can return a callback function
    // which will be called when the observable we created is unsubscribed from.
    // Just as we used `ref.on()` previously our callback function calls `ref.off`
    // to tell firebase that we are no longer interested in the changes
    return () => ref.off('value', callbackFn);
  });
}
like image 165
adamduren Avatar answered Sep 22 '22 02:09

adamduren


Let's say our data structure looks something like this

{
  batches: {
    names: [
      {
        first: 'FirstName',
        last: 'LastName'
      },
      {
        first: 'FirstName1',
        last: 'LastName1'
      }
    ]
  }
}

For name we might have an interface that looks like this

export interface Name { first: string; last: string; }

Then we have a service that looks something like this

import { Injectable } from '@angular/core';
import { Name } from "./name";
import { AngularFireDatabase, AngularFireList } from "angularfire2";


@Injectable()
export class NameService {

  constructor(private db:AngularFireDatabase) { }

  getAllNames(): AngularFireList<Name> {
      return this.db.list('batch/name');
  }

}

Last our component can look something like this

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

@Component({
  selector: 'app-name-list',
  template: `
  <div *ngFor="let name of names$">
    {{ name.last }}, {{ name.first }}
  </div>
`
})

export class NameListComponent implements OnInit {
  names$: AngularFireList<Name>;

  constructor(private nameService: NameService) {}

  ngOnInit() {
    this.names$ = nameService.getAllNames();
  }
}

Angular uses websockets to update in realtime. I am not sure but I think this might be the behavior you're looking for.

like image 40
Woot Avatar answered Sep 24 '22 02:09

Woot