yaronn / blessed-contrib

Build terminal dashboards using ascii/ansi art and javascript
MIT License
15.47k stars 841 forks source link

Allow to provide tree childrens asynchronously #44

Open FWeinb opened 9 years ago

FWeinb commented 9 years ago

It would be awesome if it would be possible to provide the data for contrib.tree in an async way. Maybe it would be possible to return a Promise in the function passed to the children key like:

{
  extended: true,
  name: 'root',
  children: () => {
     //return Promise() 
  }
}

or via a callback function like:

{
  extended: true,
  name: 'root',
  children: (self, done) => {
     asyncTask((err, data) => { done(data) }); 
  }
}
yaronn commented 9 years ago

Hi @FWeinb

You can call tree.setData() anytime, including from a callback. Do you prefer to provide the data from the input properties? I think the current methods allows most flexibility.

theredcat commented 9 years ago

@yaronn I've tough about it, but this is much more complicated to handle since I need to know if the node I'm displaying have children or not (the question is "can this node be extended?").

However this is possible :

I'll tought about it this WE

theredcat commented 9 years ago

@FWeinb @yaronn I think this is a good idea. I think every GUI should be 100% async since a GUI is by definition event-based

FWeinb commented 9 years ago

@theredcat Glad you are seeing it the same way.

FWeinb commented 9 years ago

After looking into this a little bit more i found that there are even more problems with the tree implementation. First of all using the blassed.list isn't working because it can't handle multiple element with the same name. So If you have the same element in the exact same level, selecting the second one will always trigger the select event for the first element (because list.getSelectedIndex is doing a .indexOf for the element string over the items array)

It would be really great to have list component that would work with arbitrary object (exposing some kind of an interface e.q. have an .id property to identify it, and use the .toString() method to render it)

[Edit] There is a workaround. Accessing the list.selected index on the list itself returns the "real" index.

dundalek commented 7 years ago

I tried to use the workaround as suggested by @yaronn.

I kick off async job that fetches the children in the children function. When the job completes I set childrenContent on the node, and re-render the whole tree by setting tree.setData(root) and screen.render().

However, when a node is expanded, the tree widget loads not only children but also children of children. That means the app re-renders hundreds of times and the performance is really bad.

dundalek commented 7 years ago

Alright, I tried another approach which seems to work well. The key is to pass in static tree without children functions. Triggering the loading function is done from the select event handler. After it returns data we can just insert it into the static tree and re-render once.