Reputation: 1544
So I have a collection of divs in app-component filled with my app-cube
components, and I have an array filled with GridProp
objects in the parent component which's indexes represents each cell in the collection, meaning I create the content of each "cell" based on the content of array[i].
At array[i] I have an object with an Observable in it among other things like the content for cells. When I update something in cell i
I want to update the siblings of cell [i-1]
and [i+1]
.
I am trying to do this by having each app-cube
subscribe to the observable in the matching object in the array. This way I should be able to simply send a value to the observer in the array at any index and have it trigger the subscribed components subscription handler, right?
The objects in the array are of this class
export class GridProp implements OnInit {
private terrain = new Terrain();
coord: Array<number>;
cont: {};
observer: Observer<string>;
observable: Observable<string> = new Observable((observer: Observer<string>) => {
this.observer = observer;
});
}
Then in the cube-component.ts
@Component({
selector: 'app-cube',
templateUrl: './cube.component.html',
styleUrls: ['./cube.component.css']
})
export class CubeComponent implements OnInit {
@Input() point; // Represents the relevant GridProp object
ngOnInit() {
this.point.observable.subscribe(this.handleCubeUpdate);
}
handleCubeUpdate() {
console.log('handling!');
this.updateWalls();
}
updateWalls() {
// Do stuff to the "walls" values used in this cube.component.html
}
toggleWalls() {
for (const direction in this.point.cont.terrain.walls) {
if (this.getSide(direction) && this.getSide(direction).cont.terrain.isBlock) {
this.point.cont.terrain.walls[direction] = false;
this.getSide(direction).observer.next(true);
} else {
this.point.cont.terrain.walls[direction] = true;
}
}
this.updateWalls();
}
}
In the parent component I distrubte the app-cube
<div class="grid-point-z" *ngFor="let z of y">
<div
class="grid-point"
attr.data-point="{{ z.coord }}"
[ngStyle]="{
'z-index': z.coord[2]
}">
<app-cube
[point]="z"
[coord]="z"
[level]="level"
(click)="clickPoint(z)"
(change)="onCubeChange($event)">
{{z}}
</app-cube>
</div>
</div>
It crashes with the error
ERROR TypeError: this.updateWalls is not a function
at SafeSubscriber.push../src/app/assets/cube/cube.component.ts.CubeComponent.handleCubeUpdate [as _next] (cube.component.ts:71)
So I guess it doesn't recognize this.handleCubeUpdate
in the subscription in CubeComponent. Is there a work-around for this?
Upvotes: 0
Views: 1706
Reputation: 373
From my point of view here we have kind of design problem.
In Angular, components can interact with each other using their @Input()
and @Output()
properties(Input - to be aware about changes from parent component and update view, Output - to notify others about changes inside).
So better to add to your CubeComponent
output event somethingChanged
and emit this event when, as you said, you update something.
export class CubeComponent implements OnInit {
@Input() point; // Represents the relevant GridProp object
@Output() somethingChanged = new EventEmitter<GridProp>();
ngOnInit() {
}
notifyOthersComponentAboutUpdate() {
let id = this.point; // You can emit point or some id, to identify it later in parent component
this.somethingChanged.emit(id);
}
}
In you parent component(app-component
) which contains array of app-cube
components create a method updateSiblings(idOfComponentWhichWasUpdated)
and call it from app-cube
component
<div class="grid-point-z" *ngFor="let z of y">
<div
class="grid-point"
attr.data-point="{{ z.coord }}"
[ngStyle]="{
'z-index': z.coord[2]
}">
<app-cube
[point]="z"
[coord]="z"
[level]="level"
(somethingChanged)="updateSiblings($event)">
{{z}}
</app-cube>
</div>
</div>
And in that updateSiblings
method your have access to your y
array(*ngFor="let z of y"
) which contains all data for app-cube
components(y: GridProp[]
).
You have id of component which was updated, so you can find it in array y
and you can find his siblings and update their data as well. Angular will automatically detect changes in that y
array and your siblings will be updated on UI level.
Upvotes: 1
Reputation: 9584
Have you considered using a REDUX architecture in Angular?
Here's a good tutorial on how it works and the benefits. I have used the exact same tutorial to learn about it and implement.
You could then keep that array in your store and dispatch actions to change thing. Each action could contain the logic to update itself + the two items surrounding it.
Upvotes: 0
Reputation: 602
Now it make more sense to me that I could try to answer it here.
Hopefully I could articulate this
properly :).
So, when you pass in a function reference in your subscribe
, this
in handleCubeUpdate
has a different context.
You can change your function implementation to arrow function.
handleCubeUpdate = () => {
this.updateWalls();
}
additionally, if your updateWalls
is calling another function, then you should change that updateWalls
function arrow function.
Upvotes: 2