使用 Angular2 中的服务在组件之间共享数据
Posted
技术标签:
【中文标题】使用 Angular2 中的服务在组件之间共享数据【英文标题】:Share data between components using a service in Angular2 【发布时间】:2017-07-22 21:39:05 【问题描述】:我正在使用 angular2 开发一个应用程序。我有一个场景,我需要在路由(使用 router.navigate())时将复杂数据(对象数组)从一个组件传递到另一个组件(它们不是父子,它们是两个独立的组件)。我用谷歌搜索了这个,大部分结果都描述了父子组件的场景。我浏览了结果,找到了这些传递数据的方法。
1) 创建共享服务 2) 作为路由参数传递
第二种方法对我有用(尽管我不喜欢上面解释的复杂数据)。我无法使用共享服务共享数据。 所以我的问题是,使用服务在组件之间传递数据是否仅在组件处于父子关系时才有效?另外,请告诉我是否还有其他首选方式在一个组件和另一个组件之间传递数据?
更新: 我正在从我的场景中添加一些代码。请让我知道我的错误,为什么通过共享服务传递数据不起作用。
我有 2 个组件:1) SearchComponent 2) TransferComponent 我在SearchComponent中设置数据,想通过utilityService访问TransferComponent中的数据。
实用服务:
import Injectable from "@angular/core";
@Injectable()
export class UtilityService
constructor()
public bioReagentObject = [];
routeBioReagentObj(bioReagentObj)
console.log("From UtilityService...", bioReagentObj);
for (let each of bioReagentObj)
this.bioReagentObject.push(each)
// console.log("From UtilityService",this.bioReagentObject);
returnObject()
console.log("From UtilityService",this.bioReagentObject);
return this.bioReagentObject
搜索组件.ts
import UtilityService from "../services/utilityservice.component";
import Component, OnInit, OnDestroy, Input from '@angular/core';
@Component(
selector: 'bioshoppe-search-details',
providers: [UtilityService],
templateUrl: 'app/search/searchdetails.component.html',
styleUrls: ['../../css/style.css']
)
export class SearchDetailsComponent implements OnInit, OnDestroy
constructor(private route: ActivatedRoute,
private utilityService: UtilityService,
private router: Router)
@Input() selected: Array<String> = [barcode:123, description:"xyz"];
//This method is called from .html and this.selected has the data to be shared.
toTransfer()
this.utilityService.routeBioReagentObj(this.selected);
this.router.navigate(['/transfer']);
TransferService.ts
import Component, Input, OnInit, OnDestroy from '@angular/core';
import TransferService from "../services/transferservice.component";
import UserService from "../services/userservice.component";
import SearchService from "../services/searchservice.component";
import ActivatedRoute from '@angular/router';
import UtilityService from "../services/utilityservice.component";
@Component(
selector: 'bioshoppe-transfer',
providers: [TransferService, UserService, SearchService, UtilityService],
templateUrl: 'app/transfer/transfer.component.html',
styleUrls: ['../../css/style.css', '../../css/transfer.component.css']
)
export class TransferComponent implements OnInit, OnDestroy
constructor(private transferService: TransferService,
private userService: UserService,
private searchService: SearchService,
private utilityService: UtilityService,
private route: ActivatedRoute
)
ngOnInit()
// I am trying to access the data here, but it print "undefind"
console.log("From Transferpage",this.utilityService.returnObject());
我确定有问题,但只是我无法弄清楚。感谢任何帮助。
【问题讨论】:
不,服务不需要有父子关系。服务可以创建为模块的单例,并通过模块重用。 一个共享服务可以在任何组件之间共享,它们不必是父/子关系。在大多数情况下,共享服务应该可以工作,您能否提供一些示例代码来说明您拥有什么以及您正在尝试做什么。这样人们就可以尝试给你例子 如果你愿意,你可以只拥有 DataService。 ***.com/questions/36835123/… @davidejones 我添加了代码以使我的场景更加清晰。如果您发现任何问题,请告诉我。 如果您想共享一项服务并在其上保存数据,请确保该服务是一个模块的提供者,该模块位于所有其他模块之上。例如,将您的UtilityService
添加到您的app.module.ts
的providers:
。如果您将提供程序添加到 @Component()
,您将获得该服务的全新实例,在这种情况下,您的 UtilityService
。
【参考方案1】:
卡马龙是对的。你的错误是你两次声明UtilityService
:
-
曾经在
SearchComponent
的提供者中。
曾经在TransferComponent
的提供者中。
您应该只声明一次服务,以确保两个组件获得相同的实例。为此,您可以在以下任一选项中进行选择:
-
在
@NgModule
的提供者中声明服务,该@NgModule
在其声明中同时具有SearchComponent
和TransferComponent
。 10 次中有 9 次这是正确的解决方案!
在@Component
的提供者中声明服务,它是SearchComponent
和TransferComponent
的父级。这可能不可行,具体取决于您的组件树的外观。
按照选项 #1,您最终会得到:
@NgModule(
imports: [CommonModule, ...],
// Look, the components injecting the service are here:
declarations: [SearchComponent, TransferComponent, ...],
// Look, the service is declared here ONLY ONCE:
providers: [UtilityService, ...]
)
export class SomeModule
然后在组件的构造函数中注入UtilityService
,而不在组件的提供者中重新声明它:
@Component(
selector: 'foo',
template: '...',
providers: [] // DO NOT REDECLARE the service here
)
export class SearchComponent
constructor(private utilityService: UtilityService)
@Component(
selector: 'bar',
template: '...',
providers: [] // DO NOT REDECLARE the service here
)
export class TransferComponent
constructor(private utilityService: UtilityService)
【讨论】:
不将服务放入组件的 providers 数组会导致此错误:错误:未捕获(承诺中):错误:[服务名称]没有提供者 不应该。如果您在应用程序的其他位置在@NgModule.providers
中声明所述服务,则不会。请注意,您在其中声明服务的 NgModule 不应延迟加载。
我的设置与原始帖子类似。当我的其他组件调用 returnObject() 时,我收到错误,因为 http 调用尚未完成,并且 returnObject 中返回的变量尚未设置。我怎么能解决这个问题?
@Anthony:您从.subscribing
的 HTTP 调用中获取数据到它的 observable。您可能正在尝试订阅进行调用的服务,而不是订阅使用该服务的组件。您的服务应该返回 observable 包装 HTTP 调用(不订阅它),然后应用程序中对数据感兴趣的不同部分可以订阅该 observable。跨度>
@AngularFrance 我想我在关注你,但不确定如何多次订阅 observable。请查看我刚刚在这里提出的问题:***.com/questions/46429698/…【参考方案2】:
你需要区分 service 是来自 NgModule 还是来自其他 Component。
基础知识
在 Angular2 中,您有一个具有父子关系的组件分层树(如您所见)。同时,所有的服务都是在注入器的帮助下注入的,注入器也形成了一个层次结构树(与组件层次结构平行)。注入器树不必与组件树一致,因为出于效率目的可能存在来自父级的代理注入器。
工作原理
注入器的作用是将你在构造函数中定义的服务注入到你的组件中,即找到它,如果没有找到就启动并提供给你。当您将需要一些服务的构造函数放入构造函数时,它会从您的组件到根(即 NgModule)查找注入器树以确保服务存在。如果注入器发现该服务有定义的提供者但没有服务实例,它将创建该服务。如果没有供应商,它会继续上涨。
到目前为止你所看到的
您所看到的是,父组件通过“提供者”定义服务,而用于搜索服务的注入器在树中向上跳一跳,找到提供者,启动服务并提供给您的子组件。为了让某个子树的所有组件拥有相同的 Service,您只需在子树的公共根中定义 provider。如果您想为整个模块提供相同的服务,则需要在模块的提供者中定义此服务。这就是所谓的Singleton服务。
一些额外的
通常,如果您想注入服务并且一直找不到它,那么您将在编译/运行时出错。但是如果你定义要注入可选服务,如果没有找到该服务的提供者,注入器不会失败。
或多或少...
一些有用的链接:
https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html
https://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html
【讨论】:
我发现您的答案很有吸引力且完整,但是,它正在使用一些急需的代码 sn-ps 来提高您的答案的价值。 (例如***.com/a/40033975/3991654, ***.com/a/40191287/3991654) -- 恭敬地【参考方案3】:您可以通过使用 Observables 在两个组件之间共享数据来实现。
这篇文章不是我写的,但它非常有用且易于理解 -
一个快速的帖子展示了一个让我卡住了一段时间的例子 - 如何在 Angular 2/4 中的组件之间进行通信。
解决办法是使用一个 Observable 和一个 Subject(这是 observable 的一种类型)
http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject
【讨论】:
以上是关于使用 Angular2 中的服务在组件之间共享数据的主要内容,如果未能解决你的问题,请参考以下文章
使用 Typescript 在 Angular 2 中的组件之间共享行为