2

I have a service that call a REST endpoint:

import { Task } from './task';
import { TaskStatus } from './task-status';
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

@Injectable()
export class TaskService {
    constructor(private http: Http){
    }
    getTasks() {
        return this.http.get('http://localhost:8080/tasks').map(res => res.json()).map(rest => rest._embedded.tasks);
    }
}

The endpoint returns a result like this:

{
  "_embedded": {
    "tasks": [
      {
        "title": "zxc",
        "description": "zxc",
        "status": "New",
        "_links": {
          "self": {
            "href": "http://localhost:8080/tasks/1"
          },
          "task": {
            "href": "http://localhost:8080/tasks/1"
          }
        }
      },
      {
        "title": "asd",
        "description": "qweqwe",
        "status": "New",
        "_links": {
          "self": {
            "href": "http://localhost:8080/tasks/2"
          },
          "task": {
            "href": "http://localhost:8080/tasks/2"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/tasks"
    },
    "profile": {
      "href": "http://localhost:8080/profile/tasks"
    }
  },
  "page": {
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "number": 0
  }
}

I use the service in this component:

@Component({
  selector: 'app-mycomp',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.css']
})
export class MyComponent implements OnInit {
  tasks: Array<Task>;

  constructor(private taskService:TaskService) {
    taskService.getTasks()
    .subscribe(tasks => this.tasks = tasks, 
                err => console.error(err), 
                () => console.log('done'));
  }
}

And the template looks like this;

<task-details *ngFor="let task of tasks" [task]="task"></task-details>

This works as expected, but when I try to use a pipe in the template:

<task-details *ngFor="let task of tasks | WithStatus: TaskStatus.New" [task]="task"></task-details>

I got the error "Cannot read property 'filter' of undefined".

Here is the pipe implementation:

import { Pipe, PipeTransform } from '@angular/core';
import { TaskStatus } from './task-status';

@Pipe({ name: 'WithStatus', pure: true })
export class TaskStatusFilter implements PipeTransform{
    transform(value: any, ...args: any[]): any {
        console.log(value);// value is undefined
        return value.filter(item => item.status == args[0]);
    }
}
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
Evgeni Dimitrov
  • 21,976
  • 33
  • 120
  • 145

2 Answers2

3

On initial change detection cycle when ajax haven't been completed that time it tries to evaluate bindings and pass tasks value as in undefined to WithStatus pipe and it throws an error. For such case you have to handle it inside a Pipe only.

@Pipe({ name: 'WithStatus', pure: true })
export class TaskStatusFilter implements PipeTransform{
    transform(value: any, ...args: any[]): any {
        console.log(value);// value is undefined
        return (value || []).filter(item => item.status == args[0]);
    }
}

The other way would be you should inject task-details DOM inside DOM tree until ajax gets succeeded using *ngIf structural directive.

<template [ng-if]="tasks">
  <task-details 
    *ngFor="let task of tasks | WithStatus: TaskStatus.New" 
    [task]="task">
  </task-details>
</template>

Instead of <template> you can also use <ng-container> which allows to use the same syntax as inline *ngIf:

<ng-container *ngIf="tasks">
  <task-details 
    *ngFor="let task of tasks | WithStatus: TaskStatus.New" 
    [task]="task">
  </task-details>
</ng-container>
yurzui
  • 205,937
  • 32
  • 433
  • 399
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • Thanks @yurzui sir, I didn't know about `ng-container`, I don't find any resource mentioning the same, could you provide a reference please, Appreciate your help :) – Pankaj Parkar Sep 24 '16 at 16:02
  • Unfortunately there is no good documentation about `ng-container`. The best reference is this https://github.com/angular/angular/blob/2.1.0-beta.0/modules/%40angular/core/test/linker/ng_container_integration_spec.ts – yurzui Sep 24 '16 at 16:07
  • @yurzui cool, once again I appreciate your help, you work at Angular 2 team? – Pankaj Parkar Sep 24 '16 at 16:08
1

You can try *ngIf="task". It will not initialize your pipe until data arrives.

<div *ngIf="tasks" >
<task-details *ngFor="let task of tasks | WithStatus: TaskStatus.New" 
              [task]="task"></task-details>
</div>
micronyks
  • 54,797
  • 15
  • 112
  • 146