จะถ่ายทอดเหตุการณ์จากพ่อแม่สู่ลูกได้อย่างไร?


114

ฉันกำลังใช้ Angular 2 โดยปกติแล้วเราจะใช้@Output() addTab = new EventEmitter<any>();แล้วaddTab.emit()ปล่อยเหตุการณ์ไปยังองค์ประกอบหลัก

มีวิธีใดบ้างที่เราสามารถทำได้รองลงมาจากรุ่นแม่สู่รุ่นลูก?


2
เพียงแค่เปลี่ยนค่าของนิพจน์ที่ส่งผ่านเป็นอินพุตไปยังคอมโพเนนต์ลูกและคอมโพเนนต์ลูกจะได้รับ (และรับแจ้งจาก ngOnChanges) คุณยังสามารถปล่อยเหตุการณ์โดยใช้บริการที่ใช้ร่วมกันที่มี Observable
JB Nizet

คำตอบ:


199

ใช้RxJsคุณสามารถประกาศSubjectในองค์ประกอบที่ผู้ปกครองของคุณและผ่านมันเป็นองค์ประกอบเด็กองค์ประกอบเด็กเพียงแค่ต้องสมัครเป็นสมาชิกนี้ObservableObservable

องค์ประกอบหลัก

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() {
  this.eventsSubject.next();
}

ผู้ปกครอง HTML

<child [events]="eventsSubject.asObservable()"> </child>    

องค์ประกอบเด็ก

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit(){
  this.eventsSubscription = this.events.subscribe(() => doSomething());
}

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}

13
นอกจากนี้ข้อมูลสามารถส่งผ่านไปพร้อมกับเหตุการณ์โดยใช้วิธีนี้ เช่นเดียวกับthis.eventsSubject.next({data});ในผู้ปกครองแล้วthis.events.subscribe(({data}) => doSomething(data));ในเด็ก
วินซ์

2
ฉันได้แก้ไขคำตอบที่ดีนี้เพื่อเพิ่มการยกเลิกการสมัคร +1
Umar Farooq Khawaja

1
อาจเป็นคำถามเริ่มต้นที่นี่: ทำไมต้องเปลี่ยนeventsSubjectเป็น Observable แทนที่จะสมัครเป็น Subject?
Justin Morgan

11
การแปลงเหตุการณ์
BlueNC

2
ขอบคุณสำหรับวิธีแก้ปัญหานี้ได้ผล แต่ฉันได้รับข้อผิดพลาดในคอนโซล: "core.js: 6185 ERROR TypeError: Cannot read property 'subscribe' of undefined" ข้อผิดพลาดชี้ไปที่ events.subscribe () ใน ngOnInit ขององค์ประกอบลูก: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" ฉันใช้เวอร์ชัน: "@ angular / cli": "~ 9.1.0" และ "rxjs" : "~ 6.5.4"
Eduardo

75

เท่าที่ฉันรู้มี 2 วิธีมาตรฐานที่คุณสามารถทำได้

1. @ อินพุท

เมื่อใดก็ตามที่ข้อมูลในพาเรนต์เปลี่ยนแปลงเด็กจะได้รับแจ้งเกี่ยวกับสิ่งนี้ในเมธอด ngOnChanges เด็กสามารถกระทำได้ นี่เป็นวิธีมาตรฐานในการโต้ตอบกับเด็ก

Parent-Component
public inputToChild: Object;

Parent-HTML
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes: { [property: string]: SimpleChange }){
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.
}
  1. แนวคิดการบริการร่วมกัน

การสร้างบริการและการใช้งานที่สังเกตได้ในบริการที่ใช้ร่วมกัน เด็กสมัครรับข้อมูลและเมื่อใดก็ตามที่มีการเปลี่ยนแปลงเด็กจะได้รับการแจ้งเตือน นี่เป็นวิธีที่นิยมเช่นกัน เมื่อคุณต้องการส่งสิ่งอื่นนอกเหนือจากข้อมูลที่คุณส่งผ่านเป็นอินพุตสามารถใช้ได้

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>{

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.})

