web-dev-qa-db-ja.com

Angular2のサービスを使用してコンポーネント間でデータを共有する

Angular2を使用してアプリケーションを開発しています。ルーティング中に(router.navigate()を使用して)あるコンポーネントから別のコンポーネント(親子ではなく、2つの別個のコンポーネント)に複雑なデータ(オブジェクトの配列)を渡す必要があるシナリオがあります。私はこれをグーグルで調べましたが、結果のほとんどは親子であるコンポーネントのシナリオを説明しています。私は結果を調べ、データを渡すこれらの方法を見つけました。

1)共有サービスの作成2)ルートパラメーターとして渡す

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

私は何かが間違っていると確信していますが、それは私がそれを理解することができないということだけです。

17
mrsan22

カマロンは正しい。あなたの間違いはUtilityServiceを2回宣言する

  1. SearchComponentのプロバイダーに入ったら。
  2. TransferComponentのプロバイダーに入ったら。

一度だけサービスを宣言して、両方のコンポーネントが同じインスタンスを取得するようにしてください。このため、次のオプションのいずれかを選択できます。

  1. 宣言にSearchComponentTransferComponentの両方がある@NgModuleのプロバイダーでサービスを宣言します。 10回のうち9回が正しい解決策です!
  2. SearchComponentTransferComponentの両方の親である@Componentのプロバイダーでサービスを宣言します。これは、コンポーネントツリーの外観によっては実行できない場合があります。

オプション#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) { }
}
24
AngularChef

サービスがNgModuleからのものであるか、他のコンポーネントからのものであるかを区別する必要があります。

基本

Angular2には、親と子の関係を持つコンポーネントの階層ツリーがあります(お気づきのとおり)。同時に、すべてのサービスはインジェクターの助けを借りて注入されます。インジェクターはまた、階層ツリーを形成します(コンポーネント階層と並行して)。インジェクターのツリーは、コンポーネントツリーと必ずしも一致する必要はありません。効率化のために、親からのプロキシインジェクターが存在する可能性があるためです。

仕組み

インジェクターの役割は、コンストラクターで定義したサービスを取得してコンポーネントに注入することです。つまり、それを見つけ、見つからない場合は開始し、提供します。何らかのサービスを必要とするコンストラクターに配置すると、サービスの存在について、コンポーネントからルート(NgModule)までのインジェクターツリーを調べます。インジェクタは、このサービスに対して定義されたプロバイダはあるがサービスインスタンスはないことを検出すると、サービスを作成します。プロバイダーが存在しない場合は、引き続き上昇します。

これまでに見たもの

これまでに見てきたのは、親コンポーネントが「プロバイダー」を通じてサービスを定義し、サービスを検索するインジェクターがツリー内を1ホップ進み、プロバイダーを見つけてサービスを開始し、子コンポーネントに提供することです。あるサブツリーのすべてのコンポーネントが同じサービスを持つようにするには、サブツリーの共通ルートでのみプロバイダーを定義する必要があります。モジュール全体で同じサービスを使用する場合は、モジュールのプロバイダーでこのサービスを定義する必要があります。これは、いわゆるSingletonサービスです。

一部の追加

通常、サービスをインジェクトしたいが、ルートまで完全に見つけられなかった場合、コンパイル/実行中にエラーが発生します。ただし、注入する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

4
laser

Observablesを使用して2つのコンポーネント間でデータを共有することで実現できます。

私はこの投稿を書きませんでしたが、非常に便利で理解しやすいです-

Angular 2/4。でコンポーネント間で通信する方法-私は少しの間動けなくなった何かの例を示す簡単な投稿。

解決策は、ObservableとSubject(observableの一種)を使用することです

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

0
Aamol