使用 Angular2 中的服务在组件之间共享数据

Share data between components using a service in Angular2

我正在使用 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
    }



}

searchcomponent.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());
}
}

我确定出了什么问题,但我无法理解 out.Any 感谢您的帮助。

卡马龙是对的。你的错误是你声明UtilityService两次:

  1. 曾经在 SearchComponent 的提供者中。
  2. 曾经在 TransferComponent 的供应商中。

您应该只声​​明一次服务以确保两个组件获得相同的实例。为此,您可以选择以下任一选项:

  1. @NgModule 的提供者中声明服务, 在其声明 中同时包含 SearchComponentTransferComponent。 10 次中有 9 次是正确的解决方案!
  2. @Component 的提供者中声明服务,它是 SearchComponentTransferComponent 父级。这可能不可行,具体取决于您的组件树的外观。

按照选项 #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) { }
}

你需要区分服务是来自NgModule,还是来自其他Component。

基础知识

在 Angular2 中,您有一个具有 parent-child 关系的组件层次结构树(如您所见)。同时,所有服务都在注入器的帮助下注入,注入器也形成了层次结构树(与组件层次结构平行)。注入器树不必与组件树重合,因为出于效率目的可能有来自 parent 的代理注入器。

工作原理

注入器的作用是获取您在构造函数中定义的服务并将其注入到您的组件中,即找到它,如果找不到则启动并提供给您。当你把你想要的一些服务放入构造函数时,它会从你的组件到根(即 NgModule)查找注入器树以查找服务是否存在。如果注入器发现该服务有定义的提供者但没有服务实例,它将创建该服务。如果没有提供者,它会继续上升。

你目前所见

你看到的是 parent 组件通过 "providers" 服务定义,搜索服务的注入器在树中向上跳一跳,找到提供者,启动服务并给你的 child 组件。为了使某个子树的所有组件都具有相同的服务,您只需要在子树的公共根中定义提供者。如果你想为整个模块提供相同的服务,你需要在模块的提供者中定义这个服务。这是 so-called Singleton 服务。

一些额外的

通常情况下,如果你想注入服务,但一直找不到它到 root,你会在 compilation/runtime 期间出错。但是,如果您定义要注入 Optional 服务,则如果找不到此服务的提供者,注入器将不会失败。

或多或少...

一些有用的链接:

  1. https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

  2. https://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html

您可以通过使用 Observables 在两个组件之间共享数据来实现。

这不是我写的 post 但它非常有用且易于理解 -

快速 post 展示一个让我卡住了一段时间的例子 - 如何在 Angular 2/4 中的组件之间进行通信。

解决方案是使用一个 Observable 和一个 Subject(这是一种 observable)

http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject