Hi, I've been trying to get some bidirectional binding action between CForms and XML.
I've been following the blocks/forms/XML Binding example without complete success. It will bind quite happily to the CForms, it will map changes back to the XML, but only when the row "number" already exists. E.g., if I change 1, 2, 3 for 3, 2, 1, and leave the dates unchanged the correct mapping works (the dates get swapped around). If I add a new row with report number 4, or change, for example report number 2, to report number 4 and change the date it gets ignored and I end up with the old data (report number 2, old date) written back to the XML. Just had a but more of a play. This problem seems entirely related to the number field - which is the identity in the repeater. How should I be identifying new and existing rows? It seems that doing it with a field that can be modified is a bad move (even though this field should be unique), each report could have an id attribute (I had actually previously thought of this for this type of reason) and I could use that to uniquely identify each "report". How would I add that into my code? (This example is closely related to my previous mail on Request parameters and XSLT). I've stripped this down as much as I know how: Sitemap.xmap: ------------- <?xml version="1.0"?> <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0"> <map:resources> <!-- this will later become a virtual transformer --> <map:resource name="simple-page2html"> <map:transform src="context://samples/common/style/xsl/html/simple-page2html.xsl"> <map:parameter name="contextPath" value="{request:contextPath}"/> <map:parameter name="servletPath" value="{request:servletPath}"/> <map:parameter name="sitemapURI" value="{request:sitemapURI}"/> <map:parameter name="file" value="{file}"/> <map:parameter name="remove" value="{../0}"/> </map:transform> </map:resource> </map:resources> <map:flow language="javascript"> <map:script src="form.js"/> </map:flow> <map:pipelines> <map:pipeline> <map:match pattern="*-*-form"> <!-- 1. Document, 2. dataSource --> <map:call function="showForm"> <map:parameter name="formName" value="{1}"/> <map:parameter name="dataSource" value="{2}"/> </map:call> </map:match> <!-- CONTINUATIONS --> <map:match pattern="*.continue"> <map:call continuation="{1}"/> </map:match> <!-- DISPLAY PIPELINES --> <map:match pattern="*-*-*-form-pipeline"> <!-- 1. Document, 2. dataSource, 3. subSection--> <!-- pipeline to show the form --> <map:generate type="jx" src="{1}-form-template.xml"/> <map:call resource="simple-page2html"> <map:parameter name="file" value="{1}-form-template.xml"/> </map:call> <map:transform src="../resources/forms-samples-styling.xsl"> <map:parameter name="resources-uri" value="../resources"/> </map:transform> <map:serialize/> </map:match> <map:match pattern="output"> <map:generate src="input.xml" type="file"/> <map:transform src="output.xsl" type="xslt"> <map:parameter name="use-request-parameters" value="true"/> </map:transform> <map:serialize/> </map:match> </map:pipeline> </map:pipelines> </map:sitemap> Flowscript: cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/v2/Form.js") ; function showForm(formName, dataSource) { var form = new Form(formName + "-form-definition.xml"); form.createBinding(formName + "-form-bind.xml"); var wid = form.getWidget(); var document = loadDocument(dataSource + ".xml"); // bind the document data to the form form.load(document); wid.addReport.onClick = function() { wid.reports.addRow(); } wid.removeReport.onClick = function() { wid.reports.removeRow(function(row) {return row.select.value}); } form.setBookmark(); form.showForm(formName + "-" + dataSource + "-Reports-form-pipeline"); form.save(document); saveDocument(document, dataSource + ".xml"); cocoon.redirectTo("output"); } function loadDocument(uri) { var parser = null; var source = null; var resolver = null; try { parser = cocoon.getComponent(Packages.org.apache.excalibur.xml.dom.DOMParser.ROLE); resolver = cocoon.getComponent(Packages.org.apache.cocoon.environment.SourceResolver.RO LE); source = resolver.resolveURI(uri); var is = new Packages.org.xml.sax.InputSource(source.getInputStream()); is.setSystemId(source.getURI()); return parser.parseDocument(is); } finally { if (source != null) resolver.release(source); cocoon.releaseComponent(parser); cocoon.releaseComponent(resolver); } } function saveDocument(document, uri) { var source = null; var resolver = null; var outputStream = null; try { resolver = cocoon.getComponent(Packages.org.apache.cocoon.environment.SourceResolver.RO LE); source = resolver.resolveURI(uri); var tf = Packages.javax.xml.transform.TransformerFactory.newInstance(); if (source instanceof Packages.org.apache.excalibur.source.ModifiableSource && tf.getFeature(Packages.javax.xml.transform.sax.SAXTransformerFactory.FEATURE )) { outputStream = source.getOutputStream(); var transformerHandler = tf.newTransformerHandler(); var transformer = transformerHandler.getTransformer(); transformer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.INDENT , "true"); transformer.setOutputProperty(Packages.javax.xml.transform.OutputKeys.METHOD , "xml"); transformerHandler.setResult(new Packages.javax.xml.transform.stream.StreamResult(outputStream)); var streamer = new Packages.org.apache.cocoon.xml.dom.DOMStreamer(transformerHandler); streamer.stream(document); } else { throw new Packages.org.apache.cocoon.ProcessingException("Cannot write to source " + uri); } } finally { if (source != null) resolver.release(source); cocoon.releaseComponent(resolver); if (outputStream != null) { try { outputStream.flush(); outputStream.close(); } catch (error) { cocoon.log.error("Could not flush/close outputstream: " + error); } } } } Most of that is a cut and paste jobby from the example, which, admittedly is v1. Input.xml: ---------- <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="output.xsl"?> <data> <reports> <report> <number>1</number> <date>03/08/01</date> </report> <report> <number>2</number> <date>03/07/01</date> </report> <report> <number>3</number> <date>22/10/01</date> </report> </reports> </data> input-form-bind.xml: -------------------- <fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" path="/"> <fb:repeater id="reports" row-path="data/reports/report" parent-path="."> <fb:identity> <fb:value id="number" path="number"/> </fb:identity> <fb:on-bind> <fb:value id="number" path="number"/> <fb:value id="date" path="date"> <fd:convertor datatype="date"/> </fb:value> </fb:on-bind> </fb:repeater> </fb:context> input-form-template.xml: ------------------------ <?xml version="1.0"?> <page xmlns:ft="http://apache.org/cocoon/forms/1.0#template" xmlns:fi="http://apache.org/cocoon/forms/1.0#instance" xmlns:jx="http://apache.org/cocoon/templates/jx/1.0"> <jx:import uri="resource://org/apache/cocoon/forms/generation/template.jx"/> <title>Reports</title> <content> <ft:form-template action="${cocoon.continuation.id}.continue" method="POST"> <ft:widget-label id="reports"/> <br/> <ft:repeater-size id="reports"/> <table border="1"> <tr> <th> <ft:repeater-widget-label id="reports" widget-id="number"/> </th> <th> <ft:repeater-widget-label id="reports" widget-id="date"/> </th> </tr> <ft:repeater-widget id="reports"> <tr> <td> <ft:widget id="number"/> </td> <td> <ft:widget id="date"/> </td> <td> <ft:widget id="select"/> </td> </tr> </ft:repeater-widget> <tr> <td colspan="8" align="right"> <ft:widget id="addReport"/> <ft:widget id="removeReport"/> </td> </tr> </table> <input type="submit" value="Submit"/> </ft:form-template> </content> </page> A typical request would be: http://path/to/sitemap/input-input-form As I've said, forward binding works, and it DOES write input.xml for rows where the identity "number" is not new (That seems to be my understanding), if I change the date the new date gets written. Adding rows, or more to the point, new report "numbers" get ignored. Any help would be much appreciated. The flowscript is probably what I understand least, any pointers to good resources, especially v2 would be most useful. Thanks for your time, Ben. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
