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]

Reply via email to