Aktualisieren Sie die Eigenschaft der übergeordneten Komponente von der untergeordneten Komponente in Angular 2

79

Ich verwende @input, um eine Eigenschaft von der übergeordneten Komponente zu erhalten, um eine CSS-Klasse in einem Element der untergeordneten Komponente zu aktivieren.

Ich kann die Eigenschaft vom Elternteil erhalten und auch die Klasse aktivieren. Das funktioniert aber nur einmal. Die Eigenschaft, die ich vom übergeordneten Element erhalte, sind boolesche Daten, die eingegeben werden. Wenn ich den Status falsevon der untergeordneten Komponente auf setze , ändert sich dies nicht im übergeordneten Element.

Plunkr: https://plnkr.co/edit/58xuZ1uzvToPhPtOING2?p=preview

app.ts

import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { HeaderComponent } from './header';
import { SearchComponent } from './header/search';

@Component({
  selector: 'my-app',
  template: `
    <app-header></app-header>
  `,
})
export class App {
  name:string;
  constructor() {
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, HeaderComponent, SearchComponent ],
  bootstrap: [ App ]
})
export class AppModule {}

header.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-header',
  template: `<header>
              <app-search [getSearchStatus]="isSearchActive"></app-search>
              <button (click)="handleSearch()">Open Search</button>
            </header>`
})
export class HeaderComponent implements OnInit {
  isSearchActive = false;

  handleSearch() {
    this.isSearchActive = true
    console.log(this.isSearchActive)
  }

  constructor() { }
  ngOnInit() { }
}

header / search.ts

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-search',
  template: `<div id="search" [class.toggled]="getSearchStatus">
              search 
              <button  (click)="getSearchStatus = false" class="close">Close Search</button>
            </div>`
})
export class SearchComponent implements OnInit {
  @Input() getSearchStatus: boolean;

  constructor() { }

  ngOnInit() {

  }
}

Bitte überprüfen Sie den oben angegebenen Plunker. Die offene Suchfunktion funktioniert nur einmal. Nach dem Schließen der Suche wird sie nicht erneut ausgelöst.

Ist @inputder richtige Anwendungsfall für dieses Szenario? Bitte helfen Sie mir, dies zu beheben. (Bitte aktualisieren Sie den Plunker).

Körper
quelle
Wo setzen Sie `this.isSearchActive = false;`, nachdem es auf truein gesetzt wurde handleSearch()?
Günter Zöchbauer
in header / search.ts '<button (click) = "getSearchStatus = false" class = "close">'
Body

Antworten:

135

Sie müssen die bidirektionale Datenbindung verwenden.

@Input()ist eine Möglichkeit zur Datenbindung. Um die bidirektionale Datenbindung zu aktivieren, müssen Sie @Output()der Eigenschaft ein entsprechendes Suffix "Ändern" hinzufügen

@Input() getSearchStatus: boolean;
@Output() getSearchStatusChange = new EventEmitter<boolean>();

Wenn Sie die an Ihrer Immobilie vorgenommenen Änderungen für das übergeordnete Element veröffentlichen möchten, müssen Sie das übergeordnete Element benachrichtigen mit:

this.getSearchStatusChange.emit(newValue)

und im übergeordneten Element müssen Sie die Banana-in-a-Box-Notation für diese Eigenschaft verwenden:

[(getSearchStatus)]="myBoundProperty"

Sie können auch an die Eigenschaft binden und einen Rückruf auslösen, wenn sich das untergeordnete Element ändert:

[getSearchStatus]="myBoundProperty" (getSearchStatusChange)="myCrazyCallback($event)"

siehe die plnkr

