【问题标题】:How to Create Product filter based of checkbox in angular如何基于角度中的复选框创建产品过滤器
【发布时间】:2021-03-02 23:04:47
【问题描述】:

我想制作一个复选框过滤器,其中有一个作物列表和地区列表。

如下所示:

实际上我想要的是,当我取消选中右侧的 RICE 时,我应该隐藏所有包含 RICE 的地区名称。此外,我想将过滤逻辑添加到区域复选框,例如当我取消选中 Thane 时,在右侧它应该隐藏包含 Thane 的 Rice 卡,而不是所有 Rice Card。

我尝试了逻辑,但是当我编辑我的对象数组时,它显示了两个复选框。我不想要两个具有相同名称的复选框。 让我向您展示我的输出:

在这里我与你们分享我的代码库:

1. crop.model.ts

export class Crop {
    name: string;
    checked: boolean;
    district: string
    subCategory: Subcategory[];
}

export class Subcategory {
    id: number;
    name: string;
    checked: boolean;
}

2。 crop.data.ts

import { Crop } from "./crop.model";

export const CROPS: Crop[] = [
    {
        name: "Rice",    // I want this Rice 
        checked: true,
        district: "Thane",
        subCategory: [
            {
                id: 1,
                name: "Basmati",
                checked: true
            },
            {
                id: 2,
                name: "Ammamore",
                checked: true
            }
        ]
    }, {
        name: "Rice",  // also this one but on clicking on single Checkbox with name as Rice
        checked: true,
        district: "Nashik ",
        subCategory: [
            {
                id: 1,
                name: "Basmati",
                checked: true
            },
            {
                id: 2,
                name: "Ammamore",
                checked: true
            }
        ]
    },
    {
        name: "Wheat",
        checked: true,
        district: "Nashik",
        subCategory: [
            {
                id: 1,
                name: "Durum",
                checked: true
            },
            {
                id: 2,
                name: "Emmer",
                checked: true
            }
        ]
    },
    {
        name: "Barley",
        checked: true,
        district: "Ratnagiri",
        subCategory: [
            {
                id: 1,
                name: "Hulless Barley",
                checked: true
            },
            {
                id: 2,
                name: "Barley Flakes",
                checked: true
            }
        ]
    }
];

3. crop.service.ts

import { Injectable } from "@angular/core";

import { Observable, of } from "rxjs";

import { Crop } from "../shared/crop.model";
import { CROPS } from "../shared/crop.data";

@Injectable({
  providedIn: "root"
})
export class CropService {
  constructor() { }

  crops: Crop[] = CROPS;

  getAllCrops(): Observable<Crop[]> {
    return of(this.crops);
  }

  getCrop(name: string): Observable<any> {
    const crop = this.crops.filter(crop => crop.name === name)[0];

    return of(crop);
  }
}

4. all-trades.component.html

<app-header></app-header>
<div
  fxLayout="row"
  fxLayout.lt-md="column"
  fxLayoutAlign="space-between start"
  fxLayoutAlign.lt-md="start stretch"
>
  <div class="container-outer" fxFlex="20">
    <div class="filters">
      <section class="example-section">
        <span class="example-list-section">
          <h1>Select Crop</h1>
        </span>
        <span class="example-list-section">
          <ul>
            <li *ngFor="let crop of crops$ | async">
              <mat-checkbox
                [checked]="crop.checked"
                (change)="onChange($event, i, crop)"
              >
                {{ crop.name }}
              </mat-checkbox>
            </li>
          </ul>
        </span>
      </section>

      <section class="example-section">
        <span class="example-list-section">
          <h1>Select District</h1>
        </span>
        <span class="example-list-section">
          <ul>
            <li *ngFor="let crop of crops$ | async">
              <mat-checkbox
                [checked]="crop.checked"
                (change)="onChange($event, i, crop)"
              >
                {{ crop.district }}
              </mat-checkbox>
            </li>
          </ul>
        </span>
      </section>
    </div>
  </div>
  <div class="content container-outer" fxFlex="80">
    <mat-card
      class="crop-card"
      style="min-width: 17%"
      *ngFor="let crop of crops$ | async"
      [hidden]="!crop.checked"
    >
      <a [routerLink]="[crop.name]">
        <mat-card-header>
          <img
            mat-card-avatar
            class="example-header-image"
            src="/assets/icons/crops/{{ crop.name }}.PNG"
            alt="crop-image"
          />
          <mat-card-title>{{ crop.name }}</mat-card-title>
          <mat-card-subtitle>100 Kgs</mat-card-subtitle>
        </mat-card-header>
      </a>
      <mat-card-content>
        <p>PRICE</p>
      </mat-card-content>
      <mat-card-content>
        <p>{{ crop.district }}</p>
      </mat-card-content>
    </mat-card>
  </div>
</div>

<app-footer></app-footer>

5. all-trades.component.ts

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Crop } from 'src/app/shared/crop.model';
import { CropService } from '../crop.service';

@Component({
  selector: 'app-all-trades',
  templateUrl: './all-trades.component.html',
  styleUrls: ['./all-trades.component.css'],
})
export class AllTradesComponent implements OnInit {

  crops$: Observable<Crop[]>;

  constructor(private cropService: CropService) { }
  ngOnInit(): void {
    this.crops$ = this.cropService.getAllCrops();
  }
  onChange(event, index, item) {
    item.checked = !item.checked;
    console.log(index, event, item);
  }

}

【问题讨论】:

    标签: arrays angular typescript object filter


    【解决方案1】:

    费萨尔。

    也许这个 stackblitz 将帮助您解决问题。

    我认为,可以不使用表单来解决您的问题。我已将过滤器逻辑和数据拆分为可观察对象。新的filteredCrops$ observable 将结合来自crops$ 的最新数据,以及2 个带有过滤器的行为主题。如果该特定作物名称和地区的过滤器是真实的(即检查),则作物数据数组仍将使用返回 true 的函数进行过滤。

    1. app.component.ts

    interface Filter {
      name: string;
      checked: boolean;
    }
    
    @Component({
      selector: "my-app",
      templateUrl: "./app.component.html",
      styleUrls: ["./app.component.css"]
    })
    export class AppComponent {
      crops$: Observable<Crop[]>;
      filteredCrops$: Observable<Crop[]>;
      nameFilters$ = new BehaviorSubject<Filter[]>([]);
      districtFilters$ = new BehaviorSubject<Filter[]>([]);
      filteredDistrictCheckboxes$: Observable<Filter[]>;
    
      constructor(private cropService: CropService) {}
      ngOnInit(): void {
        this.crops$ = this.cropService.getAllCrops().pipe(
          tap(crops => {
            const names = Array.from(new Set(crops.map(crop => crop.name)));
            this.nameFilters$.next(
              names.map(name => ({ name, checked: true } as Filter))
            );
            const dictricts = Array.from(new Set(crops.map(crop => crop.district)));
            this.districtFilters$.next(
              dictricts.map(name => ({ name, checked: true } as Filter))
            );
          })
        );
        this.filteredCrops$ = combineLatest(
          this.crops$,
          this.nameFilters$,
          this.districtFilters$
        ).pipe(
          map(
            ([crops, nameFilters, districtFilters]: [
              Crop[],
              Filter[],
              Filter[]
            ]) => {
              let items = [...crops];
              items = items.filter(item => {
                const associatedNameFilter = nameFilters.find(
                  filter => filter.name === item.name
                );
                const associatedDistrictFilter = districtFilters.find(
                  filter => filter.name === item.district
                );
                return (
                  associatedNameFilter.checked && associatedDistrictFilter.checked
                );
              });
              return items;
            }
          )
        );
    
        this.filteredDistrictCheckboxes$ = this.nameFilters$.pipe(
          switchMap((nameFilters: Filter[]) => {
            return this.crops$.pipe(
              map(crops => {
                const enabledNames = nameFilters
                  .filter(item => item.checked)
                  .map(filter => filter.name);
                const enabledDistricts = Array.from(new Set(crops.filter(crop => enabledNames.includes(crop.name)).map(crop => crop.district)));
                const result = this.districtFilters$.value.filter(item =>
                  enabledDistricts.includes(item.name)
                );
                return result;
              })
            );
          })
        );
      }
    
      onNameFilterChange(item) {
        this.nameFilters$.value.find(
          filter => filter.name === item.name
        ).checked = !item.checked;
        this.nameFilters$.next([...this.nameFilters$.value]);
      }
    
      onDistrictFilterChange(item) {
        this.districtFilters$.value.find(
          filter => filter.name === item.name
        ).checked = !item.checked;
        this.districtFilters$.next([...this.districtFilters$.value]);
      }
    }
    

    2。来自 app.component.html 的模板

    <div fxLayout="row" fxLayout.lt-md="column" fxLayoutAlign="space-between start" fxLayoutAlign.lt-md="start stretch"
        *ngIf="crops$ | async">
        <div class="container-outer" fxFlex="20">
            <div class="filters">
                <section class="example-section">
                    <span class="example-list-section">
              <h1>Select Crop</h1>
            </span>
                    <span class="example-list-section">
              <ul>
                <li *ngFor="let filter of nameFilters$ | async">
                  <mat-checkbox
                    [checked]="filter.checked"
                    (change)="onNameFilterChange(filter)"
                  >
                    {{ filter.name }}
                  </mat-checkbox>
                </li>
              </ul>
            </span>
                </section>
    
                <section class="example-section">
                    <span class="example-list-section">
              <h1>Select District</h1>
            </span>
                    <span class="example-list-section">
              <ul>
                <li *ngFor="let filter of filteredDistrictCheckboxes$ | async">
                  <mat-checkbox
                    [checked]="filter.checked"
                    (change)="onDistrictFilterChange(filter)"
                  >
                    {{ filter.name }}
                  </mat-checkbox>
                </li>
              </ul>
            </span>
                </section>
            </div>
        </div>
        <div class="content container-outer" fxFlex="80">
            <mat-card class="crop-card" style="min-width: 17%" *ngFor="let crop of filteredCrops$ | async"
                [hidden]="!crop.checked">
                <a [routerLink]="[crop.name]">
                    <mat-card-header>
                        <img
                mat-card-avatar
                class="example-header-image"
                src="/assets/icons/crops/{{ crop.name }}.PNG"
                alt="crop-image"
              />
                        <mat-card-title>{{ crop.name }}</mat-card-title>
                        <mat-card-subtitle>100 Kgs</mat-card-subtitle>
                    </mat-card-header>
                </a>
                <mat-card-content>
                    <p>PRICE</p>
                </mat-card-content>
                <mat-card-content>
                    <p>{{ crop.district }}</p>
                </mat-card-content>
            </mat-card>
        </div>
    </div>
    

    【讨论】:

    • 太棒了! Alexey,但在选择区它仍然显示两个 Nashik 复选框。在 stackbliz 中,它显示了 Nashik 区的一个复选框,但是当我在我的代码上实现时,它显示了两个 nashik 复选框
    • 顺便说一句thanx...我在github上关注你并在linkedin上请求你希望你不会介意?
    • Faisal,也许我没有完全理解你的问题。您是否希望在选择作物名称后,区域部分的复选框应包含唯一选择作物的区域?这样,您需要一个更可观察的区域复选框列表。我会尝试更新我的 stackblitz。
    • Faisal,请参阅更新的 stackblitz 或上面答案中的代码。我在nOnInit 方法中添加了filteredDistrictCheckboxes$ observable 及其代码。模板也更新为地区复选框列表的新变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 2019-02-17
    • 1970-01-01
    相关资源
    最近更新 更多