Reputation: 2470
In Angular, a component can receive as @Input()
a variable or object. Whenever the variable changes, Angular notifies the component. Very cool...
I'm trying to build an app that shows a ng2-tree
view on the left, and an edit-view on the right. When the user clicks on a node in the tree, the details of that item is displayed on the right, and the user can change properties of that item. What I need is for those changes to propagate back to the tree.
The individual nodes in the tree are an ItemTreeNode
:
export class ItemTreeNode implements TreeModel {
value: string;
id: string;
children: Array<ItemTreeNode>;
icon: string;
settings: TreeModelSettings;
loadChildren;
constructor(
private itemService: ItemService,
private parent_id: string,
public item: Item
){ ... }
}
The tree renders the node using .value
for the name. This is set during the constructor by calling this.item.nodeName()
. Once set, it doesn't change. The problem is, it needs to change whenever the item is changed.
What I'm looking for is a way for this object to be notified if the item changes, so that the value
property can be updated.
Is there a way for the ItemTreeNode
to "watch" the item and receive a callback if it ever changes, as part of Angular's change detection cycle?
Or is there a better way of implementing the desired behavior?
Upvotes: 0
Views: 1314
Reputation: 20814
This could be done with the help of RxJs. Let's interrupt Item's name setting via setter and trigger an Observable .next()
.
import {BehaviorSubject} from 'rxjs/BehaviorSubject'
class Item {
private _name: string;
private nameSource = new BehaviorSubject<string>('');
name$ = this.nameSource.asObservable();
set name(name: string) {
this._name = name;
this.nameSource.next(name);
}
get name() { return this._name; }
getNodeName() { return this._name + ' !!!'; }
constructor(name: string){
this._name = name;
}
}
You see, I push the original Item's name on this.nameSource.next(name)
. In my example I assume that Item's name and the Node's name are defferent. Observable $name
is public, it is for external subscriptions. Then I need to subscribe on Item's name change, and the best place for this is the ItemTreeNode constructor which gets an Item instance as a parameter:
class Node {
value: string;
constructor(item: Item) {
item.name$.subscribe(result => this.value = item.getNodeName());
}
}
Here the result
is the Item's new name, but since it's not the same as Item's Node name, I do an extra call .getNodeName()
. So the only thing you need to do is to pass Item's instance to Node's constructor, and then RxJs does the binding magic for you.
this.item = new Item('My Name');
const node = new Node(this.item);
That's all, I creatred a Plunker to demonstrate this approach.
Upvotes: 0