AngularJS Provider升级至Angular 4:工厂转类实现方法咨询
嘿,我来帮你把这个AngularJS的provider转换成Angular 4的类实现,一步步拆解给你看哈~
转换步骤与最终实现
首先,先给你看转换后的完整Angular 4服务类,之后再逐个解释对应原代码的部分:
import { Injectable } from '@angular/core'; // 假设原代码中的marketServiceBase是全局变量,后续会给你优化建议 declare const marketServiceBase: string; @Injectable({ providedIn: 'root' // 全局单例模式,也可以在对应模块的providers数组中注册 }) export class WebApiService { // 对应原provider中的私有状态nameUrlMap private nameUrlMap: { [key: string]: string } = {}; constructor() {} // 对应原provider中的registerApiUrl方法(补全了你没写完的逻辑) registerApiUrl(name: string, url: string): void { this.nameUrlMap[name] = url; } // 完全对应原getUrl方法,适配类的写法 getUrl(name: string): string { const ret = this.nameUrlMap[name]; if (!ret) { throw new Error(`Unable to find API URL for API ${name}`); } return marketServiceBase + ret; } }
1. 核心结构替换
原AngularJS里的angular.module(...).provider(...),在Angular 4+中用**@Injectable装饰器+类**来实现。装饰器里的providedIn: 'root'表示这个服务是全局单例,不需要手动在模块里注册(当然你也可以选择在特定模块的providers数组中注册,控制作用域)。
2. 私有状态迁移
原provider里的var nameUrlMap = {}是内部维护的映射表,在类中我们用private nameUrlMap: { [key: string]: string } = {}来定义:
private确保这个属性只能在类内部访问,和原代码的私有变量逻辑一致- TypeScript的类型注解
{ [key: string]: string }让代码更清晰,明确键值都是字符串类型
3. 方法转换
原provider里的方法直接转换成类的成员方法:
registerApiUrl:补全了你没写完的逻辑,就是把传入的name和url存入映射表,用this.nameUrlMap访问类的私有属性getUrl:逻辑和原代码完全一致,只是把原来的独立函数改成类的方法,错误提示用模板字符串(${name})比原写法更简洁,当然你也可以保持原有的字符串拼接方式
4. 服务的使用方式
在Angular 4中,你不需要像AngularJS那样手动获取服务,直接通过构造函数注入就能使用:
比如在某个组件里使用这个服务:
import { Component } from '@angular/core'; import { WebApiService } from './web-api.service'; @Component({ selector: 'app-example', template: '<p>用户API地址:{{ userApiUrl }}</p>' }) export class ExampleComponent { userApiUrl: string; constructor(private webApiService: WebApiService) { // 先注册API地址映射 this.webApiService.registerApiUrl('user', '/api/users'); // 获取拼接后的完整URL this.userApiUrl = this.webApiService.getUrl('user'); } }
优化建议:替换全局变量marketServiceBase
原代码里的marketServiceBase如果是全局变量,在Angular里更推荐用依赖注入的方式提供配置,这样代码更模块化,也方便测试。比如:
- 先定义一个配置的Token:
import { InjectionToken } from '@angular/core'; export const MARKET_SERVICE_BASE = new InjectionToken<string>('marketServiceBase');
- 在你的根模块(比如AppModule)中提供这个配置:
import { NgModule } from '@angular/core'; import { MARKET_SERVICE_BASE } from './tokens'; @NgModule({ providers: [ { provide: MARKET_SERVICE_BASE, useValue: 'https://your-production-base-url.com' } ] }) export class AppModule {}
- 在WebApiService中注入这个配置:
import { Inject, Injectable } from '@angular/core'; import { MARKET_SERVICE_BASE } from './tokens'; @Injectable({ providedIn: 'root' }) export class WebApiService { private nameUrlMap: { [key: string]: string } = {}; // 通过构造函数注入配置 constructor(@Inject(MARKET_SERVICE_BASE) private marketServiceBase: string) {} getUrl(name: string): string { const ret = this.nameUrlMap[name]; if (!ret) { throw new Error(`Unable to find API URL for API ${name}`); } // 现在直接用类的私有属性访问基础URL return this.marketServiceBase + ret; } registerApiUrl(name: string, url: string): void { this.nameUrlMap[name] = url; } }
这样就摆脱了对全局变量的依赖,符合Angular的最佳实践。
内容的提问来源于stack exchange,提问作者mbrc
相关产品推荐
相关产品推荐

