[javascript] Angular 2 형제 구성 요소 통신

ListComponent가 있습니다. ListComponent에서 항목을 클릭하면 해당 항목의 세부 정보가 DetailComponent에 표시되어야합니다. 둘 다 동시에 화면에 표시되므로 라우팅이 필요하지 않습니다.

ListComponent에서 클릭 한 항목을 DetailComponent에게 어떻게 알립니 까?

부모 (AppComponent)까지 이벤트를 생성하는 것을 고려했으며 부모가 @Input을 사용하여 DetailComponent에 selectedItem.id를 설정하도록했습니다. 또는 관찰 가능한 구독으로 공유 서비스를 사용할 수 있습니다.


편집 : 이벤트 + @Input을 통해 선택한 항목을 설정하면 추가 코드를 실행해야 할 경우 DetailComponent가 트리거되지 않습니다. 그래서 이것이 허용 가능한 해결책인지 확신하지 못합니다.


그러나이 두 방법 모두 $ rootScope. $ broadcast 또는 $ scope. $ parent. $ broadcast를 통해 작업을 수행하는 Angular 1 방법보다 훨씬 복잡해 보입니다.

Angular 2의 모든 것이 구성 요소이기 때문에 구성 요소 통신에 대한 더 많은 정보가 없다는 것에 놀랐습니다.

이를 수행하는 또 다른 /보다 간단한 방법이 있습니까?



답변

rc.4로 업데이트 됨 :
angular 2의 형제 구성 요소간에 전달되는 데이터를 가져 오려고 할 때 현재 가장 간단한 방법 (angular.rc.4)은 angular2의 계층 적 종속성 주입을 활용하고 공유 서비스를 만드는 것입니다.

서비스는 다음과 같습니다.

import {Injectable} from '@angular/core';

@Injectable()
export class SharedService {
    dataArray: string[] = [];

    insertData(data: string){
        this.dataArray.unshift(data);
    }
}

자, 여기에 PARENT 구성 요소가 있습니다.

import {Component} from '@angular/core';
import {SharedService} from './shared.service';
import {ChildComponent} from './child.component';
import {ChildSiblingComponent} from './child-sibling.component';
@Component({
    selector: 'parent-component',
    template: `
        <h1>Parent</h1>
        <div>
            <child-component></child-component>
            <child-sibling-component></child-sibling-component>
        </div>
    `,
    providers: [SharedService],
    directives: [ChildComponent, ChildSiblingComponent]
})
export class parentComponent{

} 

그리고 그 두 자녀

아이 1

import {Component, OnInit} from '@angular/core';
import {SharedService} from './shared.service'

@Component({
    selector: 'child-component',
    template: `
        <h1>I am a child</h1>
        <div>
            <ul *ngFor="#data in data">
                <li>{{data}}</li>
            </ul>
        </div>
    `
})
export class ChildComponent implements OnInit{
    data: string[] = [];
    constructor(
        private _sharedService: SharedService) { }
    ngOnInit():any {
        this.data = this._sharedService.dataArray;
    }
}

자식 2 (형제)

import {Component} from 'angular2/core';
import {SharedService} from './shared.service'

@Component({
    selector: 'child-sibling-component',
    template: `
        <h1>I am a child</h1>
        <input type="text" [(ngModel)]="data"/>
        <button (click)="addData()"></button>
    `
})
export class ChildSiblingComponent{
    data: string = 'Testing data';
    constructor(
        private _sharedService: SharedService){}
    addData(){
        this._sharedService.insertData(this.data);
        this.data = '';
    }
}

NOW :이 방법을 사용할 때주의해야 할 사항입니다.

  1. 하위가 아닌 PARENT 구성 요소의 공유 서비스에 대한 서비스 제공자 만 포함하십시오.
  2. 여전히 생성자를 포함하고 하위에 서비스를 가져와야합니다.
  3. 이 답변은 원래 초기 Angular 2 베타 버전에서 답변되었습니다. 그러나 변경된 것은 모두 import 문이므로 원래 버전을 우연히 사용한 경우 업데이트해야 할 전부입니다.

답변

두 개의 다른 구성 요소 (내포 된 구성 요소가 아닌 parent \ child \ grandchild)의 경우 다음을 제안합니다.

MissionService :

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';

@Injectable()

export class MissionService {
  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();
  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();
  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }
  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }

}

Astronaut 구성 요소 :

import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';
@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;
  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }
  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }
  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}

출처 : 부모와 자녀는 서비스를 통해 소통합니다.


답변

이를 수행하는 한 가지 방법은 공유 서비스를 사용하는 것 입니다.

그러나 다음 솔루션이 훨씬 간단하다는 것을 알게되어 두 형제간에 데이터를 공유 할 수 있습니다 (나는 Angular 5 에서만 테스트했습니다 )

부모 구성 요소 템플릿에서 :

<!-- Assigns "AppSibling1Component" instance to variable "data" -->
<app-sibling1 #data></app-sibling1>
<!-- Passes the variable "data" to AppSibling2Component instance -->
<app-sibling2 [data]="data"></app-sibling2> 

app-sibling2.component.ts

import { AppSibling1Component } from '../app-sibling1/app-sibling1.component';
...

export class AppSibling2Component {
   ...
   @Input() data: AppSibling1Component;
   ...
}


답변

여기에 그것에 대한 논의가 있습니다.

