View Full Version : Recursive Structures?
donpark
06-18-2003, 02:51 PM
How does one create dynamic recursive structures like trees in LZX? One can do this on the server-side using JSP or similar mechanisms, but that requires regeneration of LZX for each instance of the structure.
I understand how <dataset> supports dynamic repetitive structures like list and table in LZX, but I don't see any references to recursive structures. One possible use is OPML viewer/editor.
antun
06-18-2003, 03:31 PM
Here's a quick example that uses data replication. The <dataset> you mentioned is just a set of data. You use data replication to (as you pointed out) create grids and tables. But that doesn't mean to say that you are limited to grids and tables at all.
<canvas>
<dataset name="foo">
<page name="Root Page">
<page name="First Page">
<page name="Sub-page number 1" />
<page name="Sub-page number 2" />
</page>
<page name="Second Page" />
<page name="Third Page" />
</page>
</dataset>
<class name="recursor" bgcolor="0xe5e5e5">
<attribute name="haschildren" value="false"/>
<method event="ondata">
<![CDATA[
if( this.datapath.getNodeCount() ){
this.setAttribute( 'haschildren' , true );
}
]]>
</method>
<simplelayout/>
<!-- This is the visible portion -->
<view>
<simplelayout axis="x" spacing="5" />
<!-- The tag name -->
<text name="nm" width="80" datapath="name()"/>
<!-- The value of the "name" attribute -->
<text name="nm" width="100" datapath="@name"/>
</view>
<!-- This is a workaround for a bug in the LPS that causes an
infinite loop if you try to set a datapath to * when there are
no child nodes. This will be fixed eventually and you won't
have to use a state -->
<state name="childstate">
<attribute name="apply" constraint="classroot.haschildren"/>
<recursor x="10" datapath="*"/>
</state>
</class>
<recursor datapath="foo:/*"/>
</canvas>
Does that answer your question?
-Antun
donpark
06-18-2003, 05:03 PM
Yup! Using ondata to recurse sure worked. Nice. Thanks!
felasfa
07-01-2003, 06:56 PM
Dear Antun,
I read you post re:using onData and state to make a recursive structure.
I am desperately trying to make a folding tree structure (i.e. to replicate a directory tree) but am stuck on dynamically attaching subviews on user clicks. Not working.
Is there a simple way to do this - or do you have a prototype component you can send our way. This is a component of Flash MX and replicating it seems like a lot of work for a beginner laszlo programmer like myself !
antun
07-01-2003, 10:01 PM
Unfortunately we haven't got a ready made tree component. It has been mentioned before, and it's very likely that at some point in the future there will be one available.
Right now we're in the process of redoing all the components from the ground up. They're going to be far more practical and interactive, as well as being skinnable. I'm not sure if a tree component is in that batch.
What's the problem with the component you're trying to build? Where are you getting stuck? Do you want to post some of it online?
-Antun
felasfa
07-02-2003, 12:41 PM
thanks for the reply.
I am trying to to develop an tree-like interface where the user clicks on a twiddle and the branches open and close - exactly like the folder view in Mac Finder (list view) - small screenshot of my current attempt attached.
The tree would be populated using an XML datasource. Thanks to your previous posting, I learned how to populate it using recursion and thus can now show a static version of the whole tree.
But, I cannot figure out how to "close" a branch, i.e. to make the views hidden on user input. In particular, how do you click on an object/view and make its next sibling hidden & un-hidden- i.e., what is the proper addressing ?
Also, if I observed the functioning of your code correctly, the views added via "recursion" are coming up as children of the immediately preceeding item. Therefore, I would need to make not siblings but children hidden/un-hidden.
Finally, I think I see a bug in the execution (i.e. LPS bug) of the code you posted. I have attached your code below, slightly modified.
When the "visible" attribute of the views added by recursion (in the "state" tag) is set to "false", only the parent elements should be displayed. In this example, none of the "white" fields should be displayed. Yet, the first child of each parent element is inadvertently displayed. Check it out.
Again, would appreciate any help.
thank you.
<?xml version="1.0" encoding="UTF-8"?>
<canvas debug="true" height="800" width="800">
<debug height="400" width="400" x="300"/>
<dataset name="foo">
<pages>
<page name="page 1"/>
<page name="page 2">
<page name="page 2, sub page 1"/>
<page name="page 2, sub page 2">
<page name="sub page2, sub page 1"/>
<page name="sub page2, sub page 2"/>
</page>
</page>
<page name="page 3">
<page name="page 3, sub page 1">
<page name="page 3, sub page 1, sub page 1"/>
</page>
</page>
<page name="page 4"/>
</pages>
</dataset>
<class bgcolor="0xe5e5e5" name="recursor">
<attribute name="haschildren" value="false"/>
<method event="ondata"><![CDATA[
if( this.datapath.getNodeCount() ){
this.setAttribute( 'haschildren' , true );
}
]]></method>
<simplelayout/>
<!-- This is the visible portion -->
<view>
<simplelayout axis="x" spacing="5"/>
<!-- The tag name -->
<text datapath="name()" name="nm" resize="true"/>
<!-- The value of the "name" attribute -->
<text datapath="@name" name="item_name" resize="true"/>
</view>
<!-- This is a workaround for a bug in the LPS that causes an
infinite loop if you try to set a datapath to * when there are
no child nodes. This will be fixed eventually and you won't
have to use a state -->
<state name="childstate">
<attribute constraint="classroot.haschildren" name="apply"/>
<!-- ********** note addition of "visible" and "bgcolor" attributes ********* -->
<recursor bgcolor="white" datapath="*" x="10" visible="false" />
</state>
</class>
<simplelayout axis="y" spacing="2"/>
<recursor datapath="foo:/pages/page"/>
</canvas>
antun
07-02-2003, 01:43 PM
This has actually opened up a can of worms that I wasn't expecting. Here is the code that does most of what you want:
<canvas debug="true" height="800" width="800">
<debug height="400" width="400" x="300"/>
<dataset name="foo">
<pages>
<page name="page 1"/>
<page name="page 2">
<page name="page 2, sub page 1"/>
<page name="page 2, sub page 2">
<page name="sub page2, sub page 1"/>
<page name="sub page2, sub page 2"/>
</page>
</page>
<page name="page 3">
<page name="page 3, sub page 1">
<page name="page 3, sub page 1, sub page 1"/>
</page>
</page>
<page name="page 4"/>
</pages>
</dataset>
<class bgcolor="0xe5e5e5" name="recursor" clickable="true">
<attribute name="haschildren" value="false"/>
<method event="ondata"><![CDATA[
if( this.datapath.getNodeCount() ){
this.setAttribute( 'haschildren' , true );
}
]]></method>
<method event="onclick">
this.toggleVisibility();
</method>
<method name="toggleVisibility">
<![CDATA[
if ( this.smelly ) {
if ( this.smelly.getNodeCount() ) {
// smelly is a LzReplicationManager
for ( var i=0; i < this.smelly.getNodeCount(); i++ ) {
var firstClone = this.smelly.getCloneNumber( i );
firstClone.setVisible( !firstClone.visible );
}
} else {
// smelly is just a view, because there is only one
this.smelly.setVisible( !this.smelly.visible );
}
}
]]>
</method>
<simplelayout/>
<!-- This is the visible portion -->
<view>
<simplelayout axis="x" spacing="5"/>
<!-- The tag name -->
<text datapath="name()" name="nm" resize="true"/>
<!-- The value of the "name" attribute -->
<text datapath="@name" name="item_name" resize="true"/>
</view>
<!-- This is a workaround for a bug in the LPS that causes an
infinite loop if you try to set a datapath to * when there are
no child nodes. This will be fixed eventually and you won't
have to use a state -->
<state name="childstate">
<!-- Notice new attribute constraint syntax below: -->
<attribute value="${classroot.haschildren}" name="apply"/>
<!-- ********** note addition of "visible" and "bgcolor" attributes ********* -->
<recursor name="smelly" bgcolor="white" datapath="*" x="10"
visible="true" />
</state>
</class>
<simplelayout axis="y" spacing="2"/>
<recursor datapath="foo:/pages/page"/>
</canvas>
... and now for the pain:
In LZX, when a view has a datapath that matches more than once, it becomes an instance of LzReplicationManager (docs: http://www.laszlosystems.com/developers/learn/documentation/lzxref/replicationmanager.php). If it's datapath matches only once, then it is just a view.
As a view, it has the setVisible() method, but a LzReplicationManager, on the other hand does not. Instead, you have to talk to the individual cloned views that the LzReplicationManager manages.
In the above example, if there is just one subpage, you can turn its visibility on or off using smelly.setVisible(). However if it has two or more subpages, then smelly is an instance of LzReplicationManager, and we have to loop through it's cloned views by getting a pointer to each one in turn using getCloneNumber( n ), then turning the visibility on or off for it.
Since a LzReplicationManger has a getCloneNumber() method and a view does not, I used that to test if it was a LzReplicationManager, or a view.
I'm going to file a bug on this, but in the meantime, the code I gave you will work.
Now you wanted them all closed at first. This is a bit trickier, because if you make smelly invisible at the start, you will catch every other view. Instead you need to make the entire recursor class invisible (and the smelly class too) (I haven't done this - I'll leave that one to you to try first), and keep track of the depth of recursion using an attribute of the class. Then turn it's visibility on if it is one of the top level pages only. From there, just affect smelly, don't call setvisible on the view.
If you get stuck, let me know!
Finally, have a look here for how to mark up code when posting - it helps preserve the whitespace. I edited your post myself and added the code tags:
http://www.laszlosystems.com/developers/community/forums/misc.php?action=bbcode#buttons
-Antun
felasfa
07-02-2003, 05:52 PM
first of all, wow !
you are a giant among us lilliputian programmers. I am amazed by your rapid and accurate insight (even before you had dispatched my problem without a bead of sweat !).
thanks for the help. I've got it working now.
as for making all but the "root" folders invisible, I found a shortcut. I simply detected all nodelevels > 1 and made them invisible. See below.
<method event="ondata"><![CDATA[
if( this.datapath.getNodeCount() ){
this.setAttribute( 'haschildren' , true );
}
if(this.nodeLevel > 1)
this.setAttribute('visible',false);
]]></method>
The actual node level will depend on where the code is but a simple debug.write can discover what the cutoff should be.
Screenshot of working twiddles attached.
thanks !
vBulletin® v3.8.4, Copyright ©2000-2012, Jelsoft Enterprises Ltd.