0

I'm trying to display some data using an ngFor. I want to have a filter on the displayed data using a toggle.

I have so far made the filter to work partially so far. Currently when I click on the toggle it indeed filters my data. For example: When I click on the toggle "Gym" it returns all the houses which has an amenity gym. But it does not filter the houses when i click on say more than one toggle e.g when i click on "Gym" and "Wifi" It should return a list of houses with both the amenity: Gym and Wifi

I expect the filter to return a list of houses when I click on more than one toggle e.g when i click on "Gym" and "Wifi" It should return a list of houses with both the amenity: Gym and Wifi

My Component is:

import { Component, OnInit } from '@angular/core';
import { Home } from '../../models/IHome.model';


@Component({
  selector: 'app-search',
  templateUrl: './homes.component.html',
  styleUrls: ['./homes.component.css']
})

export class HomesComponent implements OnInit {

  defaultSelection = 'All';
  searchText: any;
  address: string;
  counter = 0;
  homes: Home[];

  filterArgs: any = { type: '', address: '', car_type: '', amenity: '' };


  amenities = [
    {
      'name': 'Wifi',
      'value': false
    },
    {
      'name': 'Gym',
      'value': false
    },
    {
      'name': 'Swimming Pool',
      'value': false
    },
    {
      'name': 'Garden',
      'value': false
    }
  ];

  constructor() { }

  ngOnInit() {
    this.homes = [
      {
        'id': 1,
        'type': 'Villa',
        'price': 920000,
        'address': 'CMC',
        'area': 6292,
        'bedrooms': 2,
        'bathrooms': 2,
        'car_type': 'Cars',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil10.jpg'
      },
      {
        'id': 2,
        'type': 'Apartment',
        'price': 3000,
        'address': 'Summit',
        'area': 921,
        'bedrooms': 3,
        'bathrooms': 1,
        'car_type': 'Cars',
        'park_spots': 2,
        'amenity': ['Wifi'],
        'homeUrl': '../../assets/ezembil6.jpg'
      },
      {
        'id': 3,
        'type': 'Villa',
        'price': 820000,
        'address': 'Hayat',
        'area': 4921,
        'bedrooms': 2,
        'bathrooms': 2,
        'car_type': 'Trucks',
        'park_spots': 3,
        'amenity': ['Garden', 'Swimming Pool'],
        'homeUrl': '../../assets/ezembil8.jpg'
      },
      {
        'id': 4,
        'type': 'Apartment',
        'price': 420000,
        'address': 'Sarbet',
        'area': 3921,
        'bedrooms': 2,
        'bathrooms': 3,
        'car_type': 'Cars',
        'park_spots': 4,
        'amenity': ['Swimming Pool'],
        'homeUrl': '../../assets/ezembil1.jpg'
      },
      {
        'id': 5,
        'type': 'Villa',
        'price': 629000,
        'address': 'Mekhanisa',
        'area': 2921,
        'bedrooms': 1,
        'bathrooms': 1,
        'car_type': 'Vans',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil6.jpg'
      },
      {
        'id': 6,
        'type': 'Apartment',
        'price': 720000,
        'address': 'Bole',
        'area': 1921,
        'bedrooms': 3,
        'bathrooms': 3,
        'car_type': 'Bikes',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil5.jpg'
      }
    ];

  }

  amenityChange(item: any, e: any) {
    if (e.srcElement.checked === true) {
      console.log('checked');
      for (let i = 0; i < this.amenities.length; i++) {
        if (this.amenities[i].name === item) {
          this.amenities[i].value = true;
        }
      }
    }
    if (e.srcElement.checked === false) {
      console.log('unchecked');
      this.counter = 0;
      for (let i = 0; i < this.amenities.length; i++) {
        if (this.amenities[i].name === item) {
          this.amenities[i].value = false;
        }
      }
    }
    for (let j = 0; j < this.amenities.length; j++) {
      if (this.amenities[j].value === true) {
        this.filterArgs.amenity = this.amenities[j].name;
        this.counter = 1;
      }
    }
    if (this.counter === 0) {
      this.filterArgs.amenity = '';
    }
  }

}

My pipe is:

import { Pipe, PipeTransform } from '@angular/core';
import { Home } from '../models/IHome.model';

@Pipe({
    name: 'amenitiesFilter',
    pure: false
})

export class AmenitiesFilterPipe implements PipeTransform {

    transform(values: any[], filter: Home): any {
        /* console.log('amenities', values, filter); */
        if (!values || !filter || !filter.amenity) {
            return values;
        }
        return values.filter(item => {
            return item.amenity.indexOf(filter.amenity) !== -1;
        });
    }
}

My template is:

 <p *ngFor="let amenity of amenities">
   <label>{{ amenity.name }}</label>
   <label class="switch">
     <input type="checkbox" id={{amenity.name}} name="amenities" (change)="amenityChange(amenity.name, $event)">                      
     <span class="toggle round"></span>
   </label>
 </p>

  <ng-container *ngFor="let home of homes  | amenitiesFilter: filterArgs  
      | pricerangeFilter: 'price': min:max       
      | paginate: { itemsPerPage: 6, currentPage: p } ">

            <div class="homes" (click)="openDetails()">
                <img class="homes_content" src="{{ home.homeUrl }}" /><br>
                <div class="labels">
                    <label><i class="fa fa-map-marker fa-fw" aria-hidden="true"></i>&nbsp;{{ home.address }}</label><br>
                    <label><i class="fa fa-money fa-fw"
              aria-hidden="true"></i>&nbsp;{{ home.price | currency:"USD":"symbol" : "1.0"}}</label>
                </div>
                <hr>
                <button class="details"><i class="fa fa-tag fa-fw" aria-hidden="true"></i>&nbsp;{{ home.type }}</button>
                <label>&nbsp;SqFt:{{ home.area }}</label><br>
            </div>

   </ng-container>
   <pagination-controls (pageChange)="p= $event" style="float:right"></pagination-controls>