https://github.com/angular/angular.io/issues/2663

Alex J의 대답은 좋지만 2017 년 7 월 현재 현재 Angular 4에서는 더 이상 작동하지 않습니다.

그리고이 플 런커 링크는 공유 서비스와 관찰 가능을 사용하여 형제들간에 통신하는 방법을 보여줍니다.

https://embed.plnkr.co/P8xCEwSKgcOg07pwDrlO/


답변

지시문은 특정 상황에서 구성 요소를 ‘연결’하는 데 의미가있을 수 있습니다. 실제로 연결되는 사물이 전체 구성 요소 일 필요도 없으며 때로는 더 가볍고 그렇지 않은 경우 실제로 더 간단합니다.

예를 들어 Youtube Player컴포넌트 (Youtube API 래핑)가 있고이를위한 컨트롤러 버튼이 필요했습니다. 버튼이 내 주요 구성 요소의 일부가 아닌 유일한 이유는 DOM의 다른 곳에 위치하기 때문입니다.

이 경우에는 ‘부모’구성 요소에서만 사용할 수있는 ‘확장’구성 요소 일뿐입니다. 나는 ‘부모’라고 말하지만 DOM에서는 형제이므로 원하는대로 부르십시오.

내가 말했듯이 전체 구성 요소가 될 필요조차 없습니다 <button>.

@Directive({
    selector: '[ytPlayerPlayButton]'
})
export class YoutubePlayerPlayButtonDirective {

    _player: YoutubePlayerComponent; 

    @Input('ytPlayerVideo')
    private set player(value: YoutubePlayerComponent) {
       this._player = value;    
    }

    @HostListener('click') click() {
        this._player.play();
    }

   constructor(private elementRef: ElementRef) {
       // the button itself
   }
}

에 대한 HTML 에서 Youtube API를 래핑하는 내 구성 요소는 분명히 ProductPage.component어디에 있습니까 youtube-player?

<youtube-player #technologyVideo videoId='NuU74nesR5A'></youtube-player>

... lots more DOM ...

<button class="play-button"        
        ytPlayerPlayButton
        [ytPlayerVideo]="technologyVideo">Play</button>

지시문은 나를 위해 모든 것을 연결하고 HTML에서 (클릭) 이벤트를 선언 할 필요가 없습니다.

따라서 지시문은 ProductPage중재자 로 참여하지 않고도 비디오 플레이어에 멋지게 연결할 수 있습니다 .

실제로 이것을 한 것은 이번이 처음이므로 훨씬 더 복잡한 상황에서 얼마나 확장 가능한지 아직 확실하지 않습니다. 이를 위해 나는 행복하지만 HTML은 단순하고 모든 것에 대한 책임은 구별됩니다.


답변

여기에 간단한 실제적인 설명은 다음과 같습니다 간단히 설명 여기

call.service.ts에서

import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class CallService {
 private subject = new Subject<any>();

 sendClickCall(message: string) {
    this.subject.next({ text: message });
 }

 getClickCall(): Observable<any> {
    return this.subject.asObservable();
 }
}

버튼이 클릭되었음을 다른 컴포넌트에 알리기 위해 observable을 호출하려는 컴포넌트

import { CallService } from "../../../services/call.service";

export class MarketplaceComponent implements OnInit, OnDestroy {
  constructor(public Util: CallService) {

  }

  buttonClickedToCallObservable() {
   this.Util.sendClickCall('Sending message to another comp that button is clicked');
  }
}

다른 구성 요소를 클릭 한 버튼에 대해 작업을 수행하려는 구성 요소

import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";


ngOnInit() {

 this.subscription = this.Util.getClickCall().subscribe(message => {

 this.message = message;

 console.log('---button clicked at another component---');

 //call you action which need to execute in this component on button clicked

 });

}

import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";


ngOnInit() {

 this.subscription = this.Util.getClickCall().subscribe(message => {

 this.message = message;

 console.log('---button clicked at another component---');

 //call you action which need to execute in this component on button clicked

});

}

http://musttoknow.com/angular-4-angular-5-communicate-two-components-using-observable-subject/ 를 읽고 구성 요소 통신에 대한 이해가 명확합니다 .


답변

공유 서비스는이 문제에 대한 좋은 해결책입니다. 일부 활동 정보도 저장하려면 기본 모듈 (app.module) 공급자 목록에 공유 서비스를 추가 할 수 있습니다.

@NgModule({
    imports: [
        ...
    ],
    bootstrap: [
        AppComponent
    ],
    declarations: [
        AppComponent,
    ],
    providers: [
        SharedService,
        ...
    ]
});

그런 다음 구성 요소에 직접 제공 할 수 있습니다.

constructor(private sharedService: SharedService)

Shared Service를 사용하면 기능을 사용하거나 주제를 생성하여 한 번에 여러 장소를 업데이트 할 수 있습니다.

@Injectable()
export class FolderTagService {
    public clickedItemInformation: Subject<string> = new Subject();
}

목록 구성 요소에서 클릭 한 항목 정보를 게시 할 수 있습니다.

this.sharedService.clikedItemInformation.next("something");

그런 다음 세부 구성 요소에서이 정보를 가져올 수 있습니다.

this.sharedService.clikedItemInformation.subscribe((information) => {
    // do something
});

구성 요소 공유를 나열하는 데이터는 무엇이든 될 수 있습니다. 도움이 되었기를 바랍니다.