कोणीय 2 सिबलिंग घटक संचार


118

मेरे पास एक ListComponent है। जब किसी सूची को ListComponent में क्लिक किया जाता है, तो उस आइटम का विवरण DetailComponent में दिखाया जाना चाहिए। दोनों एक ही समय पर स्क्रीन पर हैं, इसलिए कोई रूटिंग शामिल नहीं है।

मैं कैसे बताऊं कि DetailComponent को ListComponent में किस आइटम पर क्लिक किया गया था?

मैंने माता-पिता (AppComponent) तक एक घटना को उत्सर्जित करने पर विचार किया है, और एक @Input के साथ DetailComponent पर माता-पिता को selectItem.id सेट किया है। या मैं अवलोकन योग्य सदस्यता के साथ एक साझा सेवा का उपयोग कर सकता हूं।


संपादित करें: ईवेंट + @Input के माध्यम से चयनित आइटम को सेट करने से DetailComponent ट्रिगर नहीं होता है, हालांकि, अगर मुझे अतिरिक्त कोड निष्पादित करने की आवश्यकता होती है। तो मुझे यकीन नहीं है कि यह एक स्वीकार्य समाधान है।


लेकिन ये दोनों तरीके उन चीजों को करने के एंगुलर 1 तरीके से कहीं अधिक जटिल हैं जो या तो $ rootScll के माध्यम से थे। $ प्रसारण या $ गुंजाइश। $ माता-पिता। $ प्रसारण।

कोणीय 2 में सब कुछ एक घटक होने के नाते, मुझे आश्चर्य है कि घटक संचार के बारे में अधिक जानकारी नहीं है।

क्या इसे पूरा करने का एक और / सीधा तरीका है?


क्या आपको डेटा साझा करने के लिए कोई तरीका मिला? मुझे यह देखने योग्य है ..
ह्यूमन बीइंग

जवाबों:


65

Rc.4 को अपडेट किया गया: जब कोणीय 2 में सिबलिंग घटकों के बीच डेटा पास करने की कोशिश की जा रही है, तो अभी (angular.rc.4) का सबसे सरल तरीका कोणीय 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;
    }
}

बच्चा 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 = '';
    }
}

अब: इस पद्धति का उपयोग करते समय ध्यान देने योग्य बातें।

  1. केवल घटक घटक में साझा सेवा के लिए सेवा प्रदाता को शामिल करें और बच्चों को नहीं।
  2. आपको अभी भी निर्माणकर्ताओं को शामिल करना है और बच्चों में सेवा का आयात करना है
  3. यह उत्तर मूल रूप से एक प्रारंभिक कोणीय 2 बीटा संस्करण के लिए उत्तर दिया गया था। सभी जो बदल गए हैं हालांकि आयात स्टेटमेंट हैं, इसलिए यदि आपको संयोग से मूल संस्करण का उपयोग करना है, तो आपको केवल अपडेट करने की आवश्यकता है।

2
क्या यह अभी भी कोणीय-आरसी 1 के लिए वैध है?
सर्जियो

4
मुझे विश्वास नहीं है कि यह भाई-बहन को सूचित करता है कि साझा सेवा में कुछ अद्यतन किया गया है। अगर चाइल्ड-कंपोनेंट 1 ऐसा कुछ करता है, जिसका चाइल्ड-कंपोनेंट 2 को जवाब देना है, तो यह तरीका उसे हैंडल नहीं करता है। मेरा मानना ​​है कि इसके आसपास का रास्ता वेधशालाओं के साथ है?
डेनिस.शेपर्ड

1
@ सूफियान: मैं यह अनुमान लगाने जा रहा हूं कि बच्चों के लिए प्रदाता क्षेत्रों को जोड़ने से कोणीय को प्रत्येक के लिए नए निजी उदाहरण बनाने होंगे। जब आप उन्हें नहीं जोड़ते हैं, तो वे माता-पिता के "सिंगलटन" उदाहरण का उपयोग करते हैं।
राल्फ

1
ऐसा लगता है कि नवीनतम अपडेट्स के साथ यह काम नहीं कर रहा है
सूफियान जाबरे

3
यह पुराना है। directivesअब घटक में घोषित नहीं किए जाते हैं।
नैट गार्डनर

26

2 अलग-अलग घटकों (नेस्टेड घटकों, माता-पिता के बच्चे / पोते) के मामले में मैं आपको यह सुझाव देता हूं:

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

}

AstronautComponent:

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
आप इस उत्तर में कुछ शब्दावली जोड़ना चाहेंगे। मेरा मानना ​​है कि यह RxJS, ऑब्जर्वेबल पैटर्न, आदि के अनुरूप है। पूरी तरह से निश्चित नहीं है, लेकिन इसमें से कुछ का विवरण जोड़ना लोगों के लिए फायदेमंद होगा (जैसे खुद)।
कर्न्स