2 Answers 2

1

I changed a bit your code.. don't use pipe

homes: Home[];
 homesFiltered:Home[];

  filterArgs: any = { type: '', address: '', car_type: '', amenity: '' };


  amenities = [
    {
      'name': 'Wifi',
      'value': false
    },
    {
      'name': 'Gym',
      'value': false
    },
    {
      'name': 'Swimming Pool',
      'value': false
    },
    {
      'name': 'Garden',
      'value': false
    }
  ];

  constructor() { }

  ngOnInit() {
    this.homes = [
      {
        'id': 1,
        'type': 'Villa',
        'price': 920000,
        'address': 'CMC',
        'area': 6292,
        'bedrooms': 2,
        'bathrooms': 2,
        'car_type': 'Cars',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil10.jpg'
      },
      {
        'id': 2,
        'type': 'Apartment',
        'price': 3000,
        'address': 'Summit',
        'area': 921,
        'bedrooms': 3,
        'bathrooms': 1,
        'car_type': 'Cars',
        'park_spots': 2,
        'amenity': ['Wifi'],
        'homeUrl': '../../assets/ezembil6.jpg'
      },
      {
        'id': 3,
        'type': 'Villa',
        'price': 820000,
        'address': 'Hayat',
        'area': 4921,
        'bedrooms': 2,
        'bathrooms': 2,
        'car_type': 'Trucks',
        'park_spots': 3,
        'amenity': ['Garden', 'Swimming Pool'],
        'homeUrl': '../../assets/ezembil8.jpg'
      },
      {
        'id': 4,
        'type': 'Apartment',
        'price': 420000,
        'address': 'Sarbet',
        'area': 3921,
        'bedrooms': 2,
        'bathrooms': 3,
        'car_type': 'Cars',
        'park_spots': 4,
        'amenity': ['Swimming Pool'],
        'homeUrl': '../../assets/ezembil1.jpg'
      },
      {
        'id': 5,
        'type': 'Villa',
        'price': 629000,
        'address': 'Mekhanisa',
        'area': 2921,
        'bedrooms': 1,
        'bathrooms': 1,
        'car_type': 'Vans',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil6.jpg'
      },
      {
        'id': 6,
        'type': 'Apartment',
        'price': 720000,
        'address': 'Bole',
        'area': 1921,
        'bedrooms': 3,
        'bathrooms': 3,
        'car_type': 'Bikes',
        'park_spots': 1,
        'amenity': ['Gym'],
        'homeUrl': '../../assets/ezembil5.jpg'
      }
    ];
this.homesFiltered=this.homes;
  }


    amenityChange(amenity) {
      amenity.value=!amenity.value;
      let filterBy= this.amenities.filter((x)=>x.value==true).map((v)=>{
         return  v.name;
        });

      if(filterBy && filterBy.length){
        this.homesFiltered=this.homes.filter((x)=>x.amenity.some(r=> filterBy.indexOf(r) >= 0))
      }else{
        this.homesFiltered=this.homes;
      }
    }

and in the html change the checkbox to:

<input type="checkbox" id={{amenity.name}} [checked]="amenity.value" name="amenities" (change)="amenityChange(amenity)">    
Sign up to request clarification or add additional context in comments.

Comments

0

I'd ditch the pipe approach for that kind of filtering. There's too much code there for simple functionality...

on amenityChange(), add some code to filter the home by the selected amenities with true value. Hold the result on the component as filteredHomes, and loop through filteredHomes instead of homes. Remove everything else that is related to the filtering and whatever serves that pipe.

For example;

filteredHomes = homes;
.
.
.

amenityChange(item: any, e: any) {
    if (e.srcElement.checked === true) {
      console.log('checked');
      for (let i = 0; i < this.amenities.length; i++) {
        if (this.amenities[i].name === item) {
          this.amenities[i].value = true;
        }
      }
    }
    if (e.srcElement.checked === false) {
      console.log('unchecked');
      this.counter = 0;
      for (let i = 0; i < this.amenities.length; i++) {
        if (this.amenities[i].name === item) {
          this.amenities[i].value = false;
        }
      }
    }

    let filteredAmenities = this.amenities.filter(a => a['value'] == true);
    if (filteredAmenities.length == 0){
        this.filteredHomes = homes;
    }
    else{
        this.filteredHomes = [];
        for (let fa of filteredAmenities){
            this.filteredHomes = this.homes.filter(h => h['amenity'].indexOf(fa['name']) > -1)
            .concat(this.filteredHomes);
        }
        //Here you can remove duplicates however you like, here's an example with lodash
        this.filteredHomes = _.uniqBy(this.filteredHomes, 'id');
        }
    }

BTW, you would be able to remove the first part of amenityChange() if you'll bind each checkbox to a property and use types.

1 Comment

I opted to bind each checkbox to a property and used types as u suggested and it works just fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.