Angular 2의 구성 요소간에 데이터를 어떻게 공유합니까?


84

Angular 1.xx에서는 단순히 동일한 서비스를 요청하면 동일한 인스턴스로 끝나므로 서비스에서 데이터를 공유 할 수 있습니다.

이제 Angular 2에는 내 서비스에 대한 참조가있는 구성 요소가 있습니다. 나는 서비스의 데이터를 읽고 수정할 수 있습니다. 다른 구성 요소에 동일한 서비스를 삽입하려고하면 새 인스턴스를 얻는 것처럼 보입니다.

내가 도대체 ​​뭘 잘못하고있는 겁니까? 패턴 자체가 잘못되었거나 (서비스를 사용하여 데이터를 공유) 서비스를 싱글 톤 (앱의 한 인스턴스 내) 또는 다른 것으로 표시해야합니까?

나는 2.0.0-alpha.27/ btw에있다

주석의 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/


각 구성 요소간에 데이터를 공유하기위한 여섯 개 방법 : - angulartutorial.net/2017/12/...
Prashobh

여기에
오시면

답변:


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
나는 그것을 알아. '앱'에 하나의 서비스 인스턴스 만 삽입합니다. 하위 생성자에 매개 변수를 추가 할 때 동일한 인스턴스가 자동으로 상속됩니다. :) 새 인스턴스를 만드는 하위 구성 요소에 다른 appInjector를 추가하는 실수를 저질렀습니다.
당 Hornshøj-Schierbeck

1
@AlexanderCrush 답변을 업데이트 할 수 있습니까? 이후 알파 버전 (alpha 30+)에서는 appInjector가 제거되었습니다 . 현재 정답은를 사용하는 것 viewInjector입니다.
Eric Martinez

1
@EricMartinez 감사합니다, 답변 및 plunker가 업데이트되었습니다.
Alexander Ermolov 2015 년

1
이것이 작동하는 이유와 방법을 이해하는 흥미로운 리소스 : blog.thoughtram.io/angular/2015/08/20/… .
soyuka

2
메인 앱과 .NET Framework를 사용하는 구성 요소 자체에 서비스를 주입했기 때문에 동일한 문제가 발생했습니다 providers: [MyService]. 공급자를 제거, 그것은 응용 프로그램의 유일한 인스턴스가되었다
maufarinelli

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

많은 구성 요소간에 데이터를 공유하는 큰 솔루션에서이 접근 방식을 사용하고 있습니다. 많은 자식을 가질 수 있으며 각 자식은 동일한 개체를받을 수 있습니다. 작동하지 않는다고 말하기 전에 이것을 시도 했습니까?
Alexis Gamarra

응 나는 했어 . 그것은 작동합니다 .... 그러나 몇 가지 문제를 해결하기 위해 몇 가지 "해킹"이 있습니다. 귀하의 답변으로는 누구도 사용할 수 없습니다.
sancelot

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에는 두 개의 자식 B와 C가 있으며 A와 B 또는 A와 C간에 데이터를 공유하려면 (입력 / 출력)을 사용합니다.

B와 C 간 공유를 원하시면 입력 / 출력도 가능하지만 서비스 이용을 권장합니다.

b) 나무가 크고 복잡한 경우. (상위 및 하위 연결 수준이 너무 많은 경우)이 경우 데이터를 공유하려면 ngrx를 제안 합니다.

모든 구성 요소가 구독 할 수 있고 경쟁 조건을 생성하지 않고 업데이트 할 수있는 클라이언트 측 저장소를 생성하는 플럭스 아키텍처를 구현합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.