前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AngularDart4.0 英雄之旅-教程-06服务 顶

AngularDart4.0 英雄之旅-教程-06服务 顶

作者头像
南郭先生
发布2018-08-14 15:27:45
2.9K0
发布2018-08-14 15:27:45
举报
文章被收录于专栏:Google DartGoogle Dart

随着“英雄之旅”应用的发展,您将添加更多需要访问英雄数据的组件。

不是一遍又一遍复制和粘贴相同的代码,而是创建一个可重用的数据服务,并将其注入到需要它的组件中。 使用单独的服务可使组件保持精简并专注于支持视图,并使用模拟服务对组件进行单元测试变得容易。

因为数据服务总是异步的,所以您将使用数据服务的基于Future的版本来完成页面。

当你完成这个页面,应用程序应该看起来像这个实例(查看源代码)。

你开始的地方

在继续英雄之旅之前,请确认您具有以下结构。 如果没有,请返回前面的页面。

如果该应用程序尚未运行,请启动该应用程序。 在进行更改时,请通过重新加载浏览器窗口来保持运行。

创建一个英雄服务

利益相关者希望以不同的页面以各种方式展示英雄。 用户可以从列表中选择一个英雄。 不久,您将添加一个仪表板与顶尖的表演英雄,并创建一个单独的视图编辑英雄的细节。 所有三个视图都需要英雄数据。

目前,AppComponent定义了模拟英雄的显示。 然而,定义英雄不是组件的工作,你不能轻易与其他组件和视图共享英雄名单。 在这个页面中,您将把英雄数据采集业务转移到一个提供数据的服务中,并与需要数据的所有组件共享该服务。

创建一个可注入的HeroService

在lib / src下创建文件hero_service.dart。

服务文件的命名约定是小写的服务名称,后跟_service。 对于多词服务名称,请使用小写的snake_case。 例如,SpecialSuperHeroService的文件名是special_super_hero_service.dart。

 命名类HeroService。lib/src/hero_service.dart (empty class)

import 'package:angular/angular.dart';

@Injectable()
class HeroService {
}

注意你使用了@Injectable()注解。 这告诉Angular编译器,HeroService将成为注入的候选者(更多关于这个)。

获取英雄数据

HeroService可以从任何地方(Web服务,本地存储或模拟数据源)获取英雄数据。 现在,导入Hero和mockHeroes,并从getHeroes()方法返回模拟英雄:lib/src/hero_service.dart

import 'package:angular/angular.dart';

import 'hero.dart';
import 'mock_heroes.dart';

@Injectable()
class HeroService {
  List<Hero> getHeroes() => mockHeroes;
}

使用英雄服务

您已经准备好在其他组件中使用HeroService,从AppComponent开始。

导入HeroService,以便您可以在代码中引用它。lib/app_component.dart (hero service import)

import 'src/hero_service.dart';

不要使用new实例化HeroService AppComponent应该如何获取HeroService的实例? 你可能会像这样建一个HeroService的新实例:lib/app_component.dart (excerpt)

HeroService heroService = new HeroService(); // DON'T do this

但是,这个选项并不理想,原因如下:

  • 组件必须知道如何创建一个HeroService。 如果您更改HeroService构造函数,则必须查找并更新您创建服务的每个位置。 在多个地方修补代码是容易出错的,并增加了测试负担。
  • 每次使用新建时都会创建一个服务。 如果服务缓存英雄,并与他人共享缓存呢? 你不能这样做。
  • 通过将AppComponent锁定到HeroService的特定实现中,切换实现用于不同的场景(如离线操作或使用不同的模拟版本进行测试)将很困难。

注入HeroService 而不是使用新的表达式,添加这些行:

  • 添加一个私人的HeroService属性。
  • 添加一个初始化私有属性的构造函数。
  • 将HeroService添加到组件的提供程序元数据。

这里是属性和构造函数:lib/app_component.dart (constructor)

