angular2系列教程(七)Injectable、Promise、Interface、使用服务

本文介绍 Angular 2 中 service 的概念及其在发送 HTTP 请求和其他封装方法中的应用。通过实例展示了如何使用 Injectable 装饰器创建服务,并解释了 Promise 的作用及用法,包括解决回调地狱问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天我们要讲的ng2的service这个概念,和ng1一样,service通常用于发送http请求,但其实你可以在里面封装任何你想封装的方法,有时候控制器之间的通讯也是依靠service来完成的,让我们一睹为快!

例子

例子是官方的例子,加载一个英雄列表,点击显示详细信息。我直接放在我们的升级后的装备里面。

源代码

Injectable

在ng2里面如何编写服务呢?非常简单,你只需要写个类即可。那么这个@Injectable()是做什么的?其实单就这个例子来说,我们是不需要写个这个装饰的,因为我们的HeroSerivce没有依赖,如果你要写一个有依赖的服务,那么你需要加上这个@Injectable(),此处加上@Injectable()是可有可无的,但是写上是个好习惯。

app/hero.service.ts(部分代码)

复制代码
@Injectable()
export class HeroService {
  getHeroes() {
    return Promise.resolve(HEROES);
  }
  // See the "Take it slow" appendix
  getHeroesSlowly() {
    return new Promise<Hero[]>(resolve =>
      setTimeout(() => resolve(HEROES), 2000) // 2 seconds
    );
  }
}
复制代码

以上代码我们干了哪些事儿呢?

  1. 写了一个使用injectable装饰的类
  2. 写了两个成员函数
  3. 一个返回一个Promise,直接resolve数据
  4. 另一个也返回一个Promise,不过在两秒后resolve数据

有的同学会问:resolve的数据哪去了?Promise是什么?我们继续讲解。

Promise

如果你玩过ng1,你一定对promise不陌生,因为我们经常在路由里面写resolve,这里就可以接受一个Promise对象。还有ng1中的$q.defer()等等。

但是promise并不是ng1的专利,你可以在你的任何javascript程序中使用promise,因为在ES6中已经原生提供Promise对象。你可以查看它的用法,这里我简单描述下:

  1. 构造Promise,只需要在里面加入一个参数,这个参数是个function,这个function可以接受两个参数:resolve, reject。或者使用Promise.resolve(),不过这样没有延迟了。
  2. 使用Promise对象,最常用的方法是then(),里面接受一个function,这个function的参数为resolve的值。除了then()还有catch()等

为了让大家能够清晰的了解Promise的用法,我们打开chrome的console:

  1. 输入Promise,是个function。
  2. 输入Promise.resolve('123'),我们得到一个状态为“已经解决”的promise。
  3. 输入new Promise(resolve=>resolve('123')),我们还是得到一个状态为“已经解决”的promise。

2和3的区别在于,后者可以在参数的函数中做一些处理,比如延迟或者http请求。

然后让我们来看Promise的then方法:

  1. 首先我们写了一个已经resolved的promise,并将其赋值给p
  2. 然后使用p.then(),在回调函数里面打印参数,得到‘123’
  3. 最后p.then()整体返回的是个初始化(pending)的promise。

现在我们明白一下这个代码中promise的用法了吧?

app/hero.service.ts(部分代码)

