如何在 Angular 2 中的组件之间共享数据?
How do I share data between components in Angular 2?
在 Angular 1.x.x 中,您只需请求相同的服务并最终得到相同的实例,从而可以在服务中共享数据。
现在 Angular 2 我有一个组件引用了我的服务。我可以读取和修改服务中的数据,这很好。当我尝试在另一个组件中注入相同的服务时,好像我得到了一个新实例。
我做错了什么?是模式本身错误(使用服务共享数据)还是我需要将服务标记为单例(在应用程序的一个实例中)或其他什么?
我在 2.0.0-alpha.27/
顺便说一句
我通过 appInjector
(编辑:现在 providers
)在 @Component
注释中注入一个服务,然后在构造函数中保存一个引用。它在组件中本地工作——只是不像我想象的那样跨组件(它们不共享同一个服务实例)。
更新:从Angular 2.0.0 开始,我们现在有了@ngModule,您可以在其中定义providers
属性 下的服务上说@ngModule
。这将确保将该服务的相同实例传递给该模块中的每个组件、服务等。
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
更新:Angular 和有限元开发总体上发生了很多变化。正如@noririco 提到的,您还可以使用像 NgRx 这样的状态管理系统:
https://ngrx.io/
服务单例是一个不错的解决方案。其他方式 - 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);
您必须使用@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);
@maufarinelli 的评论值得自己的回答,因为在我看到它之前,即使有 @Alexander Ermolov 的回答,我仍然在用这个问题撞墙。
问题是,当您将 providers
添加到 component
时:
@Component({
selector: 'my-selector',
providers: [MyService],
template: `<div>stuff</div>`
})
这会导致注入一个新的服务实例...而不是 singleton.
因此,请删除您的应用程序中 providers: [MyService]
的所有实例,module
除外,它将起作用!
在父组件模板中:
<hero-child [hero]="hero">
</hero-child>
在子组件中:
@Input() hero: Hero;
Source: https://angular.io/docs/ts/latest/cookbook/component-communication.html
方法有很多种。这是一个在父元素和子元素之间使用传播的示例。这样效率很高。
我提交了一个示例,允许查看两种表单中两种数据绑定方式的使用。如果有人可以提供一个 plunkr 示例,那就太好了 ;-)
您可以寻找另一种使用服务提供商的方式。
您也可以看看这个视频以供参考:
(Sharing Data between Components in 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 代码时可能会出错,因为模型不是 "ready" 在页面初始化时使用的。在这种情况下,在 HTML 代码前加上一个 ngIf 条件:
<div *ngIf="model"> {{model.data1}} </div>
视情况而定,有没有简单的情况
a) A -> B
-> C
A 有两个 child B 和 C,如果您想在 A 和 B 或 A 和 C 之间共享数据,请使用 (input / output)
如果你想在B和C之间共享,那么你也可以使用(输入/输出),但建议使用服务。
b) 如果树又大又复杂。 (如果有那么多级别的parent和children连接。)
在这种情况下,如果您想共享数据,那么我建议 ngrx
它实现了 flux 架构,该架构创建了一个客户端存储,任何组件都可以订阅该存储,并且可以在不创建任何竞争条件的情况下进行更新。
在 Angular 1.x.x 中,您只需请求相同的服务并最终得到相同的实例,从而可以在服务中共享数据。
现在 Angular 2 我有一个组件引用了我的服务。我可以读取和修改服务中的数据,这很好。当我尝试在另一个组件中注入相同的服务时,好像我得到了一个新实例。
我做错了什么?是模式本身错误(使用服务共享数据)还是我需要将服务标记为单例(在应用程序的一个实例中)或其他什么?
我在 2.0.0-alpha.27/
顺便说一句
我通过 appInjector
(编辑:现在 providers
)在 @Component
注释中注入一个服务,然后在构造函数中保存一个引用。它在组件中本地工作——只是不像我想象的那样跨组件(它们不共享同一个服务实例)。
更新:从Angular 2.0.0 开始,我们现在有了@ngModule,您可以在其中定义providers
属性 下的服务上说@ngModule
。这将确保将该服务的相同实例传递给该模块中的每个组件、服务等。
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
更新:Angular 和有限元开发总体上发生了很多变化。正如@noririco 提到的,您还可以使用像 NgRx 这样的状态管理系统: https://ngrx.io/
服务单例是一个不错的解决方案。其他方式 - 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);
您必须使用@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);
@maufarinelli 的评论值得自己的回答,因为在我看到它之前,即使有 @Alexander Ermolov 的回答,我仍然在用这个问题撞墙。
问题是,当您将 providers
添加到 component
时:
@Component({
selector: 'my-selector',
providers: [MyService],
template: `<div>stuff</div>`
})
这会导致注入一个新的服务实例...而不是 singleton.
因此,请删除您的应用程序中 providers: [MyService]
的所有实例,module
除外,它将起作用!
在父组件模板中:
<hero-child [hero]="hero">
</hero-child>
在子组件中:
@Input() hero: Hero;
Source: https://angular.io/docs/ts/latest/cookbook/component-communication.html
方法有很多种。这是一个在父元素和子元素之间使用传播的示例。这样效率很高。
我提交了一个示例,允许查看两种表单中两种数据绑定方式的使用。如果有人可以提供一个 plunkr 示例,那就太好了 ;-)
您可以寻找另一种使用服务提供商的方式。 您也可以看看这个视频以供参考: (Sharing Data between Components in 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 代码时可能会出错,因为模型不是 "ready" 在页面初始化时使用的。在这种情况下,在 HTML 代码前加上一个 ngIf 条件:
<div *ngIf="model"> {{model.data1}} </div>
视情况而定,有没有简单的情况
a) A -> B -> C A 有两个 child B 和 C,如果您想在 A 和 B 或 A 和 C 之间共享数据,请使用 (input / output)
如果你想在B和C之间共享,那么你也可以使用(输入/输出),但建议使用服务。
b) 如果树又大又复杂。 (如果有那么多级别的parent和children连接。) 在这种情况下,如果您想共享数据,那么我建议 ngrx
它实现了 flux 架构,该架构创建了一个客户端存储,任何组件都可以订阅该存储,并且可以在不创建任何竞争条件的情况下进行更新。