Actualizar la propiedad del componente principal del componente secundario en Angular 2

79

Estoy usando @inputpara recibir una propiedad del componente principal para activar una clase CSS en uno de los elementos del componente secundario.

Puedo recibir la propiedad de los padres y también activar la clase. Pero esto funciona solo una vez. La propiedad que estoy recibiendo de los padres es un dato booleano escrito y cuando establezco el estado de la misma en falseel componente secundario, no cambia en los padres.

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() {

  }
}

Por favor revise el plunker dado arriba. La función de búsqueda abierta solo funciona una vez. Después de cerrar la búsqueda, no vuelve a dispararse.

¿ @inputEs el caso de uso adecuado para este escenario? Por favor ayúdame a arreglar esto. (Actualice el plunker).

Cuerpo
fuente
¿Dónde establece `this.isSearchActive = false;` después de que se estableció trueen handleSearch()?
Günter Zöchbauer
en header / search.ts '<button (click) = "getSearchStatus = false" class = "close">'
Cuerpo

Respuestas:

137

Necesita utilizar enlace de datos bidireccional.

@Input()es un enlace de datos de una forma. para habilitar el enlace de datos bidireccional, debe agregar un @Output()correspondiente a la propiedad, con un sufijo "Cambiar"

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

cuando desee publicar el cambio realizado en su propiedad al padre, debe notificar al padre con:

this.getSearchStatusChange.emit(newValue)

y en el padre necesita usar la notación banana-in-a-box para esa propiedad:

[(getSearchStatus)]="myBoundProperty"

también puede vincularse a la propiedad y activar una devolución de llamada cuando cambia en child:

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

ver el plnkr

n00dl3
fuente
Gracias por su respuesta. ¿Puedes actualizar mi plunker?
Cuerpo
1
Eso agregará trabajo adicional para el mismo comportamiento que usar @Output(). Eso suena a reinventar la rueda. @RobertVangor
n00dl3
2
Al usar ReactiveForms, incluso con mi propiedad secundaria marcada public, no pude usar el enfoque de "caja banana" y obtuve errores de que no era una propiedad conocida del componente ... Sin (propertyChanged)="setParentProp($event)"embargo, la vinculación solo con el funcionó perfectamente.
mc01
1
No puedo responder a su pregunta, ya que merecería su propia publicación. Su problema es que está emitiendo cambios justo después del ciclo de detección de cambios de los padres. En otras palabras, ha logrado realizar la detección de cambios para activar algunos cambios que no está permitido por Angular. @manoj
n00dl3
2
@Chandrakant no, para usar la sintaxis de banana box, debe usar el sufijo de cambio. Para salidas personalizadas, puede nombrarlo como desee. Banana box es solo un azúcar de sintaxis muy estúpido que muchas personas abusan
Allwe
7

Otro enfoque: use rxjs / BehaviorSubject para pasar el estado entre diferentes componentes.
Aquí está el plunkr .
Nombre el asunto con un sufijo 'Rxx', por lo que el BehaviorSubject para searchStatus será searchStatusRxx.

  1. inicializarlo en el componente principal como searchStatusRxx = new BehaviorSubject(false);,
  2. pasarlo al componente hijo usando @Input
  3. en la plantilla secundaria, haces async pipe.
  4. tanto en padre como en hijo, searchStatusRxx.next(value)debe cambiar el último valor.
Timatón
fuente
4

Editó su código un poco, funciona y parece más simple en mi opinión. Dime si te gusta.

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

amable usuario
fuente
Gracias. Quiero actualizar la propiedad principal del componente de búsqueda. Ese es mi caso de uso.
Cuerpo
@Body Bien, pero debería considerar usar la forma en que modifiqué sus botones y el mecanismo de clic, junto con la solución n00dl3.
amable usuario
2

Otra forma más. Plunkr . Lo que queremos es una única fuente de verdad. Podemos poner eso en niño esta vez.

  • Init en niño: searchStatus = false
  • En la plantilla principal, obtenga la instancia del niño como #aso cualquier nombre.
  • Cambiar searchStatus en parent using #as.searchStatusy en child this.searchStatus.
Timatón
fuente
De esta forma es mucho mejor y más sencilla. ¿Es esta una forma válida de proceder? ¿Por qué Angular no promueve este método? Estoy buscando una solución desde hace una semana y en todas partes encontré casos de entrada / salida.
Cuerpo
Llamemos a esto la forma de "Variables de referencia de plantilla". Parece sencillo. Pero puede ser un poco más difícil de probar y los componentes padre / hijo se están acoplando, lo que puede no ser bueno. Personalmente, prefiero la forma BehaviorSubject, que es más fácil de probar y desacoplar y se puede establecer como una propiedad de objeto complejo que pasa entre padre e hijo. Una nota al margen: eventEmitter de angular es un sujeto detrás de escena.
Timatón