n00dl3
quelle
Danke für Ihre Antwort. Kannst du bitte meinen Plunker aktualisieren?
Körper
1
Dadurch wird zusätzliche Arbeit für das gleiche Verhalten wie bei der Verwendung hinzugefügt @Output(). Das klingt nach einer Neueinstellung des Rades. @ RobertVangor
n00dl3
2
Bei der Verwendung von ReactiveForms publickonnte ich den "Banana Box" -Ansatz nicht verwenden , obwohl mein untergeordnetes Objekt markiert war. Es wurden Fehler angezeigt, dass es sich nicht um eine bekannte Eigenschaft der Komponente handelte. Die Bindung nur an das (propertyChanged)="setParentProp($event)"funktionierte jedoch perfekt.
mc01
1
Ich kann Ihre Frage nicht beantworten, da sie einen eigenen Beitrag verdient. Ihr Problem ist, dass Sie Änderungen direkt nach dem Änderungserkennungszyklus der Eltern ausgeben. Mit anderen Worten, Sie haben es geschafft, Änderungen zu erkennen, um einige Änderungen auszulösen, die Angular nicht zulässt. @manoj
n00dl3
2
@Chandrakant Nein, um die Bananenbox-Syntax zu verwenden, müssen Sie das Änderungssuffix verwenden. Für benutzerdefinierte Ausgaben können Sie sie beliebig benennen. Bananenschachtel ist nur ein sehr dummer
Syntaxzucker,
7

Ein anderer Ansatz: Verwenden Sie rxjs / BehaviorSubject, um den Status zwischen verschiedenen Komponenten zu übergeben.
Hier ist der Plunkr .
Ich benenne den Betreff mit dem Suffix 'Rxx', daher lautet das BehaviorSubject für searchStatus searchStatusRxx.

  1. Initialisieren Sie es in der übergeordneten Komponente wie searchStatusRxx = new BehaviorSubject(false); ,
  2. Übergeben Sie es mit @Input an die untergeordnete Komponente
  3. In der untergeordneten Vorlage führen Sie eine asynchrone Pipe durch.
  4. Sowohl bei Eltern als auch bei Kindern searchStatusRxx.next(value)ändern Sie den neuesten Wert.
Timathon
quelle
4

Sie haben Ihren Code ein wenig bearbeitet, er funktioniert und sieht imo einfacher aus. Sag mir, ob Du es magst.

https://plnkr.co/edit/oJOjEZfAfx8iKZmzB3NY?p=preview

freundlicher Benutzer
quelle
Vielen Dank. Ich möchte die übergeordnete Eigenschaft von der Suchkomponente aktualisieren. Das ist mein Anwendungsfall.
Körper
@Body Fine, aber Sie sollten überlegen, wie ich Ihre Schaltflächen und Ihren Klickmechanismus zusammen mit der Lösung n00dl3 geändert habe.
freundlicher Benutzer
2

Noch ein anderer Weg. Plunkr . Was wir wollen, ist eine einzige Quelle der Wahrheit. Wir können das diesmal in Kind setzen.

  • Init im Kind: searchStatus = false
  • Rufen Sie in der übergeordneten Vorlage die Instanz von child as ab #as oder wie auch immer ab.
  • Ändern Sie searchStatus im übergeordneten Element mit #as.searchStatusund im untergeordneten Element this.searchStatus.
Timathon
quelle
Dieser Weg ist viel besser und einfacher. Ist dies eine gültige Vorgehensweise? Warum fördert Angular diese Methode nicht? Ich suche seit einer Woche nach einer Lösung und überall habe ich Input / Output-Fälle gefunden.
Körper
Nennen wir dies den Weg "Vorlagenreferenzvariablen". Es scheint einfach zu sein. Das Testen ist jedoch möglicherweise etwas schwieriger, und die Eltern / Kind-Komponenten koppeln miteinander, was möglicherweise nicht gut ist. Persönlich bevorzuge ich die BehaviorSubject-Methode, die einfacher zu testen und zu entkoppeln ist und als Eigenschaft eines komplexen Objekts festgelegt werden kann, das zwischen Eltern und Kind übertragen wird. Eine Nebenbemerkung: Angulars EventEmitter ist ein Thema hinter den Kulissen.
Timathon