Need help getting filtering to work from http.get values


#1

I have been working on this for over a week and am finally at the edge of my sanity and could really use some help :slight_smile:

The code below works great in pulling in data via http.get and displaying in my app with nearly zero delay and scrolling with virtualScroll works great in keeping things fast. But, I get the runtime error item.toLowerCase is not a function .

data.ts

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

@Injectable()
export class Data {
  items: any;

  constructor(public http: Http) {}

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

    // don't have the data yet
    return new Promise(resolve => {
      this.http
        .get("https://www.soledadmemorial.com/api/v1/plaques")
        .map(res => res.json())
        .subscribe(data => {
          this.items = data;
          resolve(this.items);
        });
    });
  }
}

home.ts

import { Component } from "@angular/core";
import { IonicPage, NavController, LoadingController } from "ionic-angular";
import { Http } from "@angular/http";
import { Data } from "../../providers/data/data";


@IonicPage()
@Component({
  selector: "page-home",
  templateUrl: "home.html",
  providers: [Data]
})
export class HomePage {
  searchQuery: string = '';
  items: string[];

  constructor(
    public navCtrl: NavController,
    public http: Http,
    public dataService: Data,
    public loadingCtrl: LoadingController
  ) {
    this.loadData();
  }

  loadData() {
    this.presentLoadingDefault();  
    this.dataService.load().then(data => {
      this.items = data
    }); 
  }

  getItems(ev: any) {
    // Reset items back to all of the items
    this.loadData();

    // set val to the value of the searchbar
    let val = ev.target.value;

    // if the value is an empty string don't filter the items
    if (val && val.trim() != '') {
      this.items = this.items.filter((item) => {
        return (item.toLowerCase().indexOf(val.toLowerCase()) > -1);
      })
    }
  }

  ...
}

home.html

...
<ion-content>
  <ion-searchbar (ionInput)="getItems($event)"></ion-searchbar>
  <ion-list [virtualScroll]="items">
      <ion-item *virtualItem="let item" (click)="goToFeed(item)">
          <ion-thumbnail item-start>
             <img src="https://www.soledadmemorial.com/{{item.image_url}}">
          </ion-thumbnail>
          <h2>{{item.veteran_first}} {{item.veteran_last}}</h2>
          <p>{{item.veteran_rank}} {{item.veteran_branch}}</p>
          <button ion-button clear item-end><ion-icon name="arrow-dropright"></ion-icon> </button>
      </ion-item>
  </ion-list>
</ion-content>

To test the filter, I adjusted my home.ts file to the code below and it works as it should. "Notice the hard-code items list, changed the init of items from a string to items = [] and added the veteran_first into the filter code. So I am thinking I need to call the loadData from my data.ts file something like item.veteran_first .. But, my skill level is not there to figure that out.

Here is the test home.ts code.

import { Component } from "@angular/core";
import { IonicPage, NavController, LoadingController } from "ionic-angular";
import { Http } from "@angular/http";
import { Data } from "../../providers/data/data";


@IonicPage()
@Component({
  selector: "page-home",
  templateUrl: "home.html",
  providers: [Data]
})
export class HomePage {
  searchQuery: string = '';
  items = [];

  constructor(
    public navCtrl: NavController,
    public http: Http,
    public dataService: Data,
    public loadingCtrl: LoadingController
  ) {
    this.loadData();
  }

  loadData() {
    this.items = [
      {veteran_first: 'one'},
      {veteran_first: 'two'},
      {veteran_first: 'three'},
      {veteran_first: 'four'},
      {veteran_first: 'five'},
      {veteran_first: 'six'}
  ] 
  }

  getItems(ev: any) {
    // Reset items back to all of the items
    this.loadData();

    // set val to the value of the searchbar
    let val = ev.target.value;

    // if the value is an empty string don't filter the items
    if (val && val.trim() != '') {
      this.items = this.items.filter((item) => {
        return (item.veteran_first.toLowerCase().indexOf(val.toLowerCase()) > -1);
      })
    }
  }

  ...
}

Thank you for your help!!


#2

I’m going to speak frankly, and hopefully it will help you even though it might annoy you too. Your code is a mess. What you describe in your English prose is something that can be accomplished in literally three lines. So either that’s what you want to do and you got confused because you don’t understand Promises and Observables, or you want to do something different,

Your two biggest sins are the explicit promise construction antipattern, and sloppy typing.

I think you should delete all this code and do the following:

this.http.get<preciseTypeOfResponse>(url).map(response => filterFunction(response));

Define filterFunction with your filter, like toLowerCase or whatever you want. Either subscribe to that Observable or display response in the template using the async pipe.