1
votes

Once again I think I lack understanding in Angular 4!

I have a page, accessed by a route and I'm trying to fetch a post by url-slug OnInit. The post resides in an array of Promises which I cache from the previous page (which is a list of posts page). The list page works fine. All my content caches and is read from a cache. I've tried switching this off, same problem occurs.

Here is the service that fetches the posts.

articles: Promise<Article[]>;

/**
   * fetch a list of articles
   */
  getArticles(): Promise<Article[]> {
    // cached articles
    if (this.articles) {
      return this.articles;
    }

    // no articles fetched yet, go get!
    return this.articles 
            = this.http.get(this.url, this.options)
                       .toPromise()
                       .then(response => response.json() as Article[])
                       .catch(this.handleError);
  }


  /**
   * fetch a single article by url slug
   * @param slug
   */
  getArticle(slug: string): Promise<Article> {
    return this.getArticles()
               .then(articles => articles.find(article => article.slug == slug));
  }

and this is what gets it on the SinglePost component:

article: Article

ngOnInit(): void {
    this.getArticle();
  }

  getArticle(): void {
    this.route.paramMap
        .switchMap((params: ParamMap) => this.articleService.getArticle(params.get('name')))
        .subscribe(article => this.article = article);
  }

html:

<a href="" class="post-cat  text-uppercase">{{ article.category }}</a>

The posts (article as i've called it) are definitely present, and are definitely being read from the cache fine.

Moreover, although I get an error, the page still actually displays the correct article (I can confirm the article must be fetching correctly because of this), so I feel like it's trying to access the article object before its populated, I thought angular would handle that?

My error is:

ERROR TypeError: Cannot read property 'category' of undefined at Object.eval [as updateRenderer] (PostComponent.html:4) at Object.debugUpdateRenderer [as updateRenderer] (core.es5.js?de3d:13105) at checkAndUpdateView (core.es5.js?de3d:12256) at callViewAction (core.es5.js?de3d:12599) at execComponentViewsAction (core.es5.js?de3d:12531) at checkAndUpdateView (core.es5.js?de3d:12257) at callViewAction (core.es5.js?de3d:12599) at execComponentViewsAction (core.es5.js?de3d:12531) at checkAndUpdateView (core.es5.js?de3d:12257) at callViewAction (core.es5.js?de3d:12599)

2
post your HTML code - Sajeetharan
first, why do you have .toPromise()? second, why do you do return this.articles = this.http... instead of return this.http....? - mast3rd3mon
Posted, there's more, but you only need one example. The rest access the object exactly the same for different properties. - simon_www
@mast3rd3mon because i'm using the tour of heroes example to learn. promises work for me, so i'd like to use promises. - simon_www
i really dont get why their tutorial doesnt show the simple ways of doing things. its a weird way of doing things. a simple .map() and .subscribe() is all you ever need for returning and reading json data from a http call in angular - mast3rd3mon

2 Answers

4
votes

Try to use elvis operator ?.:

<a href="" class="post-cat  text-uppercase">{{ article?.category }}</a>

It will access the category property only if article is defined.

0
votes

Try like this :

use then() method instead of subscribe()

service.ts

articles: Promise<Article[]>;

/**
   * fetch a list of articles
   */
getArticles(): Promise<Article[]> {
    // cached articles
    if (this.articles) {
        return this.articles;
    }

    // no articles fetched yet, go get!
    return this.articles = this.http.get(this.url, this.options)
        .toPromise()
        .then((response) => response.json())
        .catch(this.handleError);
}


/**
 * fetch a single article by url slug
 * @param slug
 */
getArticle(slug: string): Promise<Article> {
    return this.getArticles()
        .then(articles => articles.find(article => article.slug == slug));
}

component.ts

getArticle(): void {
    this.route.paramMap.switchMap((params: ParamMap) => {
        this.articleService.getArticle(params.get('name'))
            .then(result => {
                console.log('result', result);
                this.article = result
            });
    })
}