final HeroService _heroService;
AppComponent(this._heroService);

 构造函数除了设置_heroService属性外什么也不做。 _heroService的HeroService类型将构造函数的参数标识为HeroService注入点。 现在Angular知道在创建一个新的AppComponent时要提供一个HeroService实例。

依赖注入页面阅读更多关于依赖注入的内容。

注入器不知道如何创建一个HeroService。 如果您现在运行代码,Angular会失败并显示以下错误: 

EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)

为了教导注入器如何创建HeroService,请添加以下提供程序列表作为@Component注解的最后一个参数。lib/app_component.dart (providers)

providers: const [HeroService],

providers参数告诉Angular在创建一个AppComponent时创建一个HeroService的新实例。 AppComponent及其子组件可以使用该服务来获取英雄数据。

AppComponent.getHeroes()方法

添加一个getHeroes()方法到应用程序组件,并删除英雄初始值设定项:lib/app_component.dart (heroes and getHeroes)

List<Hero> heroes;

void getHeroes() {
  heroes = _heroService.getHeroes();
}

ngOnInit生命周期钩子

AppComponent应该可以获取并显示英雄数据,而不会出现问题。

您可能会试图在构造函数中调用getHeroes()方法,但构造函数不应包含复杂的逻辑,特别是调用服务器的构造函数(如数据访问方法)。 构造函数用于简单的初始化,如将构造函数参数连接到属性。

要用Angular调用getHeroes(),可以实现Angular ngOnInit生命周期钩子。 Angular为组件生命周期中的关键时刻提供接口:创建,每次更改之后,最终销毁。

每个接口都有一个方法。 当组件实现该方法时,Angular会在适当的时候调用它。

在“Lifecycle Hooks”页面中详细了解生命周期挂钩。

将OnInit添加到由AppComponent实现的接口列表中,并使用里面的初始化逻辑编写一个ngOnInit()方法。 Angular会在正确的时间调用它。 在这种情况下,通过调用getHeroes()来初始化。

class AppComponent implements OnInit {
  void ngOnInit() => getHeroes();
}

 刷新浏览器。 当你点击一个英雄名字时,应用程序应该显示英雄名单和英雄详情视图。

异步英雄服务

HeroService立即返回模拟英雄列表; 它的getHeroes()签名是同步的。

lib/src/hero_service.dart (getHeroes)

List<Hero> getHeroes() => mockHeroes;

最终,英雄数据将来自远程服务器。 当使用远程服务器时,用户不必等待服务器响应; 此外,您在等待期间无法阻塞用户界面。

为了协调视图和响应,你可以使用Futures,这是一个改变getHeroes()方法签名的异步技术。

英雄服务返回一个Future

Future代表未来的计算或值。 使用Future,您可以注册回调函数,在计算完成时(结果准备就绪),或需要报告计算错误时调用。

这是一个简单的解释。 在“Asynchronous Programming: Futures”的Dart语言教程中阅读更多有关Futures的信息。

 添加dart:async的导入,因为它定义了Future,并使用这个Future返回的getHeroes()方法更新HeroService:lib/src/hero_service.dart (excerpt)

Future<List<Hero>> getHeroes() async => mockHeroes;

你还在模拟数据。 你正在模拟一个超快,零延迟的服务器的行为,通过返回一个模拟英雄立即可用的Future。

将方法标记为async会自动将返回类型设置为Future。 有关异步函数的更多信息,请参阅在Dart语言浏览中声明异步函数

处理Future

由于对HeroService的更改,应用程序组件的英雄属性现在是Future,而不是英雄列表。 您必须更改实现以在完成时处理Future结果。 当Future成功完成时,您将显示英雄。

这是当前的实现:lib/app_component.dart (synchronous getHeroes)

void getHeroes() {
  heroes = _heroService.getHeroes();
}

 将回调函数作为参数传递给Future.then()方法:lib/app_component.dart (asynchronous getHeroes)

