View Full Version : Replacing data nodes
sfarrow
09-04-2003, 05:13 AM
I have nodes in a dataset displayed in views controlled by replicationManager. Is there any way I can replace a data node without interfering with replicationManager and its replicated datapaths? I have attempted to add the updated node and delete the original, but this affects the ordering of the views and doesn't make for a terribly pleasant UI.
A practical example of its use is in creating and editor for a data node. When the user selects a node and clicks Edit, the node is copied to a working dataset. If the user accepts the changes to the node by clicking OK, the updated node needs to replace the original in the master dataset.
Thanks.
Steve
antun
09-04-2003, 11:50 AM
Hey Steve
There isn't a replaceNode() method, although I can see how it would be very useful. Apparently there are some new data APIs in the pipeline that should address (among others) this issue.
In the meantime, I wrote up an example of how I would work around the problem:
<canvas debug="true">
<debug y="200" />
<dataset name="one">
<root>
<person name="Fred" sex="male">
<hometown name="Bedrock" />
</person>
<person name="Wimla" sex="female">
<hometown name="Bedrock" />
</person>
<person name="Barney" sex="male">
<hometown name="Bedrock" />
</person>
</root>
</dataset>
<dataset name="two" />
<view datapath="one:/root">
<simplelayout axis="y" spacing="2" />
<view name="people" datapath="person" bgcolor="0xeaeaea"
onclick="dialog.edit(this)" clickable="true">
<simplelayout axis="x" spacing="2" />
<text datapath="@name" width="75" />
<text datapath="@sex" width="75"/>
<text datapath="hometown/@name" width="75"/>
</view>
</view>
<!--
canvas.datasets.two.getPointer().serialize()
-->
<view align="center" y="35" name="dialog" bgcolor="0xeaeaea"
datapath="two:/person[1]"
visible="false">
<simplelayout axis="y" spacing="2" />
<inputtext datapath="@name" width="75" align="center" />
<inputtext datapath="@sex" width="75" align="center" />
<inputtext datapath="hometown/@name" width="75" align="center" />
<method name="edit" args="clickedClone">
this.setVisible( true );
this.dp_clone = clickedClone.datapath.dupePointer();
// Clear out buffer dataset
this.dp_buf = canvas.datasets.two.getPointer();
this.dp_buf.setXPath( 'person' );
while ( this.dp_buf.isValid() ) {
this.dp_buf.deleteNode();
}
// Copy buffer info to new dataset.
this.dp_buf.setXPath( '/' );
this.dp_buf.addNodeFromPointer( this.dp_clone );
this.dp_buf.setXPath( 'person[1]' );
</method>
<button label="Submit">
<method event="onclick">
this.parent.datapath.retrieveData();
this.copyNodeFromPointer( this.parent.dp_buf,
this.parent.dp_clone );
this.parent.setVisible( false );
</method>
<method name="copyNodeFromPointer" args="dp_from, dp_to">
<![CDATA[
// Update attributes
var newAttrs = dp_from.getNodeAttributes();
for ( var k in newAttrs ) {
// TODO: See if there's a way to avoid having to call
// getNodeAttribute here. In theory it should be possible
// we should be able to say obj[k]
var val = dp_from.getNodeAttribute( k );
dp_to.setNodeAttribute( k, val );
}
// Child nodes
// Note that this doesn't check if the child nodes are the
// same name
var hasKids = dp_from.selectChild()
if ( hasKids ) {
dp_to.selectChild();
this.copyNodeFromPointer( dp_from, dp_to );
}
]]>
</method>
</button>
</view>
</canvas>
Notice that Wilma's name is mis-spelled? If you click on her row, and edit it, then click submit, that'll update the dataset called "one", although your edit takes place in the dataset called "two".
Basically I usee the buffer dataset like you suggested, then on submit called a recursive method that drilled down the XML node(s) copying over any attributes to the dataset that's used for display.
Note that my example only works with attributes (not text nodes), and it doesn't delete attributes that are removed. It does drill down into subnodes, but it doesn't verify their name, or clear them out, but you should have something to work with.
Hope this helps,
Antun
Also, have you seen the retrieveData method of datapath? You can control how data in data-bound LFC objects is fed back into a dataset by defining your own retrieveData methods.
hipik
12-09-2008, 12:25 PM
Hi all,
Here is a new approach for this very old thread method replaces node and all node attributes and elements with content and attributes of new node keeping only the name. I'm quite sure it will be helpful for someone ;)
<canvas debug="true">
<debug y="200" />
<dataset name="one">
<root>
<person name="Fred" sex="male">
<hometown name="Bedrock" />
</person>
<person name="Wimasdla" sex="asd">
<hometown name="Bedssssrock">rock</hometown>
<doodle name="Bedssssrock">rock</doodle>
</person>
<person name="Barney" sex="male">
<hometown name="Bedrock" />
</person>
</root>
</dataset>
<dataset name="two">
<root>
<person name="Dred" sex="dale">
<hometown name="Dedrock" />
</person>
<person name="WiLMa" sex="female">
<hometown name="Bedrock" />
</person>
<person name="Garney" sex="gale">
<hometown name="Gedrock" />
</person>
</root>
</dataset>
<button text="Submit">
<method event="onclick">
p1 = parent.one.getPointer();
p2 = parent.two.getPointer();
Debug.write("BEFORE one" + p1.serialize());
Debug.write("BEFORE two" + p2.serialize());
p1.setXPath('root/person[2]');
p2.setXPath('root/person[2]');
parent.replaceNodeFromPointer(p1, p2);
Debug.write("AFTER one" + parent.one.getPointer().serialize());
Debug.write("AFTER two" + parent.two.getPointer().serialize());
</method>
</button>
<method name="replaceNodeFromPointer" args="fromDP, toDP">
<![CDATA[
// Update attributes
var newAttrs = fromDP.getNodeAttributes();
var oldAttrs = toDP.getNodeAttributes();
for (var ol in oldAttrs) {
toDP.deleteNodeAttribute(ol)
}
for (var nw in newAttrs) {
var val = fromDP.getNodeAttribute(nw);
toDP.setNodeAttribute(nw, val);
}
// Update nodes
toDP.p.setChildNodes([]);
if (fromDP.selectChild()) {
do {
toDP.addNodeFromPointer(fromDP);
} while (fromDP.selectNext())
}
]]>
</method>
</canvas>
Cheers.
Hipik ZM
senshi
12-10-2008, 08:23 AM
Why all the hassle, it's only three lines of code...
<button text="Submit">
<handler name="onclick">
var src = canvas.one.getPointer().xpathQuery("/root/person[2]");
var dest = canvas.two.getPointer().xpathQuery("/root/person[2]");
Debug.write("BEFORE one: %s", canvas.one.serialize());
Debug.write("BEFORE two: %s", canvas.two.serialize());
var clone = src.cloneNode(true);
clone.setNodeName(dest.nodeName);
dest.parentNode.replaceChild(clone, dest);
Debug.write("AFTER one: %s", canvas.one.serialize());
Debug.write("AFTER two: %s", canvas.two.serialize());
</handler>
</button>
hipik
12-10-2008, 09:06 AM
Hi Senshi,
Maybe they are three lines in 4.1 but in 4.0.12 it ends up with:
ERROR @main.lzx#57: call to undefined method 'cloneNode'
ERROR @main.lzx#58: undefined object does not have a property 'setNodeName'
Good info as always.
Thanks!
Hipik ZM
senshi
12-10-2008, 09:14 AM
I don't know what's so special about 4.0.12, but I've verified it works in 4.0.5, 4.0.9.1, 4.1.1 and trunk-nightly..
hipik
12-10-2008, 09:17 AM
No clue, it's the most stabile laszlo yet. I went from 4.0.2 to 4.0.12,
( tried the 4.1 but no cigar :) )
hipik
12-10-2008, 09:33 AM
Ups,
Sorry your code does work under 4.0.12 I messed up my datasets in the meantime! :rolleyes:
anyway method looks like this now with a trick to get DataElement out of Datapointer:
<method name="replaceNodeFromPointer" args="fromDP, toDP">
var src = fromDP.xpathQuery();
var dest = toDP.xpathQuery();
var clone = src.cloneNode(true);
clone.setNodeName(dest.nodeName);
dest.parentNode.replaceChild(clone, dest);
</method>
Much much better ;)
Hipik ZM
vBulletin® v3.8.4, Copyright ©2000-2012, Jelsoft Enterprises Ltd.