Angular 服務

本教學課程示範如何建立 Angular 服務,並使用依賴注入將它納入你的應用程式。

你將學到什麼

你的應用程式將會有一個服務,它會讀取本機的靜態資料並提供給你的應用程式。 在之後的課程中,你會更新這個服務,讓它從網路服務取得資料。

服務概念預覽

本教學介紹 Angular 服務與依賴注入。

Angular 服務

_Angular 服務_提供了一種方式,讓你將應用程式的資料與功能分離出來,供應用程式中的多個元件使用。 一個服務要能被多個元件使用,必須先做成可注入的(injectable)。 可注入且被元件使用的服務,會成為該元件的相依性。 元件依賴這些服務,沒有它們就無法運作。

依賴注入

_依賴注入_是管理應用程式元件相依性,以及其他元件可使用之服務的機制。

  1. 為你的應用程式建立新服務

    這個步驟會為你的應用程式建立一個可注入的服務。

    在 IDE 的 Terminal 窗格中:

    1. 在你的專案目錄中,進入 first-app 目錄。

    2. 在 first-app 目錄中,執行下列指令,建立新服務。

      ng generate service housingService --skip-tests
    3. 執行 ng serve,建置應用程式並將它提供到 http://localhost:4200。

    4. 確認應用程式建置時沒有錯誤。 繼續下一步之前,請先修正任何錯誤。

  2. 將靜態資料加入新服務

    這個步驟會為你的新服務加入一些範例資料。 在之後的課程中,你會將靜態資料換成網路介面,像在真正的應用程式中一樣取得資料。 目前,你的應用程式的新服務使用的是到目前為止在本機 Home 中建立的資料。

    在 IDE 的 Edit 窗格中:

    1. 在 src/app/home/home.ts 中,從 Home 複製 baseUrl 變數與 housingLocationList 變數及其陣列值。

    2. 在 src/app/housing-service.ts 中:

      1. 在 HousingService 類別內,貼上你在上一個步驟中從 Home 複製的變數。

      2. 在 HousingService 類別內,將這些函式貼在你剛複製的資料之後。 這些函式讓相依者能存取服務的資料。

        src/app/housing-service.ts 中的服務函式

        import {Service} from '@angular/core';
        import {HousingLocationInfo} from './housinglocation';
        
        @Service()
        export class HousingService {
          readonly baseUrl = 'https://angular.dev/assets/images/tutorials/common';
        
          protected housingLocationList: HousingLocationInfo[] = [
            {
              id: 0,
              name: 'Acme Fresh Start Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/bernard-hermant-CLKGGwIBTaY-unsplash.jpg`,
              availableUnits: 4,
              wifi: true,
              laundry: true,
            },
            {
              id: 1,
              name: 'A113 Transitional Housing',
              city: 'Santa Monica',
              state: 'CA',
              photo: `${this.baseUrl}/brandon-griggs-wR11KBaB86U-unsplash.jpg`,
              availableUnits: 0,
              wifi: false,
              laundry: true,
            },
            {
              id: 2,
              name: 'Warm Beds Housing Support',
              city: 'Juneau',
              state: 'AK',
              photo: `${this.baseUrl}/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg`,
              availableUnits: 1,
              wifi: false,
              laundry: false,
            },
            {
              id: 3,
              name: 'Homesteady Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/ian-macdonald-W8z6aiwfi1E-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 4,
              name: 'Happy Homes Group',
              city: 'Gary',
              state: 'IN',
              photo: `${this.baseUrl}/krzysztof-hepner-978RAXoXnH4-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 5,
              name: 'Hopeful Apartment Group',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-JvQ0Q5IkeMM-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 6,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/phil-hearing-IYfp2Ixe9nM-unsplash.jpg`,
              availableUnits: 5,
              wifi: true,
              laundry: true,
            },
            {
              id: 7,
              name: 'Hopeful Housing Solutions',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-GGupkreKwxA-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 8,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/saru-robert-9rP3mxf8qWI-unsplash.jpg`,
              availableUnits: 10,
              wifi: false,
              laundry: false,
            },
            {
              id: 9,
              name: 'Capital Safe Towns',
              city: 'Portland',
              state: 'OR',
              photo: `${this.baseUrl}/webaliser-_TPTXZd9mOo-unsplash.jpg`,
              availableUnits: 6,
              wifi: true,
              laundry: true,
            },
          ];
        
          getAllHousingLocations(): HousingLocationInfo[] {
            return this.housingLocationList;
          }
        
          getHousingLocationById(id: number): HousingLocationInfo | undefined {
            return this.housingLocationList.find((housingLocation) => housingLocation.id === id);
          }
        }

        之後的課程會用到這些函式。目前只要理解這些函式會依 id 回傳特定的 HousingLocation,或回傳整份清單即可。

      3. 為 HousingLocation 加入檔案層級的匯入。

        在 src/app/housing-service.ts 中匯入 HousingLocation 型別

        import {Service} from '@angular/core';
        import {HousingLocationInfo} from './housinglocation';
        
        @Service()
        export class HousingService {
          readonly baseUrl = 'https://angular.dev/assets/images/tutorials/common';
        
          protected housingLocationList: HousingLocationInfo[] = [
            {
              id: 0,
              name: 'Acme Fresh Start Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/bernard-hermant-CLKGGwIBTaY-unsplash.jpg`,
              availableUnits: 4,
              wifi: true,
              laundry: true,
            },
            {
              id: 1,
              name: 'A113 Transitional Housing',
              city: 'Santa Monica',
              state: 'CA',
              photo: `${this.baseUrl}/brandon-griggs-wR11KBaB86U-unsplash.jpg`,
              availableUnits: 0,
              wifi: false,
              laundry: true,
            },
            {
              id: 2,
              name: 'Warm Beds Housing Support',
              city: 'Juneau',
              state: 'AK',
              photo: `${this.baseUrl}/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg`,
              availableUnits: 1,
              wifi: false,
              laundry: false,
            },
            {
              id: 3,
              name: 'Homesteady Housing',
              city: 'Chicago',
              state: 'IL',
              photo: `${this.baseUrl}/ian-macdonald-W8z6aiwfi1E-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 4,
              name: 'Happy Homes Group',
              city: 'Gary',
              state: 'IN',
              photo: `${this.baseUrl}/krzysztof-hepner-978RAXoXnH4-unsplash.jpg`,
              availableUnits: 1,
              wifi: true,
              laundry: false,
            },
            {
              id: 5,
              name: 'Hopeful Apartment Group',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-JvQ0Q5IkeMM-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 6,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/phil-hearing-IYfp2Ixe9nM-unsplash.jpg`,
              availableUnits: 5,
              wifi: true,
              laundry: true,
            },
            {
              id: 7,
              name: 'Hopeful Housing Solutions',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/r-architecture-GGupkreKwxA-unsplash.jpg`,
              availableUnits: 2,
              wifi: true,
              laundry: true,
            },
            {
              id: 8,
              name: 'Seriously Safe Towns',
              city: 'Oakland',
              state: 'CA',
              photo: `${this.baseUrl}/saru-robert-9rP3mxf8qWI-unsplash.jpg`,
              availableUnits: 10,
              wifi: false,
              laundry: false,
            },
            {
              id: 9,
              name: 'Capital Safe Towns',
              city: 'Portland',
              state: 'OR',
              photo: `${this.baseUrl}/webaliser-_TPTXZd9mOo-unsplash.jpg`,
              availableUnits: 6,
              wifi: true,
              laundry: true,
            },
          ];
        
          getAllHousingLocations(): HousingLocationInfo[] {
            return this.housingLocationList;
          }
        
          getHousingLocationById(id: number): HousingLocationInfo | undefined {
            return this.housingLocationList.find((housingLocation) => housingLocation.id === id);
          }
        }
    3. 確認應用程式建置時沒有錯誤。 繼續下一步之前,請先修正任何錯誤。

  3. 將新服務注入 Home

    這個步驟會將新服務注入應用程式的 Home,讓它能從服務讀取應用程式的資料。 在之後的課程中,你會將靜態資料換成即時資料來源,像在真正的應用程式中一樣取得資料。

    在 IDE 的 Edit 窗格中,在 src/app/home/home.ts 裡:

    1. 在 src/app/home/home.ts 頂部,將 inject 加入從 @angular/core 匯入的項目中。這會將 inject 函式匯入 Home 類別。

      更新 src/app/home/home.ts

      import {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" />
              <button class="primary" type="button">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of housingLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
        }
      }
    2. 為 HousingService 加入新的檔案層級匯入:

      在 src/app/home/home.ts 中加入匯入

      import {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" />
              <button class="primary" type="button">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of housingLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
        }
      }
    3. 從 Home 中刪除 baseUrl 變數,刪除 housingLocationList 陣列的項目,並將 housingLocationList 指派為空陣列([])。再過幾個步驟,你會更新程式碼,讓它從 HousingService 拉取資料。

    4. 在 Home 中,加入下列程式碼,注入新服務並為應用程式初始化資料。constructor 是這個元件建立時第一個執行的函式。constructor 中的程式碼會將 housingLocationList 指派為呼叫 getAllHousingLocations 所回傳的值。

      在 src/app/home/home.ts 中從服務初始化資料

      import {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" />
              <button class="primary" type="button">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of housingLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrl: './home.css',
      })
      export class Home {
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
      
        constructor() {
          this.housingLocationList = this.housingService.getAllHousingLocations();
        }
      }
    5. 儲存對 src/app/home/home.ts 的變更,確認你的應用程式建置時沒有錯誤。 繼續下一步之前,請先修正任何錯誤。

SUMMARY: 在這一課中,你為應用程式加入了一個 Angular 服務,並將它注入 Home 類別。 這讓應用程式取得資料的方式有了清楚的區隔。 目前,新服務是從靜態資料陣列取得資料。 在之後的課程中,你會重構這個服務,讓它從 API 端點取得資料。

如需本課所涵蓋主題的更多資訊,請造訪: