Angular从零到一2.2 建立一个服务完成业务逻辑

简介:

2.2 建立一个服务完成业务逻辑


如果我们把登录的业务逻辑在onClick方法中完成,这样当然也可以,但是这样做的耦合性太强了。设想一下,如果我们增加了微信登录、微博登录等,业务逻辑会越来越复杂,显然我们需要把这个业务逻辑分离出去。

那么我们接下来创建一个AuthService吧,首先我们要在在src中新建一个叫做core的文件夹(src\app\core),然后命令行中输入 ng g s core\auth (s这里是service的缩写,core)。auth.service.ts和auth.service.spec.ts这两个文件应该已经出现在你的目录里了。

下面我们为这个service添加一个方法,你可能注意到这里我们为这个方法指定了返回类型和参数类型。这就是TypeScript带来的好处,有了类型约束,你在别处调用这个方法时,如果给出的参数类型或返回类型不正确,IDE就可以直接告诉你错了。

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

 

@Injectable()

export class AuthService {

 

  constructor() { }

 

  loginWithCredentials(username: string, password: string): boolean {

    if(username === 'wangpeng')

      return true;

    return false;

  }

 

}

等一下,这个service虽然被创建了,但仍然无法在Component中使用。当然你可以在Component中import这个服务,然后实例化后使用,但是这样做并不好,仍然是一个紧耦合的模式,Angular 2提供了一种依赖性注入(Dependency Injection)的方法。

什么是依赖性注入

如果不使用DI(依赖性注入)的时候,我们自然的想法是这样的,在login.component.ts中import引入AuthService,在构造中初始化service,在onClick中调用service:

import { Component, OnInit } from '@angular/core';

//引入AuthService

import { AuthService } from '../core/auth.service';

 

@Component({

  selector: 'app-login',

  template: '

    <div>

      <input #usernameRef type="text">

      <input #passwordRef type="password">

      <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button>

    </div>

  ',

  styles: []

})

export class LoginComponent implements OnInit {

 

  //声明成员变量,其类型为AuthService

  service: AuthService;

 

  constructor() {

    this.service = new AuthService();

  }

 

  ngOnInit() {

  }

 

  onClick(username, password) {

    //调用service的方法

    console.log('auth result is: ' + this.service.loginWithCredentials(username,

password));

  }

 

}

这么做呢也可以跑起来,但存在以下几个问题:

由于实例化是在组件中进行的,意味着我们如果更改service的构造函数的话,组件也需要更改。

如果我们以后需要开发、测试和生产环境配置不同的AuthService,以这种方式实现会非常不方便。

下面我们看看如果使用DI是什么样子的,首先我们需要在组件的修饰器中配置AuthService,然后在组件的构造函数中使用参数进行依赖注入:

import { Component, OnInit } from '@angular/core';

import { AuthService } from '../core/auth.service';

 

@Component({

  selector: 'app-login',

  template: '

    <div>

      <input #usernameRef type="text">

      <input #passwordRef type="password">

      <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button>

    </div>

  ',

  styles: [],

  //在providers中配置AuthService

  providers:[AuthService]

})

export class LoginComponent implements OnInit {

  //在构造函数中将AuthService示例注入到成员变量service中

  //而且我们不需要显式声明成员变量service了

  constructor(private service: AuthService) {

  }

 

  ngOnInit() {

  }

 

  onClick(username, password) {

    console.log('auth result is: ' + this.service.loginWithCredentials(username,

password));

  }

 

}

看到这里你会发现我们仍然需要import相关的服务,import是要将类型引入进来,而provider里面会配置这个类型的实例。当然即使这样还是不太爽,可不可以不引入AuthService呢?答案是可以的。

我们看一下app.module.ts,这个根模块文件中我们会发现也有个providers,根模块中的这个providers是配置在模块中全局可用的service或参数的:

providers: [

    {provide: 'auth',  useClass: AuthService}

]

providers是一个数组,这个数组呢其实是把你想要注入到其他组件中的服务配置在这里。大家注意到我们这里的写法和上面有点区别,没有直接写成:

providers:[AuthService]

而是给出了一个对象,里面有两个属性,provide和useClass,provide定义了这个服务的名称,有需要注入这个服务的就引用这个名称就好。useClass指明这个名称对应的服务是一个类,本例中就是AuthService了。这样定义好之后,我们就可以在任意组件中注入这个依赖了。

