How do I filter an array with TypeScript in Angular 2?

450,272

Solution 1

You need to put your code into ngOnInit and use the this keyword:

ngOnInit() {
  this.booksByStoreID = this.books.filter(
          book => book.store_id === this.store.id);
}

You need ngOnInit because the input store wouldn't be set into the constructor:

ngOnInit is called right after the directive's data-bound properties have been checked for the first time, and before any of its children have been checked. It is invoked only once when the directive is instantiated.

(https://angular.io/docs/ts/latest/api/core/index/OnInit-interface.html)

In your code, the books filtering is directly defined into the class content...

Solution 2

You can check an example in Plunker over here plunker example filters

filter() {

    let storeId = 1;
    this.bookFilteredList = this.bookList
                                .filter((book: Book) => book.storeId === storeId);
    this.bookList = this.bookFilteredList; 
}

Solution 3

To filter an array irrespective of the property type (i.e. for all property types), we can create a custom filter pipe

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: "filter" })
export class ManualFilterPipe implements PipeTransform {
  transform(itemList: any, searchKeyword: string) {
    if (!itemList)
      return [];
    if (!searchKeyword)
      return itemList;
    let filteredList = [];
    if (itemList.length > 0) {
      searchKeyword = searchKeyword.toLowerCase();
      itemList.forEach(item => {
        //Object.values(item) => gives the list of all the property values of the 'item' object
        let propValueList = Object.values(item);
        for(let i=0;i<propValueList.length;i++)
        {
          if (propValueList[i]) {
            if (propValueList[i].toString().toLowerCase().indexOf(searchKeyword) > -1)
            {
              filteredList.push(item);
              break;
            }
          }
        }
      });
    }
    return filteredList;
  }
}

//Usage

//<tr *ngFor="let company of companyList | filter: searchKeyword"></tr>

Don't forget to import the pipe in the app module

We might need to customize the logic to filer with dates.

Share:
450,272
Code-MonKy
Author by

Code-MonKy

Updated on July 08, 2022

Comments

  • Code-MonKy
    Code-MonKy almost 2 years

    ng-2 parent-child data inheritance has been a difficulty for me.

    What seems that could be a fine working practical solution is filtering my total array of data to an array consisting of only child data referenced by a single parent id. In other words: data-inheritance becomes data filtering by one parent id.

    In a concrete example this can look like: filtering a books array to only show the books with a certain store_id.

    import {Component, Input} from 'angular2/core';
    
    export class Store {
      id: number;
      name: string;
    }
    
    export class Book {
      id: number;
      shop_id: number;
      title: string;
    }
    
    @Component({
      selector: 'book',
      template:`
        <p>These books should have a label of the shop: {{shop.id}}:</p>
    
        <p *ngFor="#book of booksByShopID">{{book.title}}</p>
      `
    ])
    export class BookComponent {
      @Input()
      store: Store;
    
      public books = BOOKS;
    
      // "Error: books is not defined"
      // ( also doesn't work when books.filter is called like: this.books.filter
      // "Error: Cannot read property 'filter' of undefined" )
      var booksByStoreID = books.filter(book => book.store_id === this.store.id)
    }
    
    var BOOKS: Book[] = [
      { 'id': 1, 'store_id': 1, 'name': 'Dichtertje' },
      { 'id': 2, 'store_id': 1, 'name': 'De uitvreter' },
      { 'id': 3, 'store_id': 2, 'name': 'Titaantjes' }
    ];
    

    TypeScript is new to me, but I think I am close to making things work here.

    (Also overwriting the original books array could be an option, then using *ngFor="#book of books".)

    EDIT Getting closer, but still giving an error.

    //changes on top:
    import {Component, Input, OnInit} from 'angular2/core';
    
    // ..omitted
    
    //changed component:
    export class BookComponent implements OnInit {
      @Input() 
      store: Store;
    
      public books = BOOKS;
    
      // adding the data in a constructor needed for ngInit
      // "EXCEPTION: No provider for Array!"
      constructor(
        booksByStoreID: Book[];
      ) {}
    
    
      ngOnInit() {
        this.booksByStoreID = this.books.filter(
          book => book.store_id === this.store.id);
      }
    }
    
    // ..omitted
    
  • Code-MonKy
    Code-MonKy about 8 years
    Makes sense. I get the error "Error: ngOnInit is not defined" after adding your piece of code, importing OnInit and adding booksByStoreID = Book[]; in the component.
  • Thierry Templier
    Thierry Templier about 8 years
    I think that it's rather: booksByStoreID: Book[];
  • Code-MonKy
    Code-MonKy about 8 years
    Doesn't work either. Should I puts this in a constructor maybe? I tried this, then I get an error complaining about the ]
  • Thierry Templier
    Thierry Templier about 8 years
    Could you add in your question (in an edit paragraph for example) the code your tried? It's difficult for me to guess what you tried ;-) Thanks!
  • Code-MonKy
    Code-MonKy about 8 years
    I seem to get a DI Exception, while before buidling the data filtering retrieving the store id wasn't a problem at all and printed in the template. Maybe you can find something I overlooked in my edit?
  • Thierry Templier
    Thierry Templier about 8 years
    It's because you should use this: booksByAimID: Book[]; constructor() {}. ``booksByAimID: Book[];` is a property declaration not a constructor parameter. Angular2 leverages constructor parameter for dependency injection...
  • Code-MonKy
    Code-MonKy about 8 years
    Thanks! The filtering completly works :) A new issue arose though. When clicking in the parent component such that another store is selected, the old store_id remains and the list of books stay the same...
  • Thierry Templier
    Thierry Templier about 8 years
    Great! You need to implement ngOnChanges to detect this. When store_id will be updated, this method will be called and you can execute the same thing than ngOnInit...
  • Code-MonKy
    Code-MonKy about 8 years
    ngOnChanges(store) { this.store.id = store[id].currentValue }; leaves me with a "RefferenceError". Any idea?
  • Thierry Templier
    Thierry Templier about 8 years
    Not sure what you're trying to do ;-) I thought of this: ngOnChanges() { this.booksByStoreID = this.books.filter( book => book.store_id === this.store.id); }
  • Code-MonKy
    Code-MonKy about 8 years
    Thanks, my app is functional :) I thought I could overwrite the input values since ngOnInit will be called after ngOnChanges. But maybe I misunderstood and this is the only way.
  • stackUser44
    stackUser44 about 7 years
    when I try this solution showing as " Cannot read property 'filter' of undefined"
  • Chanaka Amarasinghe
    Chanaka Amarasinghe almost 6 years
    in my case 'books' is an array and I need output also a array
  • sandeep
    sandeep about 4 years
    Can we put an OR (||) condition here like (book => book.store_id === 2|| book.store_id === 5) ?