programing

Angular 2 형제 구성 요소 통신

testmans 2023. 3. 26. 10:00
반응형

Angular 2 형제 구성 요소 통신

List Component가 있습니다.List Component에서 항목을 클릭하면 해당 항목의 세부 정보가 Detail Component에 표시됩니다.둘 다 동시에 화면에 표시되므로 라우팅은 필요하지 않습니다.

List Component에서 어떤 항목을 클릭했는지 Detail Component에 어떻게 알 수 있습니까?

부모(App Component)까지 이벤트를 송신하는 것을 검토하고 부모에게 @Input을 사용하여 Detail Component의 selectedItem.id을 설정하도록 했습니다.또는 관찰 가능한 구독을 가진 공유 서비스를 사용할 수도 있습니다.


편집: 이벤트 + @Input을 통해 선택한 항목을 설정해도 추가 코드를 실행해야 할 경우 Detail Component가 트리거되지 않습니다.그래서 나는 이것이 받아들일 수 있는 해결책인지 잘 모르겠다.


그러나 이 두 가지 방법 모두 $rootScope를 통한 Angular 1의 작업 방식보다 훨씬 복잡해 보입니다.$190 또는 $190.$parent.$140입니다.

Angular 2의 모든 것이 컴포넌트이기 때문에 컴포넌트 통신에 관한 정보가 부족하다는 것이 놀랍습니다.

이를 실현하는 다른/좀 더 간단한 방법이 있습니까?

rc.4로 업데이트: angular 2의 형제 컴포넌트 간에 데이터를 전달하려고 할 때 가장 간단한 방법(angular.rc.4)은 angular 2의 계층적 종속성 주입을 이용하여 공유 서비스를 만드는 것입니다.

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

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

child 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. 공유 서비스에 대한 서비스 제공자만 상위 구성 요소에 포함시키고 하위 구성 요소는 포함하지 마십시오.
  2. 그래도 생성자를 포함하고 하위 항목에 서비스를 가져와야 합니다.
  3. 이 답변은 원래 초기 Angle 2 베타 버전에 대한 답변이었습니다.변경된 것은 Import 스테이트먼트뿐이므로 원래 버전을 사용한 경우 업데이트만 하면 됩니다.

2개의 다른 컴포넌트(네스트된 컴포넌트가 아닌 parent\child\handchild)의 경우 다음을 권장합니다.

미션 서비스:

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

}

우주 비행사 컴포넌트:

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

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

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

다만, 이하의 솔루션은, 2명의 형제자매간에 데이터를 공유할 수 있는 것이 보다 간단하다고 생각합니다.( 테스트는 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-syslog2.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에서는 더 이상 작동하지 않습니다.

그리고 이 plunker 링크는 공유 서비스와 관찰 가능한 서비스를 사용하여 형제자매 간에 통신하는 방법을 보여줍니다.

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

특정 상황에서는 컴포넌트를 '연결'하도록 지시하는 것이 타당할 수 있습니다.실제로 접속되어 있는 것이 완전한 컴포넌트일 필요는 없습니다.또, 접속되어 있지 않으면, 보다 가볍고 심플한 경우도 있습니다.

를 들어, 나는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.Youtube PlayerYoutube API 와와와와와와와와 apiy 。버튼이 주요 컴포넌트의 일부가 아닌 유일한 이유는 버튼이 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
   }
}

for 'HTML'에서ProductPage.component서, snowledge.youtube-playerYoutube API입니다.

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

... lots more DOM ...

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

디렉티브에 의해 모든 것이 연결되므로 HTML에서 (클릭) 이벤트를 선언할 필요가 없습니다.

는 비디오 할 수 .ProductPage중재자로서

이것은 실제로 처음 하는 일이기 때문에, 훨씬 더 복잡한 상황에서 얼마나 확장할 수 있을지는 아직 확실하지 않습니다.이 때문에 저는 기쁘고 HTML의 심플함과 모든 것에 대한 책임이 남습니다.

공유 서비스는 이 문제에 대한 좋은 해결책입니다.액티비티 정보도 저장할 경우 메인 모듈(app.module) 공급자목록에 Shared Service를 추가할 수 있습니다.

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

그런 다음 컴포넌트에 직접 제공할 수 있습니다.

constructor(private sharedService: SharedService)
 

공유 서비스를 사용하면 기능을 사용하거나 제목을 만들어 여러 위치를 한 번에 업데이트할 수 있습니다.

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

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

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

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

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

컴포넌트 공유를 나열하는 데이터는 당연히 모든 것이 될 수 있습니다.이게 도움이 됐으면 좋겠다.