ฉันลองวิธีแรกและมันก็ใช้ได้ดีจนถึงจุดหนึ่งและหลังจากนั้นฉันก็ได้รับข้อความแสดงข้อผิดพลาดที่ระบุว่าค่าได้เปลี่ยนไปแล้วก่อนหน้านี้มันเป็นเท็จและตอนนี้มันเป็นจริง มันไม่ได้ให้คำอธิบายใด ๆ เพิ่มเติมนอกเหนือจากนี้
code1

2
เนื่องจากคุณหมายถึง ExpressionChangedAfterItHasBeenCheckedError ข้อผิดพลาดนี้จะไม่ปรากฏในโหมดการผลิต
user1337

<child [data]="inputToChild"> </child>น่าจะ<child [data]="(inputToChild)"> </child>ได้รับการเปลี่ยนแปลง
pmc

29

ในองค์ประกอบหลักคุณสามารถใช้ @ViewChild () เพื่อเข้าถึงวิธี / ตัวแปรขององค์ประกอบลูก

@Component({
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
})
export class NumberParentComponent {
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() {
       this.numberComponent.increaseByOne();
    }
    decrease() {
       this.numberComponent.decreaseByOne();
    }
} 

อัปเดต:

Angular 8 เป็นต้นไป -

@ViewChild(NumberComponent, { static: false })

4
ดูเหมือนว่าจะสะอาดกว่าการทำงานกับวัตถุที่สังเกตได้ ข้อเสียเปรียบคือเด็กต้องอยู่ในมุมมอง ถ้าหากเด็กจะเต็มไปด้วยการกำหนดเส้นทางสำหรับตัวอย่างเช่นนี้จะล้มเหลวจะเป็นnumberComponent undefined
Shy Agam

1
วิธีนี้เป็นแนวทางปฏิบัติที่ดีหรือไม่? ฉันเห็นด้วยกับความจริงที่สะอาดกว่าสิ่งที่สังเกตได้ แต่ฉันสงสัยเกี่ยวกับการจัดการตัวแปรของเด็กจากผู้ปกครอง อย่างไรก็ตามมันก็ใช้ได้ดีสำหรับความต้องการของฉันขอบคุณ!
Takatalvi

1
นี่คือเคล็ดลับดีๆ ดูเหมือนว่า @ViewChild จะเปลี่ยนไปใน Angular 8 หรืออย่างน้อยฉันก็ต้องระบุตัวเลือกของ ViewChild ฉันใช้สิ่งนี้ในกรณีของฉัน: @ViewChild (อื่น ๆ , {static: false}) private otherComponent: อื่น ๆ ;
Tore Aurstad

8

ใช้มัณฑนากร @Input () ในคอมโพเนนต์ลูกของคุณเพื่ออนุญาตให้พาเรนต์เชื่อมโยงกับอินพุตนี้

ในองค์ประกอบลูกคุณประกาศว่าเป็น:

@Input() myInputName: myType

ในการผูกคุณสมบัติจากพาเรนต์กับชายน์คุณต้องเพิ่มแบร็กเก็ตการโยงและชื่ออินพุตของคุณระหว่างเทมเพลต

ตัวอย่าง:

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

แต่ระวังอ็อบเจ็กต์จะถูกส่งผ่านเป็นการอ้างอิงดังนั้นหากอ็อบเจ็กต์ได้รับการอัพเดตในเด็ก var ของพาเรนต์จะได้รับการอัปเดตมากเกินไป ซึ่งอาจนำไปสู่พฤติกรรมที่ไม่ต้องการในบางครั้ง ด้วยประเภทหลักค่าจะถูกคัดลอก

หากต้องการอ่านต่อไปนี้:

เอกสาร: https://angular.io/docs/ts/latest/cookbook/component-communication.html


1

ภายในพาเรนต์คุณสามารถอ้างอิงเด็กโดยใช้ @ViewChild เมื่อจำเป็น (เช่นเมื่อเหตุการณ์จะเริ่มทำงาน) คุณสามารถเรียกใช้เมธอดในลูกจากผู้ปกครองโดยใช้การอ้างอิง @ViewChild

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.