navigationend - Angular 2-Pasar datos al componente hijo después de la inicialización principal
router events subscribe angular 4 (5)
Dado que los datos no están definidos al inicio, puede posponerlos con * ngIf = ''datos''
<div *ngIf=''data''>
<test-child [childData]="data"></test-child>
</div>
O puede implementar ControlValueAccessor en su componente y pasarlo por ngModel con ngModelChange
<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child>
Tengo un componente principal que obtiene datos de un servidor a través de un servicio. Necesito que algunos de estos datos se pasen al componente secundario.
He estado tratando de pasar estos datos con el habitual @Input()
sin embargo, como esperaba, los datos que @Input()
en el componente @Input()
undefined
están undefined
.
Ejemplo
Componente principal
@Component({
selector: ''test-parent'',
template: `
<test-child [childData]="data"></test-child>
`
})
export class ParentComponent implements OnInit {
data: any;
ngOnInit() {
this.getData();
}
getData() {
// calling service and getting data
this.testService.getData()
.subscribe(res => {
this.data = res;
});
}
}
Componente infantil
@Component({
selector: ''test-child'',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnInit {
@Input() childData: any;
ngOnInit() {
console.log(childData); // logs undefined
}
}
He mantenido el ejemplo simple para mostrar lo que estoy tratando de hacer. Mi pregunta es si es posible pasar datos al niño después de la inicialización. No estoy seguro, pero ¿ayudaría el enlace de datos bidireccional en este caso?
Más investigación
Siguiendo las respuestas publicadas, he intentado usar el gancho del ciclo de vida de ngOnChanges
. El problema que tengo es que la función ngOnChanges
no se está ngOnChanges
cuando se actualizan los datos. Los datos son un objeto por lo que creo que los cambios no se están detectando.
Código actualizado en el componente hijo
@Component({
selector: ''test-child'',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnChanges {
@Input() childData: any;
ngOnChanges() {
console.log(childData); // logs undefined
}
}
He intentado una prueba con los ejemplos en vivo en Plunker del equipo de Angular mostrando los ganchos del ciclo de vida. En la prueba, he actualizado OnChangesComponent para pasar un objeto que, como se puede ver en el ejemplo, al actualizar un objeto, ngOnChanges no detecta el cambio. (Esto también se muestra en esta question )
https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV
Parece que el ngDoCheck
podría ayudar a resolver este problema, pero me parece que no ayudaría al rendimiento a largo plazo ya que este cambio de ciclo de vida detecta cada cambio.
También sé que probablemente podría usar el @ViewChild()
para acceder al componente @ViewChild()
y establecer las variables que necesito, pero no creo que para este caso de uso tenga mucho sentido.
Publicar más código para ayudar a explicar mejor
Componente principal
@Component({
selector: ''test-parent'',
template: `
<test-child [childType]="numbers" [childData]="data" (pickItem)="pickNumber($event)">
<template let-number>
<span class="number" [class.is-picked]="number.isPicked">
{{ number.number }}
</span>
</template>
</test-child>
<test-child [childType]="letters" [childData]="data" (pickItem)="pickLetter($event)">
<template let-letter>
<span class="letter" [class.is-picked]="letter.isPicked">
{{ letter.displayName }}
</span>
</template>
</test-child>
<button (click)="submitSelections()">Submit Selection</button>
`
})
export class ParentComponent implements OnInit {
data: any;
numbersData: any;
lettersData: any;
ngOnInit() {
this.getData();
}
getData() {
// calling service and getting data for the game selections
this.testService.getData()
.subscribe(res => {
this.data = res;
setChildData(this.data);
});
}
setChildData(data: any) {
for(let i = 0; i < data.sections.length; i++) {
if(data.sections[i].type === ''numbers'') {
this.numbersData = data.sections[i];
}
if(data.sections[i].type === ''letters'') {
this.lettersData = data.sections[i];
}
}
}
pickNumber(pickedNumbers: any[]) {
this.pickedNumbers = pickedNumbers;
}
pickLetter(pickedLetters: any[]) {
this.pickedLetters = pickedLetters;
}
submitSelections() {
// call service to submit selections
}
}
Componente infantil
@Component({
selector: ''test-child'',
template: `
<!--
Content for child...
Showing list of items for selection, on click item is selected
-->
`
})
export class ChildComponent implements OnInit {
@Input() childData: any;
@Output() onSelection = new EventEmitter<Selection>;
pickedItems: any[];
// used for click event
pickItem(item: any) {
this.pickedItems.push(item.number);
this.onSelection.emit(this.pickedItems);
}
}
Eso es más o menos el código que tengo. Básicamente tengo un padre que maneja selecciones de componentes hijos y luego envío estas selecciones al servicio. Necesito pasar ciertos datos al niño porque estoy tratando de tener el objeto que el niño devuelve en el formato que espera el servicio. Esto me ayudaría a no crear todo el objeto esperado por el servicio en el componente principal. También haría al niño reutilizable en el sentido de que devuelve lo que el servicio espera.
Actualizar
Agradezco que los usuarios sigan publicando respuestas sobre esta pregunta. Tenga en cuenta que el código que se ha publicado anteriormente funcionó para mí. El problema que tuve fue que en una plantilla en particular tenía un error tipográfico que causaba que los datos undefined
estuvieran undefined
.
Espero que esté resultando útil :)
En el momento en que su ChildComponent (que también lo llamó ParentComponent, pero supongo que es un error tipográfico) se inicializa y ejecuta su ngOnInit, no hay datos disponibles en su padre ya que su servicio aún no podrá obtener sus datos. Una vez que sus datos estén allí, se enviarán al niño.
Lo que puedes hacer depende de tu caso de uso ..
Si solo desea mostrar los datos en su plantilla de ChildComponent, puede usar el operador elvis ( ?
). Algo como: {{ childData?}}
O {{ childData?.myKeyName }}
...
Si desea hacer otras cosas, puede usar un setter en su ChildComponent. Interceptará el cambio de entrada y le dará la oportunidad de modificar / probar / "hacer lo que quiera" con él antes de hacer otras cosas con él en su ChildComponent ... por ejemplo:
@Component({
selector: ''test-child'',
template: `
<!-- Content for child.... -->
`
})
export class ChildComponent implements OnInit {
private _childData: any;
@Input()
set childData(parentData: any) {
// every time the data from the parent changes this will run
console.log(parnetData);
this._childData = parentData; // ...or do some other crazy stuff
}
get childData(): any { return this._childData; }
ngOnInit() {
// We don''t need it for that...
}
}
o use ngOnChanges
como lo mencionó Madhu.
También es posible que desee echar un vistazo a la entrada del libro de cocina para Comunicación de componentes en los documentos oficiales.
Supongo que el niño está obteniendo un subconjunto de los datos de los padres, potencialmente de una acción del usuario (como cargar un formulario cuando un usuario hace clic en una fila de la cuadrícula). En este caso yo usaría un evento.
Haga que el padre dispare un evento, tal vez dataSelected, y el contenido del evento sería la información que se seleccionó.
Luego haga que el niño escuche esos eventos y, cuando se active, cargue los datos del evento en el modelo.
Esto ayudará a desacoplar sus componentes, lo que siempre es una buena idea.
puede usar ngOnChanges
para obtener datos como abajo,
ngOnChanges() {
if(!!childData){
console.log(childData);
}
}
OnChanges
Angular llama a su método ngOnChanges cuando detecta cambios en las propiedades de entrada del componente (o directiva).
Lea más sobre esto here .
Actualizar
ngOnChanges
solo se ngOnChanges
si cambia el objeto completo, si solo desea actualizar la propiedad del objeto enlazado, no el objeto completo, tiene dos opciones,
¿O vincular la propiedad directamente en la plantilla, asegúrese de usar
?
como{{childData?.propertyname}}
o puede crear una propiedad get dependiendo del objeto childdata,
get prop2(){ return this.childData.prop2; }
Ejemplo Copmlete,
import { Component, Input } from ''@angular/core'';
@Component({
selector: ''my-app'',
template: `<h1>Hello {{name}}</h1>
<child-component [childData]=''childData'' ></child-component>
`
})
export class AppComponent {
name = ''Angular'';
childData = {};
constructor(){
// set childdata after 2 second
setTimeout(() => {
this.childData = {
prop1 : ''value of prop1'',
prop2 : ''value of prop2''
}
}, 2000)
}
}
@Component({
selector: ''child-component'',
template: `
prop1 : {{childData?.prop1}}
<br />
prop2 : {{prop2}}
`
})
export class ChildComponent {
@Input() childData: {prop1: string, prop2: string};
get prop2(){
return this.childData.prop2;
}
}
¡Aquí está el Plunker !
¡¡Espero que esto ayude!!
`@Component ({ selector: ''test-child'', plantilla: ` ` }) La clase de exportación ChildComponent implementa OnChanges { @Input () childData: cualquiera; ngOnChanges () { console.log (childData); // logs undefined } } `
Se supone que esto es así, ¿no? Espero que este sea el problema
`ngOnChanges () { console.log (this.childData); // referencia con esto
}`