5

I'm using Angular CLI + Firebase + AngularFire2 have been trying to figure out how to query a single object from Firebase using the key.

The basic flow would be to show a list of items, then clicking on an item would bring up the detail view for that particular item.

This should be a pretty common use case but the AngularFire 2 docs don't seem to give any clear examples on how I could do this.

I'm trying to use a service to query the items but I can't quite get it to work.

Component

// COMPONENT

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { AngularFire, FirebaseObjectObservable} from 'angularfire2';
import { Subscription } from 'rxjs';
import { ItemsService } from '../items.service';

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

  private subscription: Subscription;
  private item;

  constructor(private af: AngularFire,
              private route: ActivatedRoute,
              private router: Router,
              private itemsService: ItemsService) { }


  ngOnInit() {
    this.subscription = this.route.params.subscribe(
      (param: any) => {
        let id = param['id'];
        console.log('Key: '+ id);
        this.item = this.itemsService.getItem(id);
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

Service

import { Injectable } from '@angular/core';
import { Headers, Http, Response } from "@angular/http";
import { AngularFire, FirebaseObjectObservable, FirebaseListObservable, FirebaseApp } from 'angularfire2';
import 'rxjs/Rx';

@Injectable()
export class ItemsService {

  private items: FirebaseListObservable<any[]>;
  private item: FirebaseObjectObservable<any>;

  constructor(private af: AngularFire) {}

  getItems(num) {
    this.items = this.af.database
      .list('/items', { query: { limitToLast: num | 20} } )
      .map( (arr) => { return arr.reverse() } ) as FirebaseListObservable<any[]>;
    return this.items;
  }

  getItem(id: string) {
    this.item = this.af.database.object('/items/'+id);
    this.item.subscribe(item => {
        console.log(item);
        return item;
      });
  }

}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
LifeOnLars
  • 398
  • 3
  • 15

1 Answers1

2

This should do it:

// COMPONENT

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { AngularFire, FirebaseObjectObservable} from 'angularfire2';
import { Subscription } from 'rxjs';
import { ItemsService } from '../items.service';

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

  private subscription: Subscription;
  private item;

  constructor(private af: AngularFire,
              private route: ActivatedRoute,
              private router: Router,
              private itemsService: ItemsService) { }

  
  ngOnInit() {
    // get id synchronously, don't need it more then once
    const key: string;
    this.route.params.take(1).subscribe(param => key = param["id"]);
    this.subscription = this.itemsService.getItem(id)
      .subscribe(item => this.item = item)
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}


@Injectable()
export class ItemsService {
  ...

  getItem(id: string) {
return this.af.database.object('/items/'+id);
  }

}

Try to subscribe to the Observable where you need the data. Your service methods should return Observable and not Subscription (unless you really have a good reason to do otherwise).

Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • Thanks for this. I changed the const to a let as it kept complaining that 'const' declarations must be initialized and used getItem(key) rather than ID. I also had to declare private item = {}.It is working now however I get a 5-7 second delay before the data appears. – LifeOnLars Oct 10 '16 at 08:52
  • The rest of the page loads pretty fast but total time is like 10-11 seconds. Not sure if it's the local dev setup that is slow or if this just isn't a very good way of querying an object. The amount of data is trivial there's only 8 text nodes in the sample data. The list view loads the same data at the moment and it's much much faster. Are we not meant to fetch objects by $key from Firebase? – LifeOnLars Oct 10 '16 at 09:53
  • It's CLI issue https://github.com/angular/angular-cli/issues/1980, once you upload precompiled app to the server it will be fast. You can check in Dev Tools> Network, then filter to only show WebSocket traffic (WS)... – Sasxa Oct 10 '16 at 11:20
  • I did check the network settings and it's not the size of the build that's the problem (even though it's 4MB). The app loads in about 4 seconds and then it takes another 8-10 seconds for the Websocket to finish which I thought was strange. I'll try to upload it and see if there's still the same delay on the websocket traffic – LifeOnLars Oct 10 '16 at 11:45