0

I'd like to have a service in an Angular2 app that does all the database work. All the tutorials I see use http instead of firebase so they aren't really helpful. I have the service constructor pull the data I'll need from the database, however when a page tries to get data from the service, it doesn't get anything because the database call hasn't finished yet. I'm starting to feel like this post, that there isn't any point in putting network calls in a service, that they should all be duplicate code in each controller instead of putting all the DB code in 1 service. Here are my files:

lesson.service.ts

import { Injectable, OnInit } from '@angular/core';
import { AngularFireDatabase, FirebaseObjectObservable } from 'angularfire2/database';

export class Lesson {
  constructor(public id: number, public buttons: Object[]) {  }
}

@Injectable()
export class LessonService implements OnInit {

  private lessons: Lesson[] = [];
  constructor(public db: AngularFireDatabase) {
  }

  ngOnInit() {
    this.db.object('/lessons', { preserveSnapshot: true }).subscribe(lessons => {
      lessons.forEach(lesson => {
        this.lessons.push(new Lesson(
          lesson.key,
          lesson.val()['buttons']
        ));
      });
    });
  }

  getLessons() {
    console.log('getting lessons', this.lessons);
    return this.lessons;
  }

  getLesson(id: number): Lesson {
    // return new Promise()
    console.log('getLesson', this.lessons);
    return this.lessons.filter(lesson => {
      return lesson.id === id;
    })[0];
  }
}

lesson.ts

import { Component, OnInit, Input } from '@angular/core';
import { Lesson, LessonService } from '../lesson.service';

@Component({
  selector: 'lesson',
  templateUrl: './lesson.html',
  styleUrls: ['./lesson.scss']
})
export class LessonComponent implements OnInit {
  @Input() lessonID: number = 0;

  lesson: Lesson;
  constructor(public service: LessonService) {  }

  ngOnInit() {
    this.lesson = this.service.getLesson(this.lessonID);
    console.log('view lessons', this.lesson);
  }
}

lesson.html

<lesson [lessonID]="selectedId"></lesson>

When I try to load the lesson component, it sees that there is no lessons because this.lessons isn't filled out from the DB from the service's constructor. The only 'solution' I see is to remove the lesson service, and do DB calls every page load, which in my mind defeats the purpose of doing a single page app.

Community
  • 1
  • 1

2 Answers2

0

It may not be a good idea to call getLesson inside your ngOnInit as the service that you injected may still initializing as well.

I had the same problem with my code before but after moving my code to ngAfterViewInit(), it fixed the problem.

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

export class LessonComponent implements OnInit, AfterViewInit {
  @Input() lessonID: number = 0;

  lesson: Lesson;
  constructor(public service: LessonService) {  }

  ngOnInit() {

  }

  ngAfterViewInit() {
   this.lesson = this.service.getLesson(this.lessonID);
    console.log('view lessons', this.lesson);
  }
}
Christian Abella
  • 5,747
  • 2
  • 30
  • 42
  • I tried this and still have the same problem, I think I got it to work in a personal project today by calling getLessons() from the view, which calls the getLessons() from the service. I'll answer my question for reference on Monday when I get back to work. It works, but feels wrong somehow, but I kinda like how it works. – unixnerd777 May 12 '17 at 12:53
0

I guess I posted the wrong ts and html files, but still had the issue with the files I posted. You can see the issue more with these files, and this is what I did to fix it. I call a function from the view that goes all the way to the service. I'm guessing this is a bad practice, but it's the only thing I found to work.

lesson.service.ts

import { Injectable, OnInit } from '@angular/core';
import { AngularFireDatabase, FirebaseObjectObservable } from 'angularfire2/database';

export class Lesson {
  constructor(public id: number, public buttons: Object[]) {  }
}

@Injectable()
export class LessonService implements OnInit {

  private lessons: Lesson[] = [];
  public buttons: object[];
  constructor(public db: AngularFireDatabase) {
    this.db.object('/lessons', { preserveSnapshot: true }).subscribe(lessons => {
      this.lessons = [];
      lessons.forEach(lesson => {
        this.lessons.push(new Lesson(
          lesson.key,
          lesson.val()['buttons']
        ));
      });
    });
  }

  ngOnInit() {
  }

  getLessons() { return this.lessons; }

  getLesson(id: number): Lesson {
    return this.lessons.filter(lesson => {
      return lesson.id === id;
    })[0];
  }

}

lesson.ts

    import { Component } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Lesson, LessonService } from './lesson.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  private lessons: Lesson[];
  private lesson: Lesson = new Lesson(0, []);
  private errorMessage: string;
  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private service: LessonService
  ) {}

  getLessons() { return this.service.getLessons(); }

  gotoLesson(id) { this.router.navigate(['/lesson', id]); }

  getButtons() { return this.service.buttons; }

  doCLick(btnID) {
    console.log('clicked', btnID);
  }
}

lesson.html

<md-toolbar>
  <button md-button routerLink="" routerLinkActive="active">Home</button>
  <button md-button [mdMenuTriggerFor]="lessonsMenu">Lessons </button>
  <md-menu #lessonsMenu="mdMenu" [overlapTrigger]="false">
    <button md-menu-item *ngFor="let lesson of getLessons()" (click)="gotoLesson(lesson.id)">Lesson {{lesson.id}}</button>
  </md-menu>
  <span class="toolbar-spacer"></span>
  <button md-mini-fab color="primary" *ngFor="let button of getButtons()" (click)="doClick(button.id)" style="margin-right: 10px;">
    <md-icon>{{button.icon}}</md-icon>
  </button>
  <button md-mini-fab style="margin-left: 10px;">
    <md-icon>search</md-icon>
  </button>
</md-toolbar>