Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array of objects piped into my custom filter always shows as length of zero

Tags:

angular

ionic2

Am looping over an array of object in my view like this (without the pipe I get the whole list displayed):

  <ion-list>
    <ion-item *ngFor= "let category  of (productCategories | onlyhomecategory) " >
      <ion-icon name="arrow-forward" item-right></ion-icon>
      {{category.title}}
    </ion-item>
  </ion-list>

I am feeding the array into a custom pipe called onlyhomecategory to filter out any categories that don't have a home property like this:

import {Injectable, Pipe} from '@angular/core';
@Pipe({
  name: 'onlyhomecategory'

})
@Injectable()
export class OnlyHomeCategories {
  transform(productCategories: any[] , args: any[]) {

    console.log(productCategories.length);  //retuns 0

    //If I hard-code/mock the array, pipe works as expected.

    return productCategories.filter(category => category.home);
  }
}     

This is the shape of how the array looks like:

[
  {
    home: 1,
    title: "Greevy Swaney",
    text: "Find the right area",
  },
  {
    home: 2,
    title: "Taney Wearx",
    text: "Choose the right system",
  },
  {
    title: "Tine rider",
  },
  {
  title: "Shade",
  }
];

This is the service:

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class ProductCategories {
  data: any = null;
  constructor(public http: Http) {}

  load() {
    if (this.data) {
      // already loaded data
      return Promise.resolve(this.data);
    }

    // don't have the data yet
    return new Promise(resolve => {
      this.http.get('https://xxxxxxxxxxxxxxxxxxxxxxxx')
        .map(res => res.json())
        .subscribe(data => {
          this.data = data;
          resolve(this.data);
        });
    });
  }
}

This is the view component:

import {Page, NavController} from 'ionic-angular';
import {Inject} from '@angular/core'
import {ProductCategories} from '../../providers/product-categories/product-categories'
import {OnlyHomeCategories} from '../../pipes/only-home-categories'

@Page({
  templateUrl: 'build/pages/product-category/product-category.html',
  pipes: [OnlyHomeCategories]
})

export class ProductCategoryPage {
  public productCategories = [];

  constructor(public nav: NavController, private prodCat: ProductCategories)     {

this.prodCat.load()
.then(data => {
  for (var key in data) {
    if (data.hasOwnProperty(key)) {
      this.productCategories.push(data[key]);
    }
  }
  console.log(`this.productCategories.length: ${this.productCategories.length}`); // this return correct length
});

} }

Am not sure where am going wrong. Without the custom pipe, my view shows all the categories but I can't seem to get the pipe to work.

like image 504
user1275105 Avatar asked Mar 13 '23 01:03

user1275105


1 Answers

Angular doesn't monitor array content changes. Because you initialize productCategories with an empty array and later add elements to this same array, change detection doesn't detect the change and doesn't invoke the pipe again.

pure: false

You can make the pipe impure, so it gets called every time change detection runs, not only when the pipe input changes

@Pipe({
  name: 'onlyhomecategory',
  pure: false

})
export class OnlyHomeCategories {
  transform(productCategories: any[] , args: any[]) {

    console.log(productCategories.length);  //retuns 0

    return productCategories.filter(category => category.home);
  }
}     

disadvantages:

Making a pipe impure has performance implications, because the pipe is called a lot (every time Angular runs change detection). To not cause this too much harm, ensure that the pipe doesn't do expensive work. For example try to cache the result of expensive calculations as long as the relevant inputs haven't changed. You need to keep track of previous and new values yourself.

different object

You can also make Angular detect the change, by not only adding content but creating a new - different - array.

this.prodCat.load()
.then(data => {
  let arr = [];
  for (var key in data) {
    if (data.hasOwnProperty(key)) {
      arr.push(data[key]);
    }
  }
  this.productCategories = arr;
  console.log(`this.productCategories.length: ${this.productCategories.length}`); // this return correct length
});

disadvantages:

If the array is large, copying can be expensive.

Hint

If you modify an array that already contains values you can create a copy using slice():

this.prodCat.load()
.then(data => {
  for (var key in data) {
    if (data.hasOwnProperty(key)) {
      this.productCategories.push(data[key]);
    }
  }
  this.productCategories = this.productCategories.slice();
  console.log(`this.productCategories.length: ${this.productCategories.length}`); // this return correct length
});

Plunker example

like image 122
Günter Zöchbauer Avatar answered Apr 08 '23 14:04

Günter Zöchbauer