下面我们改动一下login.component.ts,去掉头部的import { AuthService } from '../core/auth.service';和组件修饰器中的providers,更改其构造函数为:

constructor(@Inject('auth') private service) {

  }

我们去掉了service的类型声明,但加了一个修饰符@Inject('auth'),这个修饰符的意思是请到系统配置中找到名称为auth的那个依赖注入到我修饰的变量中。当然这样改完后你会发现Inject这个修饰符系统不识别,我们需要在@angular/core中引用这个修饰符,现在login.component.ts看起来应该是下面这个样子:

import { Component, OnInit, Inject } from '@angular/core';

 

@Component({

  selector: 'app-login',

  template: '

    <div>

      <input #usernameRef type="text">

      <input #passwordRef type="password">

      <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button>

    </div>

  ',

  styles: []

})

export class LoginComponent implements OnInit {

 

  constructor(@Inject('auth') private service) {

  }

 

  ngOnInit() {

  }

 

  onClick(username, password) {

    console.log('auth result is: ' + this.service.loginWithCredentials(username,

password));

  }

 

}

注意依赖性注入不是仅仅为Service服务的,任何的类都可以通过这种方式提供和注入,它提供了一种解耦的方式,通过Providers提供,通过constructor注入:

constructor(userService: UserService) {

  userService.addUser({username: 'wang', password:'1234'});

}

注入器从哪得到的依赖?它可能在自己内部容器里已经有该依赖了。如果它没有,也能在提供商的帮助下新建一个。提供商就是一个用于交付服务的配方,它被关联到一个令牌。Angular会使用一些自带的提供商来初始化这些注入器。我们必须自行注册属于自己的提供商,通常用组件或者指令元数据中的providers数组进行注册。简单的类提供商是最典型的例子。只要在providers数值里面提到该类就可以了。

providers: [ AuthService, UserService ]

除了上面那种最简单的提供方式之外,我们还能以令牌方式提供。我们通常在构造函数里面,为参数指定类型,让Angular来处理依赖注入。该参数类型就是依赖注入器所需的令牌。Angular把该令牌传给注入器,然后把得到的结果赋给参数。下面是一个典型的例子:

providers: [

  { provide: 'auth', useClass: AuthService },

  { provide: 'user', useClass: UserService },

  { provide: BASE_URL,  useValue:   'http://localhost:3000/todos' },

  AuthGuardService

]

我们发现providers数组是由一系列的provide对象构成的,这个对象是{provide: ..., useClass: ...}或者{provide: ..., useValue: ...}形式的。我们把第一个属性叫令牌,第二个属性叫定义对象。这两种形式分别对应类供应商和值供应商。

值供应商通常用来进行运行期常量设置,比如网站的基础地址和功能标志等。那么最简单的那种情形是怎么回事呢?比如:providers: [ AuthGuardService ],其实这是一个语法糖,等价于{provide: AuthGuardService, useClass: AuthGuardService} 。

