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

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

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

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

你开始的地方

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

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

创建一个英雄服务

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

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

创建一个可注入的HeroService

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

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

命名类HeroServicelib/src/hero_service.dart (empty class)

import 'package:angular/angular.dart';

@Injectable()
class HeroService {
}

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

获取英雄数据

HeroService可以从任何地方(Web服务,本地存储或模拟数据源)获取英雄数据。 现在,导入HeromockHeroes,并从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属性外什么也不做。 _heroServiceHeroService类型将构造函数的参数标识为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()方法更新HeroServicelib/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:Futures的Async和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(),看看应用程序的行为。

下一节

AngularDart4.0 英雄之旅-教程-06服务的更多相关文章

  1. ios中的.dylib和.a lib有什么区别?

    我知道Objectivec中的编译和运行时是什么,但是我想知道是什么画了这两个库之间的界限?他们的目的是什么,除了陈述一个是静态的而另一个是动态的?我们何时需要一个而不是另一个?

  2. iOS:核心图像和多线程应用程序

    我试图以最有效的方式运行一些核心图像过滤器.试图避免内存警告和崩溃,这是我在渲染大图像时得到的.我正在看Apple的核心图像编程指南.关于多线程,它说:“每个线程必须创建自己的CIFilter对象.否则,你的应用程序可能会出现意外行为.”这是什么意思?我实际上是试图在后台线程上运行我的过滤器,所以我可以在主线程上运行HUD(见下文).这在coreImage的上下文中是否有意义?

  3. xamarin.ios – ShareKit与MonoTouch如何?

    有人可以验证ShareKit实际上是否可用于MonoTouch并指导我完成使其工作所需的步骤?解决方法您首先从getsharekit.com下载还是使用ShareKit2.0?

  4. ios – iPhone崩溃日志不能正确地符号化并且是双重间隔的

    任何建议超过欢迎.谢谢.解决方法当这件事发生在我身上时,它只是我通过电子邮件收到的日志.如果我记得,至少有一些是在.msg文件中,我不得不把它们拿出来.它可能是Exchange编码更改.如果你显示不可见的字符,你可能会看到每个字符之间的东西.您可以找到并替换它们以删除它们或更改编辑器中的编码.

  5. Xcode C开发,需要澄清

    我非常喜欢Xcode提供对该语言可能的成员函数的深入了解的方式,并且更喜欢相对于文本伙伴使用它,如果不是因为我今天注意到的奇怪.当strings=“Teststring”时;唯一可用的substr签名如图所示但据我所知,签名应该是什么iseeonline确实s.substr(1,2);既被理解也适用于Xcode.当我尝试方法完成时为什么不显示?

  6. ios – Xcode 7 beta 2:LaunchScreen.storyboard无法打开文档

    我在两个不同的Mac(iMac和MacBookpro)上收到这个错误.不知道为什么人们不能再现它,但我需要一些帮助.在运行XX优胜美地10.10.4的Mac上运行Xcode7beta2(15六月’15).甚至无法编译和运行我的项目..我从创建菜单创建了一个视图应用程序项目,就是这样.编辑:我试图删除并重新添加storyboard文件(也可以打开Main.storyboard插件),我仍然得到相同的

  7. xamarin.ios – 没有找到ViewController ::.ctor(System.IntPtr)的构造函数

    我有一个问题,我的Monotouch应用程序有时在收到内存警告后才会崩溃.请参见下面的堆栈跟踪.堆栈跟踪是正确的,因为指定的类缺少构造函数获取IntPtr参数.但是这是有意的,因为我在应用程序中根本不使用InterfaceBuilder.那为什么会这样呢?

  8. ios – 为具有多个目标和不同平台的项目编写Podfile

    如何让CocoaPods成功整合到我的项目和iOS/Mac目标?我已经阅读了Podfile文档,但发现它在这方面缺乏.解决方法得到它了!从我的每个目标和运行的pod安装中删除libPods-xxxx.a文件,再次执行了我的目标集成.

  9. UIWebView stringByEvaluatingJavaScriptFromString在使用GCD调用时挂在iOS5.0 / 5.1上

    我在viewDidLoad中有以下代码,它在iOS4.3上正常工作,但它挂在iOS5/5.1上.在iOS5/5.1上,显示警告对话框,但无法关闭,UI线程冻结,OK按钮无法单击.这是一个bug吗?解决方法经过测试,我认为它是一个Bug,并改变使用的代码会解决

  10. ios – iPhone:一段时间后,所有动画都停止工作

    我最近有一些奇怪的行为.所有动画有时会突然停止工作.有时候一切顺利,其他时候就会发生.推送和弹出视图只是捕捉到位,UITableViewcellrow动画不起作用.该应用程序使用了很多后台线程,所以也许有东西在那里?我不能真正发布代码,因为我不知道问题在哪里.有人有同样的问题吗?解决方法你可以尝试在不同的后台线程中更新UI/animate吗?

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部