Wie kann ein Ereignis von einem Elternteil an ein Kind gesendet werden?

111

Ich verwende derzeit Angular 2. Normalerweise verwenden wir @Output() addTab = new EventEmitter<any>();und addTab.emit()senden dann ein Ereignis an die übergeordnete Komponente.

Gibt es eine Möglichkeit, dies von Eltern zu Kind zu tun?

Code1
quelle
2
Ändern Sie einfach den Wert eines Ausdrucks, der als Eingabe an die untergeordnete Komponente übergeben wird, und die untergeordnete Komponente erhält ihn (und wird von ngOnChanges informiert). Sie können ein Ereignis auch mithilfe eines gemeinsam genutzten Dienstes mit einem Observable ausgeben.
JB Nizet

Antworten:

191

Mit RxJs können Sie a Subjectin Ihrer übergeordneten Komponente deklarieren und als untergeordnete Komponente übergeben. Die untergeordneteObservable Komponente muss diese nur abonnieren Observable.

Übergeordnete Komponente

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

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

Eltern-HTML

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

Untergeordnete Komponente

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

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

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
quelle
13
Mit diesem Ansatz können auch Daten zusammen mit dem Ereignis übergeben werden. Wie this.eventsSubject.next({data});beim Elternteil, dann this.events.subscribe(({data}) => doSomething(data));beim Kind.
Vince
2
Ich habe diese großartige Antwort bearbeitet, um das Abbestellen hinzuzufügen. +1
Umar Farooq Khawaja
1
Wahrscheinlich eine Anfängerfrage hier: Warum eventsSubjectzu einem Observable konvertieren , anstatt es nur als Betreff zu abonnieren?
Justin Morgan
11
Wenn Sie eventsSubject in ein Observable konvertieren, verhindern Sie, dass die untergeordnete Komponente next () aufruft.
BlueNC
2
Vielen Dank für diese Lösung, ES FUNKTIONIERT, aber in der Konsole wird ein Fehler angezeigt: "core.js: 6185 ERROR TypeError: Die Eigenschaft 'subscribe' von undefined kann nicht gelesen werden." Der Fehler zeigt auf die events.subscribe () in ngOnInit der untergeordneten Komponente: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Ich verwende die Versionen: "@ angle / cli": "~ 9.1.0" und "rxjs" : "~ 6.5.4"
Eduardo
71

Soweit ich weiß, gibt es zwei Standardmethoden, wie Sie dies tun können.

1. @Input

Immer wenn sich die Daten im übergeordneten Element ändern, wird das untergeordnete Element in der ngOnChanges-Methode darüber benachrichtigt. Das Kind kann darauf einwirken. Dies ist die Standardmethode für die Interaktion mit einem Kind.

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. Shared-Service-Konzept

Erstellen eines Dienstes und Verwenden eines Observable im gemeinsam genutzten Dienst. Das Kind abonniert es und bei jeder Änderung wird das Kind benachrichtigt. Dies ist auch eine beliebte Methode. Wenn Sie etwas anderes als die Daten senden möchten, die Sie als Eingabe übergeben, kann dies verwendet werden.

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.})
Prajwal Gonsalves
quelle
Ich habe die erste Methode ausprobiert und sie hat bis zu einem gewissen Punkt einwandfrei funktioniert. Danach wurde die Fehlermeldung angezeigt, dass der Wert jetzt geändert wurde. Früher war er falsch und jetzt ist er wahr. Abgesehen davon gab es mir keine weitere Erklärung.
Code1
2
Wenn Sie einen ExpressionChangedAfterItHasBeenCheckedError meinen, wird dieser Fehler nicht im Produktionsmodus ausgelöst.
user1337
<child [data]="inputToChild"> </child>sollte wahrscheinlich sein <child [data]="(inputToChild)"> </child>, um Änderungen zu bekommen
pmc
28

In einer übergeordneten Komponente können Sie mit @ViewChild () auf die Methode / Variable der untergeordneten Komponente zugreifen.

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

Aktualisieren:

Winkel 8 ab -

@ViewChild(NumberComponent, { static: false })
Hardik Patel
quelle
4
Dies scheint sauberer zu sein als die Arbeit mit Observablen. Nachteil ist, dass Kind im Blick sein muss. Wenn das Kind mit Routing zum Beispiel geladen wird, wird dies nicht gelingen , wie numberComponentsein wird undefined.
Schüchterner Agam
1
Ist das eine gute Praxis? Ich stimme der Tatsache zu, dass dies sauberer ist als Observable, aber ich bezweifle, dass die Variablen des Kindes vom Elternteil manipuliert werden. Wie auch immer, es hat so gut für meine Bedürfnisse funktioniert, danke!
Takatalvi
1
Dies ist ein guter Tipp. Es sieht so aus, als ob @ViewChild in Angular 8 geändert wurde, oder zumindest musste ich Optionen für ViewChild angeben. Ich habe dies in meinem Fall verwendet: @ViewChild (Other, {static: false}) private otherComponent: Other;
Tore Aurstad
8

Verwenden Sie den Dekorator @Input () in Ihrer untergeordneten Komponente, damit das übergeordnete Element an diese Eingabe binden kann.

In der untergeordneten Komponente deklarieren Sie sie wie folgt:

@Input() myInputName: myType

Um eine Eigenschaft vom übergeordneten Element an ein untergeordnetes Element zu binden, müssen Sie in Ihrer Vorlage die Bindungsklammern und den Namen Ihrer Eingabe zwischen ihnen hinzufügen.

Beispiel:

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

Beachten Sie jedoch, dass Objekte als Referenz übergeben werden. Wenn das Objekt im untergeordneten Objekt aktualisiert wird, wird die Variable des Elternteils zu aktualisiert. Dies kann irgendwann zu unerwünschtem Verhalten führen. Bei Primärtypen wird der Wert kopiert.

Um weiter zu gehen, lesen Sie Folgendes:

Dokumente: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Noki
quelle
1

Innerhalb des übergeordneten Elements können Sie mit @ViewChild auf das untergeordnete Element verweisen. Bei Bedarf (dh wenn das Ereignis ausgelöst wird) können Sie einfach eine Methode im untergeordneten Element vom übergeordneten Element mithilfe der @ ViewChild-Referenz ausführen.

Paul Evans
quelle