XForms/Outline Editor
Motivation
editSometimes you want to allow a user to be able to add properties to a list that change the appearance of the item in the list. In this example, the level of indentation is controlled by an attribute in a list called the "level" which is the indentation level in an outline view.
Note: This program was based on an example Kurt Cagle posted on the Mozilla XForms developer news group. This example posted with his permission.
Screen Image
editLink to working Program
editSample Program
editHere is a sample outline-editor program. If you want to test this, be sure to use the edit view to copy the text. This file includes several tab characters that do not display correctly in the view mode.
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/2007/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xbl="http://www.mozilla.org/xbl">
<head>
<!-- based on example posted by Kurt Cagle on Mozilla XForms Newsgroup in Feb. 2007 -->
<style type="text/css">
#itemEntry .xf-value {
width:250px;
}
#content {
border:inset 2px blue;
width:350px;
height:400px;
overflow-y:auto;
background: silver;
}
</style>
<xf:model id="data_model">
<xf:instance id="document_data" src="item-tree.xml" xmlns=""/>
<xf:bind nodeset="//delete-trigger-enabled" relevant="count(//item) > 1" />
<xf:action ev:event="xforms-ready">
<xf:setfocus control="itemEntry" />
</xf:action>
<xf:action ev:event="addItemEvent">
<xf:insert nodeset="//data//item[position() =
index('list')]" at="index('list')" position="after" />
<xf:setvalue ref="//data//item[position() =
index('list')]" value="" />
<xf:setvalue ref="//data//item[position() =
index('list')]/@level" value="//data//item[position() =
index('list') - 1]/@level" />
<xf:setfocus control="itemEntry" />
</xf:action>
<xf:action ev:event="removeItemEvent">
<xf:delete nodeset="//data//item[position() =
index('list')]" at="index('list')" />
<xf:setindex repeat="list" index="index('list')-1" />
<xf:setfocus control="itemEntry" />
</xf:action>
<xf:action ev:event="promoteItemEvent">
<xf:setvalue ref="//data//item[position() =
index('list')]/@level" value="if(number(.) - 1 < 1,1, number(.) - 1)" />
<xf:setfocus control="itemEntry" />
</xf:action>
<xf:action ev:event="demoteItemEvent">
<xf:setvalue ref="//data//item[position() =
index('list')]/@level" value="if(index('list') != 1,
if (number(.) > //data//item[position() =
index('list')-1]/@level,(//data//item[position() =
index('list')-1]/@level) + 1,number(.) + 1),1)" />
<xf:setfocus control="itemEntry" />
</xf:action>
<xf:submission id="save" method="put" action="item-tree.xml" instance="document_data"/>
</xf:model>
</head>
<body>
<xf:group ref="/container/data">
<div>
<xf:trigger id="addItem">
<xf:label>Add</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="addItemEvent" target="data_model" />
</xf:action>
</xf:trigger>
<xf:trigger id="removeItem">
<xf:label>Remove</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="removeItemEvent" target="data_model" />
</xf:action>
</xf:trigger>
<xf:trigger id="promote">
<xf:label>Promote <</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="promoteItemEvent" target="data_model" />
</xf:action>
</xf:trigger>
<xf:trigger id="demote">
<xf:label>Demote ></xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="demoteItemEvent" target="data_model" />
</xf:action>
</xf:trigger>
<div id="content">
<xf:repeat nodeset="items/item" id="list">
<div>
<xf:input ref="." id="itemEntry" incremental="true">
<xf:label>
<xf:output value="concat(substring('                  ',
1, 3*(number(@level) - 1)),'* ')" />
</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="addItemEvent" target="data_model" />
</xf:action>
</xf:input>
</div>
</xf:repeat>
</div>
</div>
</xf:group>
<xf:submit submission="save">
<xf:label>Save</xf:label>
</xf:submit>
</body>
</html>
Sample Instance File "item-tree.xml"
edit<?xml version="1.0" encoding="UTF-8"?>
<container>
<data>
<items>
<item level="1">none</item>
<item level="2">Fiction</item>
<item level="3">Fantasy</item>
<item level="3">Mystery</item>
<item level="3">Religion</item>
<item level="3">Romance</item>
<item level="3">Science Fiction</item>
<item level="2">Non-Fiction</item>
<item level="3">Cooking</item>
<item level="3">History</item>
<item level="3">Biographies</item>
<item level="3">Computers</item>
<item level="4">Software</item>
<item level="5">XForms Tutorial and Cookbook</item>
<item level="3">Internet</item>
<item level="3">Nature</item>
<item level="3">Science</item>
<item level="1">Electronics</item>
<item level="1">Music</item>
</items>
</data>
</container>
Discussion
editThe structure of this program uses custom events. Note that in the model there are a series of actions, each with their own event name:
<xf: model id="data_model">
...
<xf:action ev:event="addItemEvent">
...
These events are called directly inside of a trigger like this:
<xf:dispatch name="addItemEvent" target="data_model" />
Note that the target attribute is the name of the model that the events are located.
Although actions can be tucked directly into a trigger, sometimes action are more logically grouped with instance data and should be placed directly in the model. This also allows other events need to reuse these actions, so it is a good ideal to put reuseable actions directly in a model
References
editA version of this program was initially posted by Kurt Cagle on the Mozilla XForms newsgroup.