13

ऐसा करने का एक तरीका साझा सेवा का उपयोग करना है ।

हालाँकि, मुझे निम्न समाधान बहुत सरल लगता है, यह 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> 

एप्लिकेशन-sibling2.component.ts

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

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

क्या यह ढीले युग्मन और इस प्रकार के घटकों के विचार का विरोध नहीं कर रहा है?
रॉबिन

किसी को भी कोई भी विचार है कि क्या यह एक साफ या गंदा तरीका है? यह एक दिशा में डेटा साझा करने के लिए बहुत सरल लगता है, उदाहरण के लिए केवल sibiling1 से sibiling2 तक, लेकिन अन्य तरीके से नहीं
सारा

9

इसे लेकर यहां चर्चा चल रही है।

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

एलेक्स जे का उत्तर अच्छा है, लेकिन यह जुलाई, 2017 तक वर्तमान कोणीय 4 के साथ काम नहीं करता है।

और यह प्लंकर लिंक साझा सेवा और अवलोकन का उपयोग करके भाई-बहनों के बीच संवाद करने का तरीका प्रदर्शित करेगा।

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


7

एक निर्देश कुछ स्थितियों में 'कनेक्ट' घटकों को समझ सकता है। वास्तव में जुड़ी हुई चीजों को पूर्ण घटकों की आवश्यकता नहीं होती है, और कभी-कभी यह अधिक हल्के होते हैं और वास्तव में सरल होते हैं यदि वे नहीं हैं।

उदाहरण के लिए मुझे एक Youtube Playerघटक मिला है (Youtube API को रैप करना) और मुझे इसके लिए कुछ नियंत्रक बटन चाहिए थे। एकमात्र कारण यह है कि बटन मेरे मुख्य घटक का हिस्सा नहीं हैं, वे डोम में कहीं और स्थित हैं।

इस मामले में यह वास्तव में सिर्फ एक 'एक्सटेंशन' घटक है जो केवल 'मूल' घटक के साथ ही उपयोग किया जाएगा। मैं 'माता-पिता' कहता हूं, लेकिन 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 के लिए ProductPage.component, जहाँ youtube-playerजाहिर तौर पर मेरा घटक है जो Youtube API को लपेटता है।

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

... lots more DOM ...

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

निर्देश मेरे लिए सब कुछ हुक करता है, और मुझे HTML में (क्लिक) घटना की घोषणा करने की आवश्यकता नहीं है।

तो निर्देश ProductPageएक मध्यस्थ के रूप में शामिल किए बिना वीडियो प्लेयर से अच्छी तरह से जुड़ सकता है ।

यह पहली बार है जब मैंने वास्तव में ऐसा किया है, इसलिए अभी तक निश्चित नहीं है कि यह और अधिक जटिल स्थितियों के लिए कितना स्केलेबल हो सकता है। हालांकि इसके लिए मैं खुश हूं और यह मेरे HTML को सरल और हर चीज की जिम्मेदारियों को छोड़ देता है।


समझने के लिए सबसे महत्वपूर्ण कोणीय अवधारणाओं में से एक यह है कि एक घटक केवल एक टेम्पलेट के साथ एक निर्देश है। एक बार जब आप वास्तव में इसकी सराहना करते हैं तो इसका मतलब है कि निर्देश काफी डरावना नहीं हैं - और आप महसूस करेंगे कि आप इसे व्यवहार में संलग्न करने के लिए किसी भी तत्व पर लागू कर सकते हैं।
सिमोन_विवर

मैंने यह कोशिश की है, लेकिन मुझे इसके बराबर डुप्लिकेट पहचानकर्ता त्रुटि मिलती है player। अगर मैं खिलाड़ी का पहला उल्लेख छोड़ देता हूं तो मुझे एक रेंज मिलती है। मुझे लगता है कि यह कैसे काम करने वाला है।
कैथरीन ओसबोर्न

@KatharineOsborne मेरे वास्तविक कोड में दिखता है जिसका उपयोग मैं _playerउस निजी क्षेत्र के लिए करता हूं जो खिलाड़ी का प्रतिनिधित्व करता है, इसलिए यदि आप इसे बिल्कुल कॉपी करते हैं तो आपको एक त्रुटि मिलेगी। अपडेट करूंगी। माफ़ करना!
साइमन_वेर

4

यहाँ सरल व्यावहारिक व्याख्या है: बस यहाँ समझाया गया है

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-compenders-using-observable-subject/