구성 요소 간에 부모-자녀 관계를 설정해야 합니다.문제는 상위 구성 요소의 생성자에 하위 구성 요소를 주입하고 로컬 변수에 저장할 수 있다는 것입니다.부모 는 부모 컴포넌트를 사용하여 선언해야 합니다.@ViewChild .부모 컴포넌트는 다음과 같습니다.

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ListComponent } from './list.component';
import { DetailComponent } from './detail.component';

@Component({
  selector: 'app-component',
  template: '<list-component></list-component><detail-component></detail-component>',
  directives: [ListComponent, DetailComponent]
})
class AppComponent implements AfterViewInit {
  @ViewChild(ListComponent) listComponent:ListComponent;
  @ViewChild(DetailComponent) detailComponent: DetailComponent;

  ngAfterViewInit() {
    // afther this point the children are set, so you can use them
    this.detailComponent.doSomething();
  }
}

https://angular.io/docs/ts/latest/api/core/index/ViewChild-var.html

https://angular.io/docs/ts/latest/cookbook/component-communication.html#parent-to-view-child

컴포넌트의 할 수 컴포넌트 뒤에 .ngAfterViewInit라이프 사이클 훅이 호출됩니다.하세요.AfterViewInit는, 「」와 됩니다.OnInit.

단, 다음 블로그 노트에서 설명한 바와 같이 다른 자산 신고자가 있습니다.http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/

다음은 간단한 실용적인 설명입니다.여기에 간단히 설명하겠습니다.

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

버튼이 클릭되었음을 다른 컴포넌트에 알리기 위해 관찰 가능한 컴포넌트를 호출하는 컴포넌트

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/ 를 참조해 주세요.

이것은 당신이 원하는 것은 아니지만 확실히 당신을 도울 것이다.

컴포넌트 통신에 관한 정보아직 없는 것이 놀랍습니다.<=> angualr2튜토리얼을 참조주십시오.

형제 컴포넌트 통신에 대해서는 다음과 같이 하는 것이 좋습니다.sharedService다른 옵션도 있습니다.

import {Component,bind} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import {NameService} from 'src/nameService';


import {TheContent} from 'src/content';
import {Navbar} from 'src/nav';


@Component({
  selector: 'app',
  directives: [TheContent,Navbar],
  providers: [NameService],
  template: '<navbar></navbar><thecontent></thecontent>'
})


export class App {
  constructor() {
    console.log('App started');
  }
}

bootstrap(App,[]);

자세한 코드는 상단에 있는 링크를 참조하십시오.

편집: 이것은 매우 작은 데모입니다.당신은 이미 당신이 이미 시도했다고 언급했습니다.sharedService자세한 내용은 angualr2튜토리얼을 참조해 주십시오.

행동 대상.나는 그것에 대해 블로그를 썼다.

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
private noId = new BehaviorSubject<number>(0); 
  defaultId = this.noId.asObservable();

newId(urlId) {
 this.noId.next(urlId); 
 }

이 예에서는 타입 번호의 noid 동작 서브젝트를 선언하고 있습니다.또한 그것은 관찰할 수 있다.또한 "something"이 발생하면 new(){} 함수로 변경됩니다.

따라서 형제자매의 컴포넌트에서 한 명은 함수를 호출하여 변경을 가하고 다른 한 명은 해당 변경의 영향을 받습니다.또는 그 반대의 경우도 마찬가지입니다.

예를 들어 URL에서 ID를 가져와 동작 주체에서 noid를 업데이트합니다.

public getId () {
  const id = +this.route.snapshot.paramMap.get('id'); 
  return id; 
}

ngOnInit(): void { 
 const id = +this.getId ();
 this.taskService.newId(id) 
}

그리고 반대편에서 그 ID가 "what anyver"인지 물어보고 그 후에 선택할 수 있습니다. 내 경우 작업을 자세히 설명하려면 해당 작업이 현재 URL이면 홈으로 리디렉션해야 합니다.

delete(task: Task): void { 
  //we save the id , cuz after the delete function, we  gonna lose it 
  const oldId = task.id; 
  this.taskService.deleteTask(task) 
      .subscribe(task => { //we call the defaultId function from task.service.
        this.taskService.defaultId //here we are subscribed to the urlId, which give us the id from the view task 
                 .subscribe(urlId => {
            this.urlId = urlId ;
                  if (oldId == urlId ) { 
                // Location.call('/home'); 
                this.router.navigate(['/home']); 
              } 
          }) 
    }) 
}

