Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement searching and filtering in Ionic

I am fetching data from a JSON file that's stored locally within my project. The JSON string is an array of locations, whose structure is like so:

JSON file

{
    "locations": [
        {
            "title": "ABC",
            "latitude": -1.2596551,
            "longitude": 36.7066604,
            "routes":["22","23","105","115"],
            "fare":[],
            "matatu":[]
        },
        {
            "title": "Adams Arcade",
            "latitude": -1.3004204,
            "longitude": 36.7770793,
            "routes": ["2","4W","102","24","24C","111"],
            "fare":[],
            "matatu":[]
        },
        {
            "title":"Aga Khan Hospital",
            "latitude":-1.2620125,
            "longitude":36.8186399,
            "routes":["11A","11F","106","107","116"],
            "fare":[],
            "matatu":[]
        }
    ]
}

Rather than filtering the data on the fly with a pipe, I have implemented a function in my provider to handle this. Here's the code:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import { Geolocation } from '@ionic-native/geolocation';

@Injectable()
export class LocationProvider {

  data: any;

  constructor(public http: Http, public geolocation: Geolocation) {

  }

  load(){

    if(this.data){
      return Promise.resolve(this.data);
    }

    return new Promise(resolve => {
        this.http.get('assets/data/locations.json').map(res => res.json()).subscribe(data => {
          this.data = this.applyHarvesine(data.locations);
          this.data.sort((locationA, locationB) => {
            return locationA.distance - locationB.distance;
          });
          resolve(this.data);
        });
    });

  }

  filterLocations(searchTerm){
    return this.data.filter((location) => {
      return location.title.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
    });
  }

}

The filterLocations() function is to take in searchTerm and returns an array containing only the elements that match the search criteria.

Here's the .ts and .html code for the page where it is rendered.

.ts

import { Component } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';
import { LocationProvider } from '../../providers/location/location';
import { PlaceDetailsPage } from '../place-details/place-details';


@IonicPage()
@Component({
  selector: 'page-places',
  templateUrl: 'places.html',
})
export class PlacesPage {

  searchTerm: string = '';



  constructor(public navCtrl: NavController, public locations: LocationProvider) {
  }

  ionViewDidLoad() {
    this.setFilteredLocations();
  }

  setFilteredLocations(){
    return this.locations.filterLocations(this.searchTerm);
  }

}

.html

<ion-content>
    <ion-searchbar [(ngModel)]="searchTerm" (ionInput)="setFilteredLocations()"></ion-searchbar>
    <ion-list no-lines>
        <button ion-item *ngFor="let location of locations.data">
            <ion-avatar item-left>
                <ion-icon name="pin"></ion-icon>
            </ion-avatar>
            <h2>{{location.title}}</h2>
            <p>{{location.distance}} km</p>
        </button>
    </ion-list>
</ion-content>

When the user types on the searchbar, setFilteredLocation() is called to trigger the filtering of data. The problem is, nothing happens.

Where have I gone wrong? My hunch is that something is amiss in the setFilteredLocation() function, but I have no clue what it is.

Is there an alternative approach to searching and filtering that doesn't involve pipes?

like image 673
mondieki Avatar asked Oct 16 '17 09:10

mondieki


2 Answers

You are doing it wrong.

  1. You do return this.data.filter(...). It has no effect to your origin array (this.data). It just return a new array and does not make change to this.data. See the filter function doc
  2. If you want to make change to your data you need to add: this.data = this.data.filter(...). But if you do like that, you will fall in other mistake. Your this.data will lost some element after filter and it can not be revert when you reset the filter.

So you should do like that:
In your component:

allData = []; //Store all data from provider
filterData = [];//Store filtered data

ionViewDidEnter(){
  this.allData = this.locations.data;
  this.filterData = this.allData;
}

setFilteredLocations(){
    this.filterData = this.allData.filter((location) => {
      return location.title.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
    });
}

And in your template use filterData in ngFor:

<button ion-item *ngFor="let location of filterData">
like image 121
Duannx Avatar answered Sep 30 '22 13:09

Duannx


Yo didn't send the $event here. So try as shown below.

.html

 <ion-searchbar [(ngModel)]="searchTerm" 
(ionInput)="setFilteredLocations($event)"></ion-searchbar>

.ts

 setFilteredLocations(ev: any){
    let val = ev.target.value;

    if (val && val.trim() !== '') {
      return this.locations.filterLocations(val);
    }

  }

You can see the official sample code here.

like image 41
Sampath Avatar answered Sep 30 '22 14:09

Sampath