हे सरल समाधान के लिए बहुत बहुत धन्यवाद> मैंने इसे स्टैकब्लिट्ज़ में आज़माया जो ठीक काम करता था। लेकिन मेरे आवेदन में आलसी लोड किए गए मार्ग हैं (प्रदान किया है: 'रूट') और HTTP कॉल सेट और प्राप्त करने के लिए। क्या आप मुझे HTTP कॉल के साथ मदद कर सकते हैं बहुत कोशिश की लेकिन काम नहीं कर रहा है:
क्षत्री

4

साझा सेवा इस समस्या का एक अच्छा समाधान है। यदि आप कुछ गतिविधि जानकारी भी संग्रहीत करना चाहते हैं, तो आप अपने मुख्य मॉड्यूल (app.module) प्रदाता सूची में साझा सेवा जोड़ सकते हैं।

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

फिर आप इसे सीधे अपने घटकों को प्रदान कर सकते हैं,

constructor(private sharedService: SharedService)

साझा सेवा के साथ आप या तो फ़ंक्शंस का उपयोग कर सकते हैं या आप एक साथ कई स्थानों को अपडेट करने के लिए एक विषय बना सकते हैं।

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

अपनी सूची घटक में आप क्लिक की गई वस्तु जानकारी प्रकाशित कर सकते हैं,

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

और फिर आप इस जानकारी को अपने विवरण घटक पर ला सकते हैं:

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

जाहिर है, घटक शेयरों को सूचीबद्ध करने वाला डेटा कुछ भी हो सकता है। उम्मीद है की यह मदद करेगा।


यह साझा सेवा की इस अवधारणा का सबसे सीधा (उर्फ ट्रिक) उदाहरण है, और इसकी दृश्यता बढ़ाने के लिए वास्तव में upvoted होना चाहिए, क्योंकि कोई स्वीकृत जवाब नहीं है।
इंजाजा

3

आपको अपने घटकों के बीच अभिभावक-बच्चे के संबंध स्थापित करने की आवश्यकता है। समस्या यह है कि आप मूल घटक के निर्माण में बच्चे के घटकों को इंजेक्ट कर सकते हैं और इसे स्थानीय चर में संग्रहीत कर सकते हैं। इसके बजाय, आपको @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/


2

व्यवहार विषय। मैंने उस बारे में एक ब्लॉग लिखा था ।

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

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

इस उदाहरण में मैं टाइप नंबर के एक स्पष्ट व्यवहार विषय की घोषणा कर रहा हूं। साथ ही यह एक अवलोकन योग्य है। और अगर "कुछ ख़ुशी" यह नए () {} फ़ंक्शन के साथ बदल जाएगा।

तो, भाई के घटकों में, एक फ़ंक्शन को कॉल करेगा, परिवर्तन करने के लिए, और दूसरा उस परिवर्तन से प्रभावित होगा, या इसके विपरीत।

उदाहरण के लिए, मैं URL से आईडी प्राप्त करता हूं और व्यवहार विषय से अपडेट को अपडेट करता हूं।

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

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

और दूसरी तरफ से, मैं पूछ सकता हूं कि क्या वह आईडी "क्या कभी मैं चाहता हूं" और उसके बाद एक विकल्प बना सकता हूं, मेरे मामले में अगर मैं एक काम करना चाहता हूं, और वह कार्य वर्तमान यूआरएल है, तो मुझे मुझे पुनर्निर्देशित करना होगा घर के लिए:

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

1

यह वह नहीं है जो आप वास्तव में चाहते हैं, लेकिन निश्चित रूप से आपकी मदद करेंगे

मुझे आश्चर्य है कि वहाँ घटक संचार के बारे में अधिक जानकारी नहीं है <=> 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 द्वारा इस ट्यूटोरियल पर विचार करें


0

मैं एक बंधन के माध्यम से माता-पिता से अपने बच्चों में से एक के लिए सेटर विधियों को पारित कर रहा हूं, उस घटक को बाल घटक से डेटा के साथ बुला रहा हूं, जिसका अर्थ है कि मूल घटक अपडेट किया गया है और फिर नए डेटा के साथ अपने दूसरे बच्चे के घटक को अपडेट कर सकता है। हालांकि यह 'बंधन' या एक तीर समारोह का उपयोग कर बंधन की आवश्यकता है।

इसका यह लाभ है कि बच्चों को एक-दूसरे से इतना जोड़ा नहीं जाता है क्योंकि उन्हें एक विशिष्ट साझा सेवा की आवश्यकता नहीं होती है।

मुझे पूरी तरह से यकीन नहीं है कि यह सबसे अच्छा अभ्यास है, इस पर दूसरों के विचारों को सुनना दिलचस्प होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.