جافاسكريبت - التواصل بين المكونات الشقيقة في أنجولار 2


لدي مكون قائمة. عند النقر على عنصر في مكون القائمة، يجب عرض تفاصيل ذلك العنصر في مكون التفاصيل. كلاهما على الشاشة في نفس الوقت، لذلك لا يوجد تحويل مسار متضمن.

كيف أخبر DetailComponent بأي عنصر في ListComponent تم النقر عليه؟

لقد فكرت في إطلاق حدث إلى العنصر الأب (AppComponent)، وجعل الأب يحدد selectedItem.id على DetailComponent باستخدام @Input. أو يمكنني استخدام خدمة مشتركة مع اشتراكات قابلة للمراقبة.

تعديل: تعيين العنصر المحدد عبر الحدث + @Input لا يؤدي إلى تشغيل DetailComponent، على الرغم من ذلك، في حال احتجت إلى تنفيذ كود إضافي. لذلك لست متأكدًا ما إذا كان هذا حلًا مقبولًا.

لكن كل من هاتين الطريقتين يبدو أنهما أكثر تعقيدًا بكثير من طريقة Angular 1 في القيام بالأشياء والتي كانت إما من خلال $rootScope.$broadcast أو $scope.$parent.$broadcast.

مع كون كل شيء في Angular 2 مكونًا، فأنا متفاجئ من عدم وجود مزيد من المعلومات حول تواصل المكونات.

هل هناك طريقة أخرى/أبسط لتحقيق ذلك؟

الإجابة رقم 1

تم التحديث إلى 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);
    }
}

الآن، سيكون هذا هو مكون الأب

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. تم الإجابة على هذا السؤال في الأصل لإصدار تجريبي مبكر من Angular 2. كل ما تغير هو عبارات الاستيراد، لذا هذا كل ما تحتاج إلى تحديثه إذا كنت قد استخدمت الإصدار الأصلي بالصدفة.

الإجابة رقم 2

في حالة وجود مكونين مختلفين (ليست مكونات متداخلة، أب\ابن\حفيد) أقترح عليك هذا:

خدمة المهمة:

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

المصدر: يتواصل الأهل والأطفال عبر خدمة

الإجابة رقم 3

إحدى الطرق للقيام بذلك هي استخدام خدمة مشتركة.

ومع ذلك أجد الحل التالي أبسط بكثير، فهو يسمح بمشاركة البيانات بين شقيقين. (لقد اختبرت ذلك فقط على 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;
   ...
}