Angular 2のコンポーネント間でデータを共有するにはどうすればよいですか?


84

Angular 1.xxでは、同じサービスを要求するだけで、最終的に同じインスタンスになり、サービス内のデータを共有できるようになります。

現在、Angular 2には、自分のサービスへの参照を持つコンポーネントがあります。サービス内のデータを読み取って変更できます。これは良いことです。同じサービスを別のコンポーネントに挿入しようとすると、新しいインスタンスを取得したように見えます。

私は何が間違っているのですか?パターン自体が間違っている(サービスを使用してデータを共有している)のでしょうか、それともサービスをシングルトン(アプリの1つのインスタンス内)としてマークする必要があるのでしょうか?

私は2.0.0-alpha.27/ ところで

アノテーションのappInjector(edit:now providers)を介してサービスを挿入@Componentし、コンストラクターに参照を保存します。これは、コンポーネント内でローカルに機能します。私が思っていたように、コンポーネント間では機能しません(同じサービスインスタンスを共有しません)。

更新:Angular 2.0.0の時点で、@ ngModuleがあり、上記のprovidersプロパティの下にサービスを定義します@ngModule。これにより、そのサービスの同じインスタンスがそのモジュール内の各コンポーネント、サービスなどに渡されるようになります。 https://angular.io/docs/ts/latest/guide/ngmodule.html#providers

更新:AngularとFEの開発全般に多くのことが起こりました。@noriricoが述べたように、NgRxのような状態管理システムを使用することもできます:https://ngrx.io/


角度成分の間でデータを共有するための6つのメソッド: - angulartutorial.net/2017/12/...
Prashobh

あなたがここに来る場合はSTATE管理システムを使用することを検討してください
noririco

回答:


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'に1つのサービスインスタンスのみを挿入します。子コンストラクターにパラメーターを追加すると、同じインスタンスが自動的に継承されます:)新しいインスタンスを作成する子コンポーネントに別のappInjectorを追加するという間違いを犯しました。
パーHornshøj-Schierbeck

1
@AlexanderCrushあなたはあなたの答えを更新できますか?それ以降のアルファバージョン(アルファ30以降)では、appInjectorが削除されました。正解は、今のところ、を使用することviewInjectorです。
エリックマルティネス

1
@EricMartinezありがとう、回答とプランカーが更新されました。
アレクサンダーエルモロフ2015

1
これが機能する理由と方法を理解するための興味深いリソース:blog.thoughtram.io/angular/2015/08/20/…
soyuka 2016

2
メインアプリと、を使用してコンポーネント自体にサービスを挿入していたため、同じ問題が発生していましたproviders: [MyService]。プロバイダーを削除すると、アプリの唯一のインスタンスになりました
maufarinelli 2016

43

@maufarinelliのコメントは、それを見るまで、@ Alexander Ermolovの回答があっても、この問題で頭を壁にぶつけていたので、独自の回答に値します。

問題は、providersあなたがあなたのcomponent:にを追加するときです

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

これにより、サービスの新しいインスタンスが注入されます...シングルトンではありません。

したがって、providers: [MyService]を除くアプリケーション内のすべてのインスタンスを削除すると、機能しますmodule


2
コメントですが、シングルトンではありません。渡されるのと同じインスタンスです。あなたはまだ...新しいインスタンスを要求することができる
パーHornshøj-Schierbeck

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 2017

私はこのアプローチを大きなソリューションで使用して、多くのコンポーネント間でデータを共有しています。多くの子を持ち、それぞれが同じオブジェクトを受け取ることができます。うまくいかないと言う前にこれをやろうとしましたか?
Alexis Gamarra 2017

はい、しました。それは動作します....しかし、いくつかの問題を解決するためにいくつかの「ハッキング」があります。あなたの答えは誰もがそれを使用することを許可していません。
sancelot 2017

2

多くの方法があります。これは、親要素と子要素の間の伝播を使用した例です。これは非常に効率的です。

2つのフォーム内で2つの方法のデータバインディングの使用法を表示できる例を提出しました。誰かがplunkrサンプルを提供できるなら、これはとてもいいでしょう;-)

サービスプロバイダーを使用して別の方法を探すことができます。参考までにこのビデオもご覧ください:(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には2つの子BとCがあり、AとB間、またはAとC間でデータを共有する場合は、(入力/出力)を使用します。

BとCの間で共有したい場合は、(入力/出力)も使用できますが、サービスを使用することをお勧めします。

b)ツリーが大きくて複雑な場合。(親と子の接続のレベルが非常に多い場合。)そしてこの場合、データを共有したい場合は、ngrxをお勧めします。

これは、任意のコンポーネントがサブスクライブでき、競合状態を作成せずに更新できるクライアント側ストアを作成するフラックスアーキテクチャを実装します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.