I have the following structure:
Data-Handling Component
|
|-- Grid Component
|-- Chart Component
That is Data-Handling
Component is the parent of two sibling components: Grid
and Chart
.
I have an array of numbers which is shared between the three components via @Input()
/ @Output
properties.
When in the Grid
component I update a single value of the array, an @Output()
property sends the information to the parent Data-Handling
component, which in turn, sends the information to the Chart
component through one of his @Input()
properties.
As such (I've re-written and skipped some of the code, so some syntactic errors might be present):
grid.component.html:
[Not Relevant?]
grid.component.ts:
@Input() gridValues: any[] = [] // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@Output() valueChanged: EventEmitter<any> = new EventEmitter<any>()
updateAllValuesToThree(): void {
for (var i = 0; i < this.gridValues.length; i++) {
this.updateValue(i, 3)
}
}
updateValue(index: number, value: number): void {
let updatedValue: any = {}
updatedValue.index = index
updatedValue.value = value
this.gridValues[index] = value
this.valueChanged.emit(updatedValue)
}
dataHandling.component.html:
<grid (valueChanged)="onValueUpdate($event)"
[gridValues]="dataArray">
</grid>
<chart [chartData]="dataArray"
[individualValueChanged]="individualValueChanged">
</chart>
dataHandling.component.ts:
dataArray: number[] = [] // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
individualValueChanged: any = {}
onValueUpdate(event: any): void {
this.dataArray[event.currentValue.index] = event.currentValue.value
this.individualValueChanged = event.currentValue
}
chart.component.ts:
@Input chartData: number[] = [] // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@Input individualValueChanged: any = {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.individualValueChanged && changes.individualValueChanged.currentValue) {
let changeInfo: any = changes.individualValueChanged.currentValue
this.chartData[changeInfo.index] = changeInfo.value
}
}
chart.component.html:
[Not Relevant?]
Well. If I update a SINGLE value from the Grid
to the Chart
, everything's OK.
However, when I call the EventEmitter
inside the for
loop for every element of the gridValues
array, only the last element of the array is modified in the chart
component's chartData
array.
So:
Grid
component I'd have: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
Data-Handling
component I'd have: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
Chart
component I'd have: [1, 2, 3, 4, 5, 6, 7, 8, 9, 3]
Every event fires correctly into the Data-Handling
component from the Grid
, but only the last update event goes into the Chart
component, thus triggering the ngOnChanges()
.
Is this working as intended? Am I missing something?
This is expected behavior. @Input
updates are done during change detection. @Output
are processed outside of change detection.
When you trigger an event here:
for (var i = 0; i < this.gridValues.length; i++) {
this.updateValue(i, 3)
}
The following method in data handling component is triggered:
onValueUpdate(event: any): void {
this.dataArray[event.currentValue.index] = event.currentValue.value
this.individualValueChanged = event.currentValue
}
So this method is triggered as many times as you trigger this.updateValue(i, 3)
. The last value of event.currentValue
will be set for this.individualValueChanged
.
Only once all events are processed Angular goes through the change detection stage and updates the input individualValueChanged
binding which contains the last value.
For more information on change detection read Everything you need to know about change detection in Angular. Also read Two Phases of Angular Applications