{ provide: BASE_URL,  useValue:   ‘http://localhost:3000/todos’ } 这个例子和其他的好像还是不太一样,BASE_URL不是个字符串对象也不是一个类对象。这是我们创建的一个令牌,这样创建的令牌拥有一个友好的名字,但不会与其他的同名令牌发生冲突:

import { OpaqueToken } from '@angular/core';

 

export const BASE_URL = new OpaqueToken('BASE_URL');

当然还有另外两种情形,一种叫别名提供商,我们为同一个对象起了不同的别名:

{ provide: MinimalLogger, useExisting: LoggerService },

另一种叫工厂提供商,提供商通过调用工厂函数来新建一个依赖对象,如下所示:

{ provide: HELLO, useFactory:  helloFactory(2), deps: [Greeting, HelloService] }

使用这项技术,可以用包含了一些依赖服务和本地状态输入的工厂函数来建立一个依赖对象。helloFactory自身不是提供商工厂函数。真正的提供商工厂函数是helloFactory返回的函数:

export function helloFactory(take: number) {

  return (greeting: Greeting, helloService: HelloService): string => {

    /* ... */

  };

};

相关文章
|
4月前
|
开发者 iOS开发 C#
Uno Platform 入门超详细指南:从零开始教你打造兼容 Web、Windows、iOS 和 Android 的跨平台应用,轻松掌握 XAML 与 C# 开发技巧,快速上手示例代码助你迈出第一步
【8月更文挑战第31天】Uno Platform 是一个基于 Microsoft .NET 的开源框架,支持使用 C# 和 XAML 构建跨平台应用,适用于 Web(WebAssembly)、Windows、Linux、macOS、iOS 和 Android。它允许开发者共享几乎全部的业务逻辑和 UI 代码,同时保持原生性能。选择 Uno Platform 可以统一开发体验,减少代码重复,降低开发成本。安装时需先配置好 Visual Studio 或 Visual Studio for Mac,并通过 NuGet 或官网下载工具包。
433 0
|
4月前
|
前端开发 UED 开发者
无障碍设计的魔法:JSF让每个用户都能畅游数字世界!
【8月更文挑战第31天】本文介绍如何使用JavaServer Faces (JSF)构建无障碍Web应用,确保所有用户都能访问和使用。文章通过实际代码示例展示了如何利用ARIA属性增强组件、实现键盘导航、提供文本替代以及使用语义化标签等技术。无障碍设计不仅是道德责任,也是提升用户体验的关键。通过这些方法,JSF可以帮助开发者创建更加公平和包容的应用。
38 0
|
4月前
|
前端开发 数据处理 数据库
Angular与Firebase的完美联合:掌握实时数据同步技术——从环境配置到数据服务的详细实现指南
【8月更文挑战第31天】在现代Web应用中,实时数据同步对于提升用户体验至关重要。本文档详细介绍如何在Angular应用中集成Firebase实时数据库,包括准备工作、配置环境、实现实时数据同步及在组件中使用数据服务等步骤。通过本教程,开发者将掌握利用Angular与Firebase高效实现数据同步的方法,增强应用的实时互动性。
45 0
|
4月前
|
数据处理 开发者
深入解析Angular服务:构建可重用业务逻辑的最佳实践与模式——从基础创建到高级异步处理的全面指南
【8月更文挑战第31天】在Angular开发中,服务用于封装可重用的业务逻辑,有助于保持代码的DRY原则。本文详细介绍如何创建和使用服务,包括基础设置、逻辑封装及高级应用,如HTTP请求和异步数据处理,帮助你构建模块化、易维护的应用。通过示例展示,你将学会如何充分利用服务提升开发效率。
52 0
|
5月前
|
设计模式 JavaScript 测试技术
Angular服务与依赖注入机制详解
【7月更文挑战第17天】Angular的服务与依赖注入机制为构建模块化、可维护和可扩展的应用程序提供了强大的支持。通过合理定义和使用服务,以及利用依赖注入来管理依赖关系,我们可以编写出更加清晰、可维护和可测试的代码。希望本文能帮助你更好地理解和应用Angular的服务与依赖注入机制。
|
5月前
|
前端开发 JavaScript
前端框架与库 - Angular基础:组件、模板、服务
【7月更文挑战第16天】Angular,谷歌维护的前端框架,专注构建动态Web应用。组件是核心,包含行为逻辑的类、定义视图的模板和样式。模板语法含插值、属性和事件绑定。服务提供业务逻辑,依赖注入实现共享。常见问题涉及组件通信、性能和服务注入。优化通信、性能并正确管理服务范围,能提升应用效率和质量。学习组件、模板和服务基础,打造高效Angular应用。
72 1
|
7月前
解决全网90%以上的日期格式转换、日期序列等骚操作问题
解决全网90%以上的日期格式转换、日期序列等骚操作问题
解决全网90%以上的日期格式转换、日期序列等骚操作问题
|
7月前
【请求后台接口】30秒完成Angular10精简版HttpClient请求服务搭建
【请求后台接口】30秒完成Angular10精简版HttpClient请求服务搭建
【请求后台接口】30秒完成Angular10精简版HttpClient请求服务搭建
|
API
Angular 2.x折腾记 :(3)初步了解服务及使用
不探究高深理论,只探究实际使用,有更好的写法或者经验请指出; 有些暂时没涉及到的知识我可能会顺着例子解释;
152 0
|
JavaScript 前端开发 API
Angular 自定义服务 notification
比如,我们这篇文章要讲到的 notification 的实现。
Angular 自定义服务 notification