复制代码
@Injectable()
export class HeroService {
  getHeroes() {
    return Promise.resolve(HEROES);
  }
  // See the "Take it slow" appendix
 getHeroesSlowly() { return new Promise<Hero[]>(resolve => setTimeout(() => resolve(HEROES), 2000) // 2 seconds  ); } }
复制代码

那么我们为何要使用promise呢?主要是为了解决回调地狱的问题。因为有了promise,你不必再写深层的回调,而是像极了同步的写法。

这是我的一个ng1的项目的部分代码,用promise的then()来解决回调地狱。

复制代码
Auth.$createUser({email: email, password: pass})
                        .then(function() {
                            // authenticate so we have permission to write to Firebase
                            return Auth.$authWithPassword({ email: email, password: pass });
                        })
                        .then(function(user) {
                            // create a user profile in our data store
                            var ref = wdutil.ref('users', user.uid);
                            return wdutil.handler(function(cb) {
                                ref.set({email: email, name: $scope.name||firstPartOfEmail(email)}, cb);
                            });
                        })
                        .then(function(/* user */) {
                            $scope.wait.show=false;
                            // redirect to the account page
                            $location.path('/account');
                        }, function(err) {
                            $scope.wait.show=false;
                            $scope.alerts.push({type:'danger',msg:wdutil.errMessage(err)});
                        });
复制代码

Interface

在编写这个服务的过程中我们使用了interface这个概念,这个知识点属于ts的范畴,我们通常在接口中声明类型,有点像react中的propTypes

app/hero.ts

export interface Hero {
  id: number;
  name: string;
}

然后我们在我们的服务中使用了这个接口:

app/hero.service.ts(部分代码)

import {Hero} from './hero';

app/hero.service.ts(部分代码)

return new Promise<Hero[]>(resolve =>
      setTimeout(() => resolve(HEROES), 2000) // 2 seconds
    );

除此之外,我们在我们的组件里面也多次使用了这个接口:

app/app.component.ts

heroes: Hero[];
  selectedHero: Hero;

app/hero-detail.component.ts

export class HeroDetailComponent {
  hero: Hero;
}

到此为止,我们的服务就算是写好了!

使用服务

让我们在组件中测试一下我们写好的服务吧:

app/app.component.ts(部分代码)

import {HeroService} from './hero.service';

app/app.component.ts(部分代码)

providers: [HeroService]

app/app.component.ts(部分代码)

复制代码
constructor(private _heroService: HeroService) { }

  getHeroes() {
    this._heroService.getHeroes().then(heroes => this.heroes = heroes);
  }
复制代码

以上代码我们干了这些事儿:

  1. 利用模块系统导入这个服务类
  2. 在组件中注入这个服务
  3. 在构造函数中将这个服务赋给一个私有变量_heroService
  4. 然后就可以尽情地在类中使用这个服务对象了this._heroService

这里的getHeroes()返回了一个Promise,所以我们可以使用then来处理接下来要发生的事。

Angular 应用中处理浏览器后退按钮的行为通常涉及对 `Location` 服务和 `Router` 模块的使用Angular 提供了多种方式来监听和响应浏览器的历史导航事件,包括后退按钮的点击行为。 ### 使用 `Location` 服务 Angular 的 `Location` 服务可以用于与浏览器的历史记录进行交互。通过该服务,可以获取当前 URL 或者监听 URL 的变化。例如,可以通过订阅 `popstate` 事件来捕获浏览器后退按钮的点击: ```typescript import { Location } from '@angular/common'; import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-root', template: `<router-outlet></router-outlet>` }) export class AppComponent implements OnInit, OnDestroy { private backEventSubscription: any; constructor(private location: Location) {} ngOnInit() { this.backEventSubscription = this.location.subscribe((event: PopStateEvent) => { // 处理浏览器后退按钮的点击 console.log('Back button clicked'); }); } ngOnDestroy() { if (this.backEventSubscription) { this.backEventSubscription.unsubscribe(); } } } ``` ### 使用 `Router` 模块 Angular 的 `Router` 模块提供了更高级的导航功能,可以通过订阅 `events` 来监听导航事件。例如,可以通过订阅 `NavigationStart` 和 `NavigationEnd` 事件来跟踪导航状态: ```typescript import { Router, NavigationStart, NavigationEnd } from '@angular/router'; import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-root', template: `<router-outlet></router-outlet>` }) export class AppComponent implements OnInit, OnDestroy { private navigationSubscription: any; constructor(private router: Router) {} ngOnInit() { this.navigationSubscription = this.router.events.subscribe(event => { if (event instanceof NavigationStart) { // 导航开始时的处理 console.log('Navigation started'); } if (event instanceof NavigationEnd) { // 导航结束时的处理 console.log('Navigation ended'); } }); } ngOnDestroy() { if (this.navigationSubscription) { this.navigationSubscription.unsubscribe(); } } } ``` ### 处理复杂的导航逻辑 在某些情况下,可能需要在用户点击后退按钮时执行特定的逻辑,例如显示确认对话框或阻止导航。可以通过结合 `Router` 的 `navigate` 方法和 `canDeactivate` 守卫来实现更复杂的导航控制。 例如,使用 `canDeactivate` 守卫可以阻止用户离开当前页面,直到满足某些条件: ```typescript import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { Observable } from 'rxjs'; export interface CanComponentDeactivate { canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; } @Injectable({ providedIn: 'root' }) export class DeactivateGuard implements CanDeactivate<CanComponentDeactivate> { canDeactivate(component: CanComponentDeactivate) { return component.canDeactivate ? component.canDeactivate() : true; } } ``` 然后在路由配置中使用该守卫: ```typescript import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DeactivateGuard } from './deactivate.guard'; import { MyComponent } from './my.component'; const routes: Routes = [ { path: 'my-path', component: MyComponent, canDeactivate: [DeactivateGuard] } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {} ``` ### 结论 通过 `Location` 服务和 `Router` 模块,Angular 提供了多种方式来处理浏览器后退按钮的行为。可以根据具体需求选择合适的方法来实现导航控制和事件监听。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值