형제간 통신을 하는 깔끔한 방법 중 하나는 한 아이에게는 @Output 데코레이터를 사용하고 다른 아이에게는 템플릿 참조 변수를 사용하여 부모가 아이의 메서드를 호출하도록 하는 것입니다.이것은 자녀와 부모의 커뮤니케이션에 @Ouput을 사용하는 것과 매우 유사합니다.

하고있다this.emitSomething.emit(something);child-2가 트리거합니다.onEmitSomething()1세 때.

child-1.component.ts

onEmitSomething(event: any): void {
  // do something
}

아이-2.component.ts

@Output() emitSomething: EventEmitter<any> = new EventEmitter<any>();

parent.component.displaces

<child-1 #child1></child-1>
<child-2 (emitSomething)="child1.onEmitSomething($event)"></child-2>

두 가지 다른 성분 상호 작용 방법은 각도 - 성분 상호 작용에서 찾을 수 있습니다.

바인딩을 통해 세터 메서드를 부모 컴포넌트에서 자식 컴포넌트 중 하나로 전달하고 있습니다.즉, 부모 컴포넌트가 갱신되어 두 번째 자녀 컴포넌트를 새로운 데이터로 갱신할 수 있습니다.단, 'this'를 바인딩하거나 화살표 기능을 사용해야 합니다.

이것은 아이들이 특별한 공유 서비스를 필요로 하지 않기 때문에 서로 결합하지 않는다는 장점이 있다.

이것이 베스트 프랙티스인지 아닌지는 잘 모르겠습니다만, 이것에 관한 다른 견해를 듣는 것은 흥미로울 것입니다.

또한 입력과 출력을 통한 부모 컴포넌트를 통한 2남매 간의 커뮤니케이션도 좋아합니다.OnPush 변경 통지는 일반 서비스를 사용하는 것보다 더 잘 처리됩니다.아니면 NgRx 스토어를 이용하세요.

예.

@Component({
    selector: 'parent',
    template: `<div><notes-grid 
            [Notes]="(NotesList$ | async)"
            (selectedNote)="ReceiveSelectedNote($event)"
        </notes-grid>
        <note-edit 
            [gridSelectedNote]="(SelectedNote$ | async)"
        </note-edit></div>`,
    styleUrls: ['./parent.component.scss']
})
export class ParentComponent {

    // create empty observable
    NotesList$: Observable<Note[]> = of<Note[]>([]);
    SelectedNote$: Observable<Note> = of<Note>();

    //passed from note-grid for selected note to edit.
    ReceiveSelectedNote(selectedNote: Note) {
    if (selectedNote !== null) {
        // change value direct subscribers or async pipe subscribers will get new value.
        this.SelectedNote$ = of<Note>(selectedNote);
    }
    }
    //used in subscribe next() to http call response.  Left out all that code for brevity.  This just shows how observable is populated.
    onNextData(n: Note[]): void {
    // Assign to Obeservable direct subscribers or async pipe subscribers will get new value.
    this.NotesList$ = of<Note[]>(n.NoteList);  //json from server
    }
}

//child 1 sibling
@Component({
  selector: 'note-edit',
  templateUrl: './note-edit.component.html', // just a textarea for noteText and submit and cancel buttons.
  styleUrls: ['./note-edit.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NoteEditComponent implements OnChanges {
  @Input() gridSelectedNote: Note;

    constructor() {
    }

// used to capture @Input changes for new gridSelectedNote input
ngOnChanges(changes: SimpleChanges) {
     if (changes.gridSelectedNote && changes.gridSelectedNote.currentValue !== null) {      
      this.noteText = changes.gridSelectedNote.currentValue.noteText;
      this.noteCreateDtm = changes.gridSelectedNote.currentValue.noteCreateDtm;
      this.noteAuthorName = changes.gridSelectedNote.currentValue.noteAuthorName;
      }
  }

}

//child 2 sibling

@Component({
    selector: 'notes-grid',
    templateUrl: './notes-grid.component.html',  //just an html table with notetext, author, date
    styleUrls: ['./notes-grid.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotesGridComponent {

// the not currently selected fromt eh grid.
    CurrentSelectedNoteData: Note;

    // list for grid
    @Input() Notes: Note[];

    // selected note of grid sent out to the parent to send to sibling.
    @Output() readonly selectedNote: EventEmitter<Note> = new EventEmitter<Note>();

    constructor() {
    }

    // use when you need to send out the selected note to note-edit via parent using output-> input .
    EmitSelectedNote(){
    this.selectedNote.emit(this.CurrentSelectedNoteData);
    }

}


// here just so you can see what it looks like.

export interface Note {
    noteText: string;
    noteCreateDtm: string;
    noteAuthorName: string;
}

언급URL : https://stackoverflow.com/questions/35884451/angular-2-sibling-component-communication

반응형