void getHeroes() {
  _heroService.getHeroes().then((heroes) => this.heroes = heroes);
}

该回调将组件的英雄属性设置为服务返回的英雄列表。刷新浏览器。 该应用程序仍然运行,显示英雄列表,并响应名称选择与详细信息视图。

使用async/await

包含一个或多个Future.then()方法的异步方法可能难以阅读和理解。 谢天谢地,Dart的异步/等待语言功能可以让你编写看起来就像同步代码的异步代码。 重写getHeroes():lib/app_component.dart (revised async/await getHeroes)

Future<Null> getHeroes() async {
  heroes = await _heroService.getHeroes();
}

Future <Null>返回类型是异步void的等价物。

在Dart语言教程的Asynchronous Programming:FuturesAsync和await部分阅读更多关于使用async / await进行异步编程的内容。

在本页的末尾, Appendix: Take it slow描述应用程序可能与不良连接类似。

回顾应用程序结构

在所有重构之后验证您是否具有以下结构:

这里是本页讨论的代码文件。

lib/src/hero_service.dart

import 'dart:async';
import 'package:angular/angular.dart';
import 'hero.dart';
import 'mock_heroes.dart';
@Injectable()
class HeroService {
  Future<List<Hero>> getHeroes() async => mockHeroes;
}

lib/app_component.dart

import 'dart:async';
import 'package:angular/angular.dart';
import 'src/hero.dart';
import 'src/hero_detail_component.dart';
import 'src/hero_service.dart';
@Component(
  selector: 'my-app',
  templateUrl: 'app_component.html',
  styleUrls: const ['app_component.css'],
  directives: const [CORE_DIRECTIVES, HeroDetailComponent],
  providers: const [HeroService],
)
class AppComponent implements OnInit {
  final title = 'Tour of Heroes';
  final HeroService _heroService;
  List<Hero> heroes;
  Hero selectedHero;
  AppComponent(this._heroService);
  Future<Null> getHeroes() async {
    heroes = await _heroService.getHeroes();
  }
  void ngOnInit() => getHeroes();
  void onSelect(Hero hero) => selectedHero = hero;
}

你做过的操作

以下是您在此页面中所取得的成果:

  • 您创建了一个可以被许多组件共享的服务类。
  • 在AppComponent激活时,您使用ngOnInit生命周期挂钩来获取英雄数据。
  • 您将HeroService定义为AppComponent的提供者。
  • 您设计了服务来返回一个Future和从未来获取数据的组件。

你的应用应该看起来像这个实例(查看源代码)。

前方的路

英雄之旅已经变得更加可重复使用共享组件和服务。 下一个目标是创建一个仪表板,添加在视图之间路由的菜单链接,以及在模板中格式化数据。 随着应用程序的发展,你会发现如何设计它,使其更容易成长和维护。

阅读下一个教程页面中有关Angular组件路由器和视图之间的导航。

附录:数据延迟

要模拟一个缓慢的连接,请将以下getHeroesSlowly()方法添加到HeroService。

lib/src/hero_service.dart (getHeroesSlowly)

Future<List<Hero>> getHeroesSlowly() {
  return new Future.delayed(const Duration(seconds: 2), getHeroes);
}

像getHeroes()一样,它也返回一个Future,但是这个Future在完成前等待两秒钟。

回到AppComponent中,用getHeroesSlowly()替换getHeroes(),看看应用程序的行为。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017/11/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 你开始的地方
  • 创建一个英雄服务
    • 创建一个可注入的HeroService
      • 获取英雄数据
        • 使用英雄服务
          • AppComponent.getHeroes()方法
            • ngOnInit生命周期钩子
            • 异步英雄服务
              • 英雄服务返回一个Future
                • 处理Future
                • 使用async/await
                • 回顾应用程序结构
                • 你做过的操作
                • 前方的路
                • 附录:数据延迟
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档