如何在Angular 2的组件之间共享数据?


84

在Angular 1.xx中,您仅要求提供相同的服务,并且最终获得相同的实例,从而可以共享服务中的数据。

现在在Angular 2中,我有一个引用了我的服务的组件。我可以读取和修改服务中的数据,这很好。当我尝试在另一个组件中注入相同的服务时,好像获得了一个新实例。

我究竟做错了什么?模式本身是错误的(使用服务共享数据)还是我需要将服务标记为单例(在应用的一个实例内)或其他?

我在2.0.0-alpha.27/ 顺便说一句

我通过注解中的appInjector(edit:now providers)注入服务@Component,然后在构造函数中保存引用。它在组件中本地工作-只是没有跨组件(它们不共享相同的服务实例),就像我认为的那样。

更新:从Angular 2.0.0开始,我们现在有了@ngModule,您可以在上的providers属性下定义said上的服务@ngModule。这将确保将该服务的相同实例传递到该模块中的每个组件,服务等。 https://angular.io/docs/ts/latest/guide/ngmodule.html#providers

更新:Angular和FE开发总体上发生了很多事情。如@noririco所述,您还可以使用像NgRx这样的状态管理系统:https ://ngrx.io/


六种方法用于共享角组件之间的数据: - angulartutorial.net/2017/12/...
Prashobh

如果你来到这里,请考虑使用状态管理系统
noririco

Answers:


63

服务单例是一个很好的解决方案。其他方式- data/events bindings

这是两个示例:

class BazService{
  n: number = 0;
  inc(){
    this.n++;
  }
}

@Component({
  selector: 'foo'
})
@View({
  template: `<button (click)="foobaz.inc()">Foo {{ foobaz.n }}</button>`
})
class FooComponent{
  constructor(foobaz: BazService){
    this.foobaz = foobaz;
  }
}

@Component({
  selector: 'bar',
  properties: ['prop']
})
@View({
  template: `<button (click)="barbaz.inc()">Bar {{ barbaz.n }}, Foo {{ prop.foobaz.n }}</button>`
})
class BarComponent{
  constructor(barbaz: BazService){
    this.barbaz = barbaz;
  }
}

@Component({
    selector: 'app',
    viewInjector: [BazService]
})
@View({
  template: `
    <foo #f></foo>
    <bar [prop]="f"></bar>
  `,
  directives: [FooComponent, BarComponent]
})
class AppComponent{}

bootstrap(AppComponent);

观看直播


20
我想到了。您只需注入一个服务实例-在“ app”中。将参数添加到子构造函数时,会自动继承该实例:)我犯了一个错误,即在子组件中添加了另一个appInjector来创建新实例。
PerHornshøj-Schierbeck2015年

1
@AlexanderCrush您可以更新您的答案吗?由于在更高的alpha版本(alpha 30+)中,删除了appInjector。目前,正确的答案应该是使用viewInjector
埃里克·马丁内斯

1
@EricMartinez谢谢,答案和朋克已更新。
2015年

1
有趣的资源以了解其原因和工作方式:blog.thoughtram.io/angular/2015/08/20/…
soyuka '16

2
我遇到了同样的问题,因为我在主应用程序中以及在组件本身中使用注入了Service providers: [MyService]。删除提供程序,它成为该应用程序的唯一实例
maufarinelli

43

@maufarinelli的评论应有自己的答案,因为直到我看到它为止,即使使用@Alexander Ermolov的答案,我仍在犹豫不决。

问题是,当您添加providers到您的component

@Component({
    selector: 'my-selector',
    providers: [MyService],
    template: `<div>stuff</div>`
})

这将导致注入新的服务实例...而不是成为一个单例

因此,删除providers: [MyService]您的应用程序中除之外的所有实例,module它将起作用!


2
只是评论,它永远不会是单例-只是传递了相同的实例。您仍然可以请求一个新实例...
PerHornshøj-Schierbeck'17

10

您必须使用@Component装饰器的输入和输出。这是同时使用两者的最基本示例;

import { bootstrap } from 'angular2/platform/browser';
import { Component, EventEmitter } from 'angular2/core';
import { NgFor } from 'angular2/common';

@Component({
  selector: 'sub-component',
  inputs: ['items'],
  outputs: ['onItemSelected'],
  directives: [NgFor],
  template: `
    <div class="item" *ngFor="#item of items; #i = index">
      <span>{{ item }}</span>
      <button type="button" (click)="select(i)">Select</button>
    </div>
  `
})

