為你的應用程式加上搜尋功能

這堂教學課程示範如何為你的 Angular 應用程式加上搜尋功能。

應用程式會讓使用者搜尋應用程式提供的資料,並且只顯示與輸入字詞相符的結果。

IMPORTANT: 本教學的這個步驟,建議你在本機環境操作。

你會學到什麼

  • 你的應用程式會使用表單的資料,搜尋相符的住房地點
  • 你的應用程式只會顯示相符的住房地點
  1. 更新首頁元件的屬性

    在這個步驟中,你會更新 Home 類別,把資料存放在一個新的陣列屬性中,用來進行篩選。

    1. 在 src/app/home/home.ts 中,為類別加上一個名為 filteredLocationList 的新屬性。

      在 home.ts 中加入 filteredLocationList 屬性

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }

      filteredLocationList 存放與使用者輸入的搜尋條件相符的值。

    2. 頁面載入時,filteredLocationList 預設應該包含全部的住房地點值。請更新 Home 的 constructor 來設定這個值。

      設定 filteredLocationList 的值

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }
  2. 更新首頁元件的範本

    Home 已經有一個輸入欄位,你會用它來取得使用者的輸入。那串文字會用來篩選結果。

    1. 更新 Home 範本,在 input 元素中加入一個名為 #filter 的範本變數。

      在 home.ts 的 input HTML 元素中加入範本變數

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }

      本範例使用範本參考變數來取得 input 元素,把它當作值來存取。

    2. 接著,更新元件範本,為 "Search" 按鈕加上事件處理器。

      在 home.ts 中把按鈕的 click 事件繫結到方法

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }

      透過繫結 button 元素上的 click 事件,你就可以呼叫 filterResults 函式。傳給這個函式的引數,是 filter 範本變數的 value 屬性,具體來說就是 input HTML 元素的 .value 屬性。

    3. 範本的最後一項更新是 @for 指令。請更新 @for,讓它逐一處理 filteredLocationList 陣列中的值。

      更新 home.ts 中的 @for 範本指令

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }
  3. 實作事件處理器函式

    範本已經更新,把 filterResults 函式繫結到 click 事件。接下來,你的任務是在 Home 類別中實作 filterResults 函式。

    1. 更新 Home 類別,加入 filterResults 函式的實作。

      加入 filterResults 函式的實作

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing-service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
          this.filteredLocationList = this.housingLocationList;
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }

      這個函式使用 Array 的 filter 函式,把 text 參數的值與 housingLocation.city 屬性互相比較。你可以更新這個函式,讓它比對任何屬性、甚至多個屬性,作為一個有趣的練習。

    2. 儲存你的程式碼。

    3. 重新整理瀏覽器,輸入文字並點選 "Search" 按鈕後,確認你可以依城市搜尋住房地點資料。

      依使用者輸入篩選後的搜尋結果

SUMMARY: 在本課中,你使用範本變數與範本值互動,並運用事件繫結與陣列函式新增搜尋功能。

想進一步了解本課介紹的主題,請造訪: