3.5.4 DBTreeView bean: how to use the new functionality

So, 3.5.4 is available through the auto update, so time to shed some light on the new abilities of the DBTreeView bean, because the bean received an major overhaul and all of this will only appear in the 4.0 documentation, which will not be available until 4.0 will be released.

To start with, it is not just the DBTreeView bean anymore, it is also the DBTreeTableView bean. Yes, you can now also have a tree with multiple columns!

And in both beans, you can build up the tree to collect childnodes from different tables. So, for example, you can go from Companies to Customers to Orders to OrderLines. The sky is the limit :D

Now, you may ask yourself: “does this work in the web as well?”. To that I can only say: YES! Ok, but what about automatically keeping in sync with datachanges, new records and the likes? Yes, that works as well. Also in the web?!?! Once again: Yes!!

So, how does it work? Basically, you still set the rootNodes by supplying a foundset. But then it differs from the old bean: You have to create a so called “binding” to a table. You bind a bean to a table and in the binding you provide information where to get the details how to render nodes that come from that table. You can bind the bean to mulitple tables but a table can only be binded to the bean once. Am I still making sense? Example please…

Example 1:
Place a DBTreeView bean on your form and name it bean_1.

var _binding = elements.bean_1.createBinding(controller.getServerName(), controller.getTableName());
_binding.setImageURLDataprovider("X"); //Replace X with the name of a dataprovider that returns a URL that points to an Image
_binding.setChildSortDataprovider("X"); //Replace X with the name of a dataprovider that returns a sort string
_binding.setHasCheckBoxDataprovider("X"); //Replace X with the name of a dataprovider that returns a boolean value to indicate whether to render the Node with a CheckBox or not
_binding.setCheckBoxValueDataprovider("X"); //Replace X with the name of a dataprovider that is linked to the checkbox
_binding.setNRelationName("X"); //Replace X with the name of the relation that supplied the childNodes for a Node
_binding.setTextDataprovider("X"); //Replace X with the name of a dataprovider that returns the text for the node
_binding.setMethodToCallOnClick(globals.XXXX, "X"); //Sets the global method that gets called when the node is clicked and replace X with the name of a dataprovider will be send as an argument into the globals.XXX method (note: no "()" behind "globals.XXXX")

controller.find();
	column_x = "someValueHere";
controller.search(true,true);
elements.bean_1.addRoots(foundset);

That is all! In the attached example solution you will find a setup as above for the bean on the upper left corner of the form. Noticed that there is a function called “setHasCheckBoxDataprovider”? With that you can determine whether or not to have a checkbox rendered on each node. Is that cool or what?

Note: The sample solution is based on the UDM database. For the bean to show a hierargy, you will need to set the hierargy in the Companies form. Make sure 1 company does not get a Parent Company, or else there will not be any rootNodes in the tree.

Example 2:
Now the next step is to link in multiple tables. How do we accomplish this? Easy! First, per table that we want to link in, we create a binding as described above. You call the createBinding function on the bean and supply the Server- and TableName for which you want to create the binding. As said before: You can only bind a table once to a bean, but a bean can have bindings to multiple tables.

Now, how do we get the bean to read the childNodes from a different table: “_binding.setNRelationDataprovider(”#“);” is your friend. Just replace the # by the name of a dataprovider that returns the name of a relation that will get you the childNodes. Offcourse, you have to have a binding for the table on the right hand side of the relation (that side holding the childNodes) to the bean as well, otherwise the bean does not know how to render the childNodes.

An example of this setup can be found in the lower left corner on the first form of the sample solution.

Example 3:
Ok, so we have a DBTreeView bean with checkboxes and nodes from multiple tables. What about the TreeTable stuff? Well, once again it is not so complicated. instead of placing a DBTreeView bena on our form, we place a DBTreeTableView bean on out form. If you look at the bean in the Method Editor, you will see there is a new subnode called “Column” and a few extra functions, but otherwise, it has a pretty similar API as the regular DBTreeView bean. So, how do we get this TreeTable stuff going? By adding columns…

On the bean, there is a function called “createColumn(servername, tablename, header, fieldname);”. The use is pretty straight forward: You can add columns of the tables you also have created a binding for for this bean. You just specify the serverName, tableName, the header text for the column and the dataprovider name (columnName in the database) and the column will be added to the bean. On a Node in the Tree that comes from that table, the column will be rendered with the data from the dataprovider you specified.

In the attached solution, the two beans on the right are Treetable beans: the upper one takes all nodes from 1 table, the one on the bottom takes its nodes from different tables.

So, that is all the new functionality in a nutshell. Review the sample solution, play around with it, modify is and start using the beans to create some interesting GUI’s :D curious to see the results in Vegas…

Regards,

Paul

TreeBeanSample.servoy (77.3 KB)

Thank you Paul,
this is great stuff! Much appreciated! :D :D

I suggest, provide more of such info! :wink:

Thanks a LOT Paul. :D :D

Really a Great stuff… :D :D

Excellent work, Paul! Sample solution is a very helpfull tool, so thanks a lot.
I tried to test the sample solution in 3.5.3, but then I get a StackOverFlow error. What jars do I need to get it working in 3.5.3, too?

The sample is 3.5.4 only.

As I said in the other posts, I ran into some issues making the example in 3.5.3. Those issues are fixed in 3.5.4.

The DBTreeView bean that comes with 3.5.4 does NOT port back to 3.5.3.

Paul

pbakker:
The sample is 3.5.4 only.
As I said in the other posts, I ran into some issues making the example in 3.5.3. Those issues are fixed in 3.5.4.
The DBTreeView bean that comes with 3.5.4 does NOT port back to 3.5.3.
Paul

Roger that, Paul. Thank you.

Paul,

My code using the DBTreeView was working fine in 3.5.3 but is now breaking in 3.5.4:

The new tree apparently has no way to remove a binding. The prior version at least replaced what was already there (effectively removing the prior binding). How can I accomplish the same with the new version. Here are the details:

The underlying data has changed, but child nodes in the tree are still visible for data that is no longer there! (I only want a single binding…) I think I need something like tree.removeBinding(). Or maybe tree.refresh() should do the job, (but it didn’t work). I’m binding to the same table, but the data has changed and the tree does not reflect the changes. I’m using the rawSql plugin to update the table (deleting and adding rows). I also tried calling plugins.rawSQL.flushAllClientsCache(…). I’ve also tried removeAllRoots() which did not help.

Any ideas?

Thanks,

Hi Mike,

I’m not exactly what your issue is. You say a couple of things:
1- You want to remove a binding
2- The Tree doesn’t reflect changes in the underlying table
3- In 3.5.3, when you “rebinded” a tree to a table again, the previous binding was replaced.

So, a couple of things:
The DBTreeView bean is designed to automatically stay in sync with the underlying data. You say that you are altering data in the database which is also shown in Servoy through the RawSQL plugin. In general, using the RawSQL plugin to update, delete of insert data is not good practice for the following reason: Servoy manages for you the entire databroadcast of datachanges. When you alter the data directly in the database, through other software or with the RawSQL plugin, Servoy doesn’t know about this, so Servoy will not show those changes in the GUI.

To me it sounds like the above is your issue. You altered data in the underlying tables through the RawSQL plugin and now you want to see those changes reflected in the tree. Correct?

If that is the issue, there is no need to rebind a table to the bean: A binding only tells the bean how to display a node based on a record from a table.

The thing you need to do is get the cached data in Servoy back into sync with the database OR EVEN better: never get them out of sync.

So, questions:

  • Why are you using the RawSQL plugin? Why not update/insert/delete through Servoy?
  • If you really need to use the RawSQL plugin and you know which PK’s in which table were effected, you can generate a dataBroadCast command yourself using the appropriate function in the RawSQL plugin
  • If you do not know exactly which PK’s were effected by the insert/update/delete through the RawSQL plugin, you got two options: 1: refresh foundsets for the affected tables (you will at least need to know which tables were affected) or
    2: do a flushAllClientsCache, but this is a very heavy operation because, as the name of the function suggests, it flushes the cache of ALL connected clients. This should be avoided at all costs if possible.

So, to come back to your issue:
1: I think you are not trying to rebind the bean to a table, but are just trying to get the tree in sync with the data in the table again.
2: Try to avoid using the RawSQL plugin for starters
3: If the issue is that you are not seeing the changes in the data in the table: make sure the datacache of the client is back in sync with the underlying table

If the above pointers do solve the issue for you, please create a case in our support system with a sample solution in which the behavior can be reproduced.

Regards,

Paul

Paul,

There is only one foundset used by the tree, added with tree.addRoots(foundset).

The relationship is parent to child on one table.

Rows were deleted and added to this one table using the rawSQL plugin.

Step 1: I did refresh the foundset that had been added to the tree by calling foundset.loadRecords(sqlQuery, sqlArgs) to load the ROOT records for the tree. This had no effect.

Step 2: I then tried plugins.rawSQL.flushAllClientsCache(myserver, mytable) (as stated in my previous post) This also, did not work.

Please correct me if I am wrong, but shouldn’t “Step 1”, shown above, have been enough to refresh the foundset used by the tree?

And, even if Step 1 was not enough, shouldn’t Step 2 (clearing all clients’ cache of the affected table) have been enough?

Is the tree using some other foundset (based on the parent to child relationship?) that I’m not aware of and that I should be refreshing?

Maybe the problem has to do with the circumstance of the parent (root) record not changing, while different child records are present?

In other words, a parent record with an ID of 1 remains in the table, but child records with IDs of 2, 3, and 4 (ParentIDs == 1) have been deleted, and child records with IDs of 5, 6, and 7 and also having ParentIDs of 1 have been inserted.

In this case, while refreshing the root foundset, the actual data would not change (since the same root or parent record remains in the table). But if the parent to child relation is followed, now different child records should be found. Somehow the tree is not detecting this?

Maybe since the root node has not changed, the tree does not know to refresh the child nodes?

What foundset is the data coming from for the child nodes? It does not appear to be coming from the foundset added to the tree using addRoots(), since I refreshed this particular foundset.

So where is the foundset containing the child node data? Please answer me this question.

Thanks,

To get the childNodes for a specific node, you specify the relationName to get the related records.

So, each set of childNodes is a realted foundset, just as you would access all these records throught he relation yourself.

So, your conslusion that “refreshing” the rootNodes by doing a loadRecords is correct: that will only have an effect on the rootNode foundset, not on any of the related foundsets.

So, step 1 you performed is not supposed to bring the tree back in sync. The only thing that does is load a set of PK’s into the root foundset, so that triggers the new records to be seen by Servoy. If you deleted records outside the scope of servoy (RawSQL plugin or another application), Servoy will still not know about that, because you didn’t tell Servoy those records were deleted. Only thing you did was specify Servoy to load certain records.

With regards to step 2: allthough at all not the proper way to solve this issue, plugins.rawSQL.flushAllClientsCache(myserver, mytable) should do the trick. Have you tried it both in Developer and in the Client? What if you also have a tableView on the same table: do you see that one getting in sync after the flush?

If the tableView would get in sync, but the tree doesn’t, there is probably an issue with the DBTReeview bean. If the tableView doesn’t get in sync, there might be soemthing else going on.

But, once again, I would like to mention that the use of the RawSQL plugin should be avioded and used only of there are no other means. When it comes to insert/delete and updates, in 99.99% of the time it’s perfectly possible to do that in Servoy without using the RawSQL plugin.

When somehow you really have to use the RawSQL plugin, then send dataChangeNotifications if possible.

Use of “plugins.rawSQL.flushAllClientsCache(myserver, mytable)” should be avoided at all cost, unless there is absolutely no other way.

Paul

Paul,

Of course I do not want to use flushAllClientsCache(…). So, the question remains, how do I refresh the foundsets that the tree is using for the child nodes?

Thanks,

Paul,

Adding to the above. I also do not want to use notifyDataChange(…).

The data changes only affect the one client, there is no need to refresh any other client.

I just want to refresh or replace any foundsets used by the tree. I would not even care if I need to recreate the entire tree and bindings. But, I don’t see any way to do so.

Thanks,

The databaseManager node has a function called refreshRecordFromDatabase. You need to check the docs for the exact syntax, but I think if you pass -1 for the recordIndex, it refreshed the entire foundset.

When you call this function on the root foundset (assuming that all nodes are based on the same table), this should do the trick.

If it doesn’t, can you then check if a full foundset on that table in tableView does see the changes after the refresh and also create a case then with sample in our support system?

Regards,

Paul

Paul,

I will try your suggestion. If it doesn’t work I will create a case.

Thank you for your quick responses!

Mike

Paul,

We have success!!!

databaseManager.refreshRecordFromDatabase(foundset, -1) does the job. It works, but it threw this error:

java.lang.NullPointerException

The tree, however, is properly refreshed.

Then, thinking about where the null pointer could be coming from, I had the idea to call this first:

elements.docTree.setNodeLevelVisible(1, false); (This changes the selected node to be the root node.)

Guess what? No more error! So everything now works.

Thanks!

Mike

So basically that NullPointer should be fixed / prevented :wink:. I have seen that too. It seems to be related to the top level being touched (nodes added or removed)…

So basically that NullPointer should be fixed / prevented

I completely agree. The work around you came up with is an interesting workaround, but a workaround nonetheless.

I’ve tested a bit with it myself and I do see that the rootNodes foundset doesn’t see newly added or removed records (where Servoy IS aware of the changes). I will address this to our engineers.

I wasn’t able to reproduce the nullPointer though. If you can, please supply us with a sample that demonstrates this.

Tnx,

Paul

I think you see the exception only in the log. The error is not “reported” in the Servoy gui.

Didn’t see anything in the log either.

So, if someone can reproduce this in a sample solution, I’d appreciate it.

Paul

Hi Paul,

This treeview bean looks good. Specially company, order, orderitem could be very useful for us. Thanks for that.

But there is one situation, that I couldn’t find out how I should do.

I have 2 tables.
Table A with menulines. Each menu has a parent menu except the root, which has no parent
Table B with programs. Each program hangs under a menuline

So there will be 2 different type of nodes. One with menulines and the program nodetype.

So I have 2 related tables here (menu_to_menu and program_to_menu). Is that also possible with this treeview (and how)

Kind regards

Martin