class SubComponent {
  onItemSelected: EventEmitter<string>;
  items: string[];

  constructor() {
    this.onItemSelected = new EventEmitter();
  }

  select(i) {
    this.onItemSelected.emit(this.items[i]);
  }
}

@Component({
  selector: 'app',
  directives: [SubComponent],
  template: `
    <div>
      <sub-component [items]="items" (onItemSelected)="itemSelected($event)">
      </sub-component>
    </div>
  `
})

class App {
  items: string[];

  constructor() {
    this.items = ['item1', 'item2', 'item3'];
  }

  itemSelected(item: string): void {
    console.log('Selected item:', item);
  }
}

bootstrap(App);

7
无需导入ngFor
理查德·汉密尔顿

7

在父组件模板中:

<hero-child [hero]="hero">
</hero-child>

在子组件中:

@Input() hero: Hero;

资料来源:https : //angular.io/docs/ts/latest/cookbook/component-communication.html


可能是,但这需要更多细节。在现实世界中,这并非易事。假设您有一个要在多个组件之间共享并访问数据的类。那行不通。
sancelot '17

我在大型解决方案中使用这种方法来在许多组件之间共享数据。您可以有多个孩子,每个孩子都接收相同的对象。在说这行不通之前,您是否尝试过这样做?
亚历克西斯·

是的,我做到了。它可以工作..但是可以解决一些问题。您的答案不允许任何人使用。
sancelot '17

2

有很多方法。这是一个使用父元素和子元素之间传播的示例。这非常有效。

我提交了一个示例,该示例允许查看两种形式中数据绑定的两种使用方式。如果有人可以提供一个样本,这将是非常好的;-)

您可能会使用服务提供商寻找其他方式。您也可以看一下此视频,以供参考:(在Angular中的组件之间共享数据

mymodel.ts(要共享的数据)

// Some data we want to share against multiple components ...
export class mymodel {
    public data1: number;
    public data2: number;
    constructor(
    ) {
        this.data1 = 8;
        this.data2 = 45;
    }
}

切记:必须有一个父级将与子级组件共享“ mymodel”。

父组件

import { Component, OnInit } from '@angular/core';
import { mymodel } from './mymodel';
@Component({
    selector: 'app-view',
    template: '<!-- [model]="model" indicates you share model to the child component -->
        <app-mychild [model]="model" >
        </app-mychild>'

        <!-- I add another form component in my view,
         you will see two ways databinding is working :-) -->
        <app-mychild [model]="model" >
        </app-mychild>',
})

export class MainComponent implements OnInit {
    public model: mymodel;
    constructor() {
        this.model = new mymodel();
    }
    ngOnInit() {
    }
}

子组件mychild.component.ts

import { Component, OnInit,Input } from '@angular/core';
import { FormsModule }   from '@angular/forms'; // <-- NgModel lives here
import { mymodel } from './mymodel';

@Component({
    selector: 'app-mychild',
    template: '
        <form #myForm="ngForm">
            <label>data1</label>
            <input type="number"  class="form-control" required id="data1 [(ngModel)]="model.data1" name="data1">
            <label>val {{model.data1}}</label>

            label>data2</label>
            <input  id="data2"  class="form-control" required [(ngModel)]="model.data2" name="data2" #data2="ngModel">
            <div [hidden]="data2.valid || data2.pristine"
                class="alert alert-danger">
                data2 is required
            </div>

            <label>val2 {{model.data2}}</label>
        </form>
    ',
})

export class MychildComponent implements OnInit {
    @Input() model: mymodel ;  // Here keywork @Input() is very important it indicates that model is an input for child component
    constructor() {
    }
    ngOnInit() {
    }
}

注意:在极少数情况下,解析HTML代码时可能会出错,因为该模型尚未“准备好”在页面初始化时使用。在这种情况下,请在HTML代码前添加ngIf条件:

<div *ngIf="model"> {{model.data1}} </div>

1

视情况是否简单而定

a)A-> B-> C A有两个孩子B和C,如果要在A和B或A和C之间共享数据,请使用(输入/输出)

如果要在B和C之间共享,则也可以使用(输入/输出),但建议使用Service。

b)如果树又大又复杂。(如果有很多级别的父级和子级连接。)在这种情况下,如果您要共享数据,那么我建议使用ngrx

它实现了流量架构,该架构创建了一个客户端存储,任何组件都可以订阅该客户端存储,并且可以在不创建任何竞争条件的情况下进行更新。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.