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

在 Angular 1.x.x 中,只需要请求相同的服务,就可以得到相同的实例,从而可以共享服务中的数据。

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

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

顺便说一下,我在 2.0.0-alpha.27/频道

我在 @Component注释中通过 appInjector(编辑: 现在是 providers)注入一个服务,然后在构造函数中保存一个引用。它在组件中本地工作——只是不像我想的那样跨组件(它们不共享相同的服务实例)。

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

更新 : 一般来说,角度和 FE 的发展已经发生了很多变化。正如@noririco 提到的,你也可以使用像 NgRx 这样的状态管理系统: Https://ngrx.io/

82856 次浏览

A service singleton is a nice solution. Other way - data/events bindings.

Here is an example of both:

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);

Watch live

You must use inputs and outputs of a @Component decorator. Here is the most basic example of using both;

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);

The comment by @maufarinelli deserves its own answer because until I saw it, I was still bashing my head against the wall with this issue even with @Alexander Ermolov's answer.

The problem is that when you add a providers to your component:

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

This causes a new instance of your service to be injected... rather than being a singleton.

So remove all instances of your providers: [MyService] in your application except in the module, and it will work!

In parent Component template:

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

In child Component:

@Input() hero: Hero;

Source: https://angular.io/docs/ts/latest/cookbook/component-communication.html

There are many ways. This one is an example using propagation between parent and child elements. This is very efficient.

I submitted an example that permits to view the usage of two ways databinding within two forms. If somebody can provide a plunkr sample this would be very nice ;-)

You may look for another way using a service provider. You may have a look at this video too for reference: (Sharing Data between Components in Angular)

mymodel.ts (data to share)

// 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;
}
}

Remember: There must be a parent that will share "mymodel" to child components.

Parent component

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() {
}
}

Child component, 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() {
}
}

Note: In some rare cases, you may have error when the HTML code is parsed, because the model is not "ready" to use at the initialisation of the page. In this case, prefix the HTML code with an ngIf condition:

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

It depends, if there is a simple case

a) A -> B -> C A has two child B and C and if you want to share data between A and B or A and C then use (input / output)

If you want to share between B and C then also you can use (input / output) but it is suggested to use Service.

b) If the tree is big and complex. (if there are so many levels of parent and children connections.) And in this case if you want to share data then I would suggest ngrx

It implements the flux architecture which creates a client side store to which any component can subscribe and can update without creating any race condition.