0

I have an Angular2 service as follows. I am trying to use this as a shared service between multiple components. If it matters, the components are binding directly to the public books, and selectedBook properties I want to cache the list of books and the currently selected book on the service and only call populate these on the first call.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

@Injectable()
export class BookService {

    public books: Book[];
    public selectedBook: Book;

    constructor(private http: Http,
        private router: Router) {

        this.http.get('/api/book/list')
            .subscribe(result => this.books = result.json() as Book[]);
    }

    public getBooks() {
        return this.books;
    }

    public setSelectedBook(id) {
        this.selectedBook = this.books.filter(x => x.id == id)[0];
    }
}

The problem is that sometimes I'm trying to read the array or call setSelecteBook(id) before the array is actually populated, which causes a crash. I have been trying to find an example of how to use Observables, but I can't find a scenario close enough to mine to be able to figure out how to adapt it to solve my problem.

halfer
  • 19,824
  • 17
  • 99
  • 186
Danny Ellis Jr.
  • 1,674
  • 2
  • 23
  • 38

3 Answers3

2

You can initialise the array books in the constructor with an empty array.

You can also do a workaround and check the length before accessing the array.

public setSelectedBook(id) {
    if(this.books.length > 0) {
        this.selectedBook = this.books.filter(x => x.id == id)[0];
    }
}
dkiriakakis
  • 148
  • 1
  • 7
  • 1
    Typescript doesn't have the safe evaluation operator `?.` – Christopher Moore Mar 08 '17 at 14:37
  • @ChristopherMoore is correct about the operator. But, I wrote out the null and length check the long way and all it did was cause the app to crash past this point; it is simply not setting the selected book when called. I need something to make it wait for the books[] to finish populating and then set the selected book afterward. – Danny Ellis Jr. Mar 08 '17 at 14:59
0

This doesn't initialise an array, it just tells the TypeScript compiler that this certain property is of this type. To actually have an array, instead of being undefined, you have to initialise it. This way the filter function will work:

@Injectable()
export class BookService {

    public books: Book[] = [];
    //...
}
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
0

As a cleaner solution i would subscribe from your book-detail component for example to the service method getBooksList.

Do the api call using this.bookService.getBooksList and as soon as the array is loaded select the desired book.

dkiriakakis
  • 148
  • 1
  • 7
  • I think I follow what you are saying, but I could use a little code for clarity. Also, I don't want to call to populate the books[] from the server every time the selected book changes. – Danny Ellis Jr. Mar 08 '17 at 15:16
  • You can have a look at this repo of mine, here: https://github.com/dkiriakakis/ng2-photoalbum Take a look into /src/app/components/photo-detail.component.ts – dkiriakakis Mar 08 '17 at 15:19