一、服务的创建和使用

1.服务的创建

服务可以简单理解为共享(公用)的模块,可供多个组件使用,就好像是vue中的vuex。例如在Angular项目中,我们使用以下命令创建一个animal服务

ng generate service services/animal
# 简写命令
ng g s services/animal

生成的animal.service.ts文件,如下

// anmial.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AnimalService {

  constructor() { }
}

2.服务的使用

更新animal.service.ts文件,添加data数据

// anmial.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AnimalService {
  data: Array<{name: string, id: number}> = [
    { name: '孔雀', id: 1001 },
    { name: '老虎', id: 1002 },
    { name: '鸭子', id: 1003 },
    { name: '梅花鹿', id: 1004 },
    { name: '河马', id: 1005 },
    { name: '狮子', id: 1006 },
    { name: '棕熊', id: 1007 },
  ];
  constructor() { }
}

创建一个组件,并在该组件中使用animal服务

  1. 导入服务
  2. 在构造函数中声明服务的类型
  3. 使用服务
import { Component, OnInit } from '@angular/core';
import { AnimalService } from '../../services/animal.service';  // 第一步,导入该服务,主要用于类型定义

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.less']
})
export class HomeComponent implements OnInit {
	
  // 第二步,在构造函数声明该服务
  constructor(private animalService: AnimalService) { }

  ngOnInit(): void {
  	// 第三步,使用服务
    console.log('获取动物数据:', this.animalService.data);
  }

}

二、服务使用的注意事项

1. providedIn

我们注意到修饰器Injectable中的配置项providedIn: 'root'

providedIn: 'root' 相当于在根模块导入这个服务,如过去掉providedIn: 'root',则需要在app.module.ts文件中的providers导入该服务(使用时也可在对应的模块/组件中的providers导入该服务),否则使用该服务时会报错。

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { AnimalService } from './services/animal.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [AnimalService],		// 导入服务
  bootstrap: [AppComponent]
})
export class AppModule { }

如果我们希望只在特定模块中使用该服务,比如,在Animal模块使用Animal服务

// animal.module.ts
import { NgModule } from '@angular/core';
import { AnimalComponent } from './animal.component';

import { AnimalService } from './services/animal.service';

@NgModule({
  declarations: [
    AnimalComponent 
  ],
  imports: [],
  providers: [AnimalService],		// 导入服务
  bootstrap: []
})
export class AnimalModule { }

2. Injectable修饰器

若我们去掉整个Injectable修饰器,然后在app.module.ts或相关模块/组件中的providers配置该服务,使用时也是正常的。

// anmial.service.ts
export class AnimalService {
  data: Array<{name: string, id: number}> = [
    { name: '孔雀', id: 1001 },
    { name: '老虎', id: 1002 },
    { name: '鸭子', id: 1003 },
    { name: '梅花鹿', id: 1004 },
    { name: '河马', id: 1005 },
    { name: '狮子', id: 1006 },
    { name: '棕熊', id: 1007 },
  ];
  constructor() { } 
}

但是当我们在服务的构造器添加参数并声明参数类型时,会报错

// anmial.service.ts
import { HttpClient } from '@angular/common/http';
export class AnimalService {
  data: Array<{name: string, id: number}> = [
    { name: '孔雀', id: 1001 },
    { name: '老虎', id: 1002 },
    { name: '鸭子', id: 1003 },
    { name: '梅花鹿', id: 1004 },
    { name: '河马', id: 1005 },
    { name: '狮子', id: 1006 },
    { name: '棕熊', id: 1007 },
  ];
  constructor(private http: HttpClient) { }		// 由于没有Injectable修饰器会报错
}

此时我们添加Injectable修饰器就恢复正常了,所以我们可以简单理解Injectable修饰器的其中一个作用就是用于服务构造函数中声明参数类型的。在日常开发中,建议服务都统一保留Injectable修饰器,以免出现意想不到的错误。

// anmial.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable()
export class AnimalService {
  data: Array<{name: string, id: number}> = [
    { name: '孔雀', id: 1001 },
    { name: '老虎', id: 1002 },
    { name: '鸭子', id: 1003 },
    { name: '梅花鹿', id: 1004 },
    { name: '河马', id: 1005 },
    { name: '狮子', id: 1006 },
    { name: '棕熊', id: 1007 },
  ];
  constructor(private http: HttpClient) { }		// 有Injectable修饰器,不会报错
}

3. 在构造函数进行服务类型声明

为什么在app.module.ts或相关模块/组件中的providers配置完服务,在使用时还需要在构造函数进行类型声明?
这是因为我们配置时只是告诉angular我们注入了该服务,在使用时需要构造注入该服务依赖的对象类型。