Skip to main content

Custom Tree Data Provider

In more complex scenarios, it's probably easiest to implement your own data provider. This provider must implement the TreeDataProvider interface, i.e.

export interface TreeDataProvider<T = any> {
onDidChangeTreeData?: (listener: (changedItemIds: TreeItemIndex[]) => void) => Disposable;
getTreeItem: (itemId: TreeItemIndex) => Promise<TreeItem<T>>;
getTreeItems?: (itemIds: TreeItemIndex[]) => Promise<TreeItem[]>;
onRenameItem?: (item: TreeItem<T>, name: string) => Promise<void>;
onChangeItemChildren?: (itemId: TreeItemIndex, newChildren: TreeItemIndex[]) => Promise<void>;
}

At least the getTreeItem method must be implemented, to declare how data can be made available to the tree structure. getTreeItems allows you to make loading more efficient if multiple entries need to be loaded at once. If you do not implement getTreeItems, they are loaded sequentially using getTreeItem.

The methods onRenameItem and onChangeItemChildren allow you to declare how updates to the tree structure should be handled, i.e. by renaming an item or moving items from one parent to another. You still need to enable this functionality in the environment by providing the respective flags. Look into the TreeCapabilities interface for more details on the necessary flags.

You can use this implementation as baseline:


class CustomDataProviderImplementation implements TreeDataProvider {
private data: Record<TreeItemIndex, TreeItem> = { ...shortTree.items };

private treeChangeListeners: ((changedItemIds: TreeItemIndex[]) => void)[] =
[];

public async getTreeItem(itemId: TreeItemIndex) {
return this.data[itemId];
}

public async onChangeItemChildren(
itemId: TreeItemIndex,
newChildren: TreeItemIndex[]
) {
this.data[itemId].children = newChildren;
this.treeChangeListeners.forEach(listener => listener([itemId]));
}

public onDidChangeTreeData(
listener: (changedItemIds: TreeItemIndex[]) => void
): Disposable {
this.treeChangeListeners.push(listener);
return {
dispose: () =>
this.treeChangeListeners.splice(
this.treeChangeListeners.indexOf(listener),
1
),
};
}

public async onRenameItem(item: TreeItem<any>, name: string): Promise<void> {
this.data[item.index].data = name;
}

// custom handler for directly manipulating the tree data
public injectItem(name: string) {
const rand = `${Math.random()}`;
this.data[rand] = { data: name, index: rand } as TreeItem;
this.data.root.children?.push(rand);
this.treeChangeListeners.forEach(listener => listener(['root']));
}
}

Reacting to Drag Events

RCT will call onChangeItemChildren when a drag operation is finished. You can use this directly to update your data source. Note that, if you add or remove items, the affected item is the parent item, not the added or removed items.

In the exemplary implementation above, this emits an event on the treeChangeListeners listeners, where you could register a custom listener to react to changes.

Reacting to Rename Events

RCT will call onRenameItem when a rename operation is finished. Implement your rename logic there.

Custom Provider Live Demo

Live Editor
Result
Loading...