Loading ...

User Guide

What is it?

jaces is a extension to dijit for writing less and simplier code for form handling, listing items, adding records and updating them. The backbone of jaces is the widget jaces/View. Views have a form and many form widgets inside. The form might be any widget extending from jaces/body/_Form and form widgets might be any dijit and dojox widget capable of handling/carrying/representing form data and actions. Examples include but not limited to text boxes, combo boxes and buttons.

Simplest Sample

The simplest sample of a view is this one:


      <div dojoType="jaces/View">
         <div dojoType="jaces/body/Form">
            A text value:
            <div dojoType="dijit/form/TextBox" valueCol=".field1"></div>
            <div dojoType="dijit/form/Button" onClick="var values = {}; this.getView().getForm().gather(values); alert('Form values: \n ' + jAces.dump(values));">
               Click to dump the form values</div>
         </div>
      </div>
   

A text value:
Click to dump the form values

The simplest sample may not make very good sense to all. Be patient please! Advanced samples are comming! When the button is clicked, the following things are dumped.

  1. field1:

    The value of the text box.

  2. isDirty:

    A boolean value indicating the user changed since the form is initiated or last saved.

  3. _select_Columns:

    The comma separated list of fields in the form. The list is generally used by the data store to provide the relevant values to the form.

  4. _update_Columns:

    The comma separated list of update fields in the form. The list is generally used by the data store to apply the values to the target entity when an insert or update runs.

Simplest Sample with Validation

At any point the form values can be validated.


      <div dojoType="jaces/View">
         <div dojoType="jaces/body/Form">
            A text value:
            <div dojoType="dijit/form/ValidationTextBox" required="true" valueCol=".field1"></div>
            <div dojoType="dijit/form/Button" onClick="if (this.getView().getForm().validate()); alert('Validation passed.'); else alert('Validation failed.');">
               Click to validate the form values</div>
         </div>
      </div>
   

A text value:
Click to validate the form values

Simplest Sample with Submit

The simplest way of submitting form values is to use the request() method of the form. Since jaces uses REST notation, the controller atrribute of the view widget should be set and the action should be given in the arguments of the request() method.


      <div dojoType="jaces/View" controller="/formSamples">
         <div dojoType="jaces/body/Form">
            A text value:
            <div dojoType="dijit/form/TextBox" valueCol=".field1"></div>
            <div dojoType="dijit/form/Button" onClick="this.getView().getForm().request({action: 'j_echo', fill: true, showResult: true, sync: true});">Click to
               echo the form values</div>
         </div>
      </div>
   

A text value:
Click to echo the form values

Let's explain the other arguments of the request() method.

  1. fill

    forces the form to fill-in the returned values to the form widgets in the json response.

  2. showResult

    displays the returning message in the json response.

  3. sync

    forces the call to stop till the response comes back.

The action written in Grails is given below.


   def j_echo = {
      def field1 = params.field1
      def result = [instance: [field1: "Echo " + field1], message: "Success"]
      render result as JSON
   }

Form Widgets Show Case

This sample has one widget from each kind in the form. Experience how they are managed.

Option 1
Option 2
Click to echo the form values


      <div dojoType="jaces/View" controller="/formSamples/index">
         <div dojoType="jaces/form/MessagePane" level="all"></div>
         <div dojoType="jaces/body/BoundedForm">
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".textBox">Text Box</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" valueCol=".textBox" cellWidth="0" />
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".date1">Date</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.DateTextBox" valueCol=".date1" cellWidth="0" lang="en_US"
                     constraints="{datePattern: 'MM.dd.yyyy'}" defaultTo="2014-07-26" />
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".fruits">Multi Select</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <select data-dojo-type="dijit/form/MultiSelect" valueCol=".fruits" size="4">
                     <option value="AP">Apples</option>
                     <option value="OR">Oranges</option>
                     <option value="PE">Pears</option>
                  </select>
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".checkbox1">Unchecked</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <div dojoType="dijit.form.CheckBox" cellWidth="0" valueCol=".checkbox1"></div>
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".checkbox2">Checked</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <div dojoType="dijit.form.CheckBox" cellWidth="0" valueCol=".checkbox2"></div>
               </div>
            </div>

            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".radio1">Radio Buttons</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  Option 1
                  <div dojoType="jaces.form.RadioButton" cellWidth="0" valueCol=".radio1" value="option1" name="radioGroup"></div>
                  Option 2
                  <div dojoType="jaces.form.RadioButton" cellWidth="0" valueCol=".radio1" value="option2" name="radioGroup"></div>
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".currency">Combo Box</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <div dojoType="jaces.form.ComboBox" class="jacesText" labelAttr="name" valueAttr="id" cellWidth="50"
                     controller="/sampleExchangeRate/index" labelBinding="name" valueBinding="id" valueCol=".currency" sortByLabel="false"
                     optionsSource="org.jaces.side.samples.SampleCurrency"></div>
               </div>
            </div>               
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".editor">Editor</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <div dojoType="dijit.Editor"
                     plugins="[ 'preview', 'print', 'selectAll', 'cut', 'copy', 'findreplace', 'paste', 'delete',
                     'bold', 'italic', 'underline', 'strikethrough', 'foreColor', 'hiliteColor', 'removeFormat', 'fontName', 'fontSize']"
                     valueCol=".editor"></div>
               </div>
            </div>
            <div class="jacesRow">
               <div dojoType="dijit/form/Button" onClick="this.getView().getForm().request({
                        action: 'j_show_case_echo',
                        fill: true, showResult: true, sync: true});">
                        Click to echo the form values</div>
            </div>
         </div>
      </div>
   

Here is the action in Grails.


   def j_show_case_echo = {
      def result = [instance: [fruits: params.list('fruits'), textBox: params.textBox,
         editor: params.editor, date1: params.date1, radio1: params.radio1,
         checkbox1: params.checkbox1, checkbox2: params.checkbox2],
         message: "All values are echoed successfully."]
      render result as JSON
   }
   

jaces/body/BoundedForm

The widget jaces/body/BoundedForm bounds itself to a controller for doing CRUD operations on an entity. It is enough to implement the Messaging Specification on the controller and the widget is flexible to do create/read/update/delete operations.

Click to save the form values


      <div dojoType="jaces.View" controller="/sampleCurrency/index" form="Form">
         <div dojoType="jaces.body.BoundedForm">
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".id">ID:</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" class="jacesId" locked="true" valueCol=".id" />
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".name">Name:</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" class="jacesText" valueCol=".name" />
               </div>
            </div>
            <div class="jacesRow">
               <div dojoType="dijit/form/Button" onClick="this.getView().getForm().update();">Click to save the form values</div>
            </div>
         </div>
      </div>
   

As seen calling this.getView().getForm().update(); is enough to persist the values to the target entity. Details of the jaces CRUD Messaging Specification will be explained later.

jaces/form/StandardToolbar

The widget jaces/form/StandardToolbar puts a toolbar into the view for the common functionalities of the jaces CRUD Messaging Specification. There is not a need for the save button now. Please notice the additional buttons in the toolbar. On the left most side, there are navigation controls for navigation among the records. The navigation controls also show the current position and the total number of records when the controller properly implements the Messaging Specification; However this is not a must part of the Messaging Specification. Optional/mandatory features of the Messaging Specification will be given later.

To the next of the navigation controls, there is a save button. To the next of the save button, the filtering buttons are positioned.

Sheet and form buttons are for switching between the grid and form views. And the next to them, there are sorting buttons for doing ascending ans descending sorting among records based on the selected field. The last button in the toolbar is the delete button.

Click to save the form values


      <div dojoType="jaces.View" gridStyle="height: 200px;" controller="sampleCurrency" form="Form">
         <div dojoType="jaces.form.StandardToolbar"></div>
         <div dojoType="jaces.body.BoundedForm">
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".id">ID:</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" class="jacesId" locked="true" valueCol=".id" />
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesCamesLabel">
                  <label for=".name">Name:</label>
               </div>
               <div class="jacesBiggessInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" class="jacesText" valueCol=".name" />
               </div>
            </div>
            <div class="jacesRow">
               <div dojoType="dijit/form/Button" onClick="this.getView().getForm().update();">Click to save the form values</div>
            </div>
         </div>
      </div>
   

More Form Widgets

Any widget having a get('value') and set('value', ...) can be placed into the view. The below example has a dijit.form.DateTextBox and a dijit.form.SimpleTextarea.


      <div dojoType="jaces.View" gridStyle="height: 400px;" query="{_selector: 'tree'}" controller="/sampleExchangeRate/index" form="Form">
         <div dojoType="jaces.form.ShortToolbar"></div>
         <div dojoType="jaces.body.BoundedForm">
            <input type="hidden" dojoType="dijit.form.TextBox" cellWidth="0" valueCol=".id" />
            <div class="jacesRow">
               <div class="jacesLabel">
                  <label for=".date">Date:</label>
               </div>
               <div class="jacesInputCnt">
                  <input type="text" lang="en_US" dojoType="dijit.form.DateTextBox" defaultTo="2014-07-26"
                     class="jacesStanDate" valueCol=".date" />
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesLabel">
                  <label for=".currency">Currency:</label>
               </div>
               <div class="jacesInputCnt">
                  <div dojoType="jaces.form.ComboBox" class="jacesText" labelAttr="name" valueAttr="id" cellWidth="50"
                     controller="/sampleExchangeRate/index" labelBinding="name" valueBinding="id" valueCol=".currency" sortByLabel="false"
                     optionsSource="org.jaces.side.samples.SampleCurrency"></div>
               </div>
            </div>
            <div class="jacesRow">
               <div class="jacesLabel">
                  <label for=".rate">Rate:</label>
               </div>
               <div class="jacesInputCnt">
                  <input type="text" dojoType="dijit.form.NumberTextBox" constraints="{pattern: '0.0000'}" class="jacesStanDate" valueCol=".rate" />
               </div>
            </div>
         </div>
      </div>
   

Let's explain the view attribiutes that we just met.

  1. actions

    jaces CRUD Messaging Specification uses the action names insert/list/update/delete for CRUD. However if the programmer wants to different names, the actions attribute is used to give the action mapping to the view. Given the map, the view, for example, can use the given name for the list action.

  2. query

    contains constant request parameters that the view adds to every HTTP request. Sometimes there are constant values to be sent to the controller in every action. The query attribute is used to set out those constant values.

  3. gridStyle

    is the style of the grid shown when the view is switched to the grid.

The action written in Grails is given below.

Common Attributes of Form Widgets

  1. valueCol

    is the name of the field to which the widget is bounded.

  2. readOnly

    marks the widget as read only. The widget is locked to prevent editing and even if set the widget value is not posted to the server.

  3. simulated

    The widget stays editable and the widget value is posted to the server. However the bounded field name is not put to the list of comma-separated update fields.

  4. defaultTo

    The widget starts with the given value. If items are retrieved from the list action, the value might over-written tough.

  5. nullValue

    represents the null value and used mainly for combo boxes.

  6. cellWidth

    gives the column width when the wview is switched to the grid view.

jaces/social/DojangoGrid

Sometimes listing items in a grid is not flexible enough to do the work and a more dynamic feature is needed. An example is an mail inbox. A grid can not satisfy a developer when he is designing an inbox. When those dynamic features are needed, jaces/social/DojangoGrid can be used to create the list. Since it is also using jaces CRUD Messaging Specification, the switching between the grid solution and dojango grid solution only needs UI change.

The below sample list exchange rates with their currency icons in floating boxes.


      <div dojoType="jaces.View" query="{_selector: 'tree'}" controller="/sampleExchangeRate/index" form="Form">
         <div dojoType="jaces.social.DojangoGrid" additivity="false">
            <input type="hidden" dojoType="dijit.form.TextBox" valueCol=".currency.name"> <input type="hidden" dojoType="dijit.form.TextBox" valueCol=".rate">
            <div dojoType="jaces.social.DojangoRowTemplate"><!--
         {% load dojox.dtl.contrib.dijit dojox.dtl.contrib.dom dojox.dtl.contrib.data %}
            <div class="box">
               <img title="{{item.currency.name }}" src='http://s.xe.com/20100208/themes/xe/images/flags/big/{{ item.currency.name|lower }}.png'>   {{item.rate}}
            </div>   
       --></div>
         </div>
      </div>
   

If you want a classic tabular view, you are free to combine django templates with HTML TABLE. Note the position of the template and the HTML tags. When this style is used, the TABLE tag must have a THEAD and TBODY. The tbody tag in turn must contain the row template.

Currency Name Currency Flag Currency Rate



      <div dojoType="jaces.View" query="{_selector: 'tree'}" controller="/sampleExchangeRate/index" form="Form" class="funtable" maxRecords="5">
         <div dojoType="jaces.social.DojangoGrid" additivity="false">
            <input type="hidden" dojoType="dijit.form.TextBox" valueCol=".currency.name"> <input type="hidden" dojoType="dijit.form.TextBox" valueCol=".rate">
            <table>
               <thead>
                  <th>Currency Name</th>
                  <th>Currency Flag</th>
                  <th>Currency Rate</th>
               </thead>
               <tbody>
                  <!--
               {% load dojox.dtl.contrib.dijit dojox.dtl.contrib.dom dojox.dtl.contrib.data %}
                  <tr>
                     <td>{{item.currency.name }}</td>                     
                     <td><img title="" src='http://s.xe.com/20100208/themes/xe/images/flags/big/{{ item.currency.name|lower }}.png'></td>   
                     <td>{{item.rate}}</td>   
                  </tr>
                -->
               </tbody>
            </table>
            <div dojoType="jaces.form.Navigator"></div>
         </div>
      </div>

   

Selectors

When the fecthed data is JSON Marshalling of an object tree, we call it "tree". When the fecthed data is JSON Marshalling of tabular data, we name it "flat". Even tough the dot notation resembles a tree, still using the dot notation for tabular data has a big advantage. When the data is submitted back to the server, the data might be applied to an object tree. jaces is able to work with tree and flat formats. A selector is the bridge between JSON and form widgets for transfering the data back and forth. When properly set, it allows widgets to play with values in JSON regardless if it is flat or tree. The selector is given an attribute of the query attribute of jaces/View.



      <div dojo-type="jaces/View" query="_selector: 'tree'">...</div>

      <div dojo-type="jaces/View" query="_selector: 'flat'">...</div>


   

Form Widgets

Some dijit widgets do not directly support what jaces wants to achieve. On those cases, jaces opted to have its extended dijit widgets.

Extended dijit widgets are

  1. jaces/form/ComboBox

    extends dijit/form/ComboBox . Combo boxes are very special form elements. Original design of combo boxes is very far away from filling today's user requirements. Dijit takes more steps to filling those requirements; however it falls short on separating the value/label list of the combo and the value/label shown on the form. jaces/form/ComboBox separates them through the following attrbiutes.

    labelAttr is the name of the field in the populating json message representing the label field.

    valueAttr is the name of the field in the populating json message representing the value field.

    labelBinding is the name of the form field representing the label.

    valueBinding is the name of the form field representing the value.

    In addition, the widget has an optionsFilter attribute for passing additional criteria to the action returning the list. optionsFilter may contain constant expressions as well as java script expression enclosed inside { and } . This expression feature might be used for example when a combo box content changes based on another combo box value.

  2. jaces/form/CheckBox

    is for providing a jaces complaint check box.

  3. jaces/form/TriStateCheckBox

    is for providing tri state.

  4. jaces/form/RadioButton

    dijit/form/RadioButton is not designed for bounding to a form field. Instead a jaces developer needs to use jaces/form/RadioButton .

More Advanced Samples

More advanced samples are available on the demo page at http://firmeyes.com/brd/t/eyJpIjo0MjQwLCJtIjoiViJ9.

Messaging Specification

jaces Messaging Specification is used to communicate with the server. Once the server component imlements mandatory sections of the specification, the widget can start talking to the server component.

URL Specification

The url should be in the form "context/controller/action".

Request Specification

A request is an http post/get. The request parameters are sent as http parameters. The specification has some pre-defined parameters while others are request specific. Pre-defined parameters are:

      
   
         /**
          *    Comma-separated list of columns to which the widget that sends the request is bounded.
          * The list is used to determine the list of the values
          * to be returned to the client in the response
          */
         static FILTER_SELECT = "_filter_Select"
         
         /**
           * Where clause if a filter is sent from the page
          */
         static FILTER_WHERE = "_filter_Where"
         
         /**
          * Values of the filter parameters if a filter is sent from the page
       */
         static FILTER_PARAM = "_filter_Param"
         
         /**
          *    Comma-separated list of columns to which widgets are bounded.
          * The list is used to determine the list of the values
          * to be returned to the client in the response
          */
         static SELECT_COLUMNS = "_select_Columns"
         
         /**
         *    Comma-separated list of update columns to which widgets are bounded.
         * The list is used to determine the list of the values
         * to be applied to the target entity.
         */
         static UPDATE_COLUMNS = "_update_Columns"
         
         /**
          * First result position if the pagination is based on the record position
          */
         static FIRST_RESULT = "_first_Result"
         
         /**
          * Maximum number of records that is fetched at one time
          */
         static MAX_RESULTS = "_max_Results"
         
         /**
          *
          * Sorting field
          */
         static SORT_FIELD = "_sort_Field"
         
         /**
           * Sorting takes only the values ASC or DESC.
          */
         static SORT_TYPE = "_sort_Type"
         
         /**
          * Sorting clause
          */
         static SORT_STATEMENT = "_sort_Statement"
         
         /**
          * If a creation request is sent from a subview and the subview is linked
          * to a parent view via a relation, the domain class of the parent view
          * is sent in the parameter _parent_Class.
          */
         static PARENT_CLASS = "_parent_Class"
         
         /**
         * If a creation request is sent from a subview and the subview is linked
         * to a parent view via a relation, the name of the link
         * from the domain class of the subview to the domain class of
         * the parent view is sent in the parameter _parent_Link.
         */
         static PARENT_LINK = "_parent_Link"
         
         /**
         * If a creation request is sent from a subview and the subview is linked
         * to a parent view via a relation, the value of the unique identifier
         * of the subview to the domain class of the parent view is sent
         * in the parameter _parent_Id.
         */
         static PARENT_ID = "_parent_Id"
         
         /**
         * If a creation request is sent from a subview and the subview is linked
         * to a parent view via a relation, the name of the unique identifier
         * of the subview to the domain class of the parent view is sent
         * in the parameter _parent_Id.
         */
         static PARENT_IDENTIFIER = "_parent_Identifier"
      
         /**
         * Default result position
         */
         static DEFAULT_FIRST_RESULT = 0
      
         /**
         * Default Maximum number of records that is fetched at one time
         */
         static DEFAULT_MAX_RESULTS = 15
      
         /**
          *
          * The selection style of the resulting list
          * For the moment there are two options: flat, tree (default option)
          *
          */
         static SELECTOR = "_selector"
         
         static TREE_SELECTOR = "tree"
         
         static FLAT_SELECTOR = "flat"
         
      

Response Specification

A response is an JSON object having pre-defined attributes.


   
         /**
          *
          * Items returning in the response if the request a list request.
          *
          */
         static ITEMS = "items"
         
         
         /**
          *
          * The resulting entity that is updated/deleted/inserted
          * if the request is an insert/update/delete request
          *
          */
         static INSTANCE = "instance"
      
         /**
          *
          *   The number of items found when the request a list request.
          *   Be aware that the number should indicate the total number of items
          *   matching the search criteria, not the number of items returning
          *   in the resulting page if the pagination is used.
          *
          */
         static ROWS_COUNT = "rowsCount"
         
         /**
          *
          *    Many times finding the total number of items matching the search criteria
          *    is a costly query. For those cases, the server components may opt to
          *    send an indicator telling there are more items instead of returning an
          *    exact count.
          *
          *
          */
         static HAS_MORE_RECORDS = "hasMoreRecords"
      
         /**
          *
          *    If the pagination is supported and the offset number that jAces sends
          *    to the server component does not make very much sense, the server
          *    component might put a book mark into the response. jAces sends this book mark
          *    to the server component when the next page is requested. The server component
          *    can then use the book mark to place the cursor to the requested offset
          *    in the result set.
          *
          *   Sample:
          *
          *      bookmarks: {last_id: "000123456"}
          */
         static BOOKMARKS = "bookmarks"

      
         
         /**
          *
          * Indicated the identifier field name of the entity.
          *
          */
         static IDENTIFIER_ATTR = "identifierAttr"
         
         
         /**
          *
          * Indicated the class name of the entity.
          *
          *
          */
         static CLAZZ = "clazz"      
      
      

Parent - Child Views

Views can be nested to create parent-child effects. When a view is positioned into another view, the parent view restarts the child views at every record navigation to let them refresh the contents. The child views, in turn, implicitly gets the identifier value of the parent view and puts into every insert/update request. The server side can use those values to associate the inserted entity with the parent entity. When the server side implementation is kind of a dynamic software capable of inserting/updating any entity, views help you by sending the child entity (class) name and the association name from the child entity to the parent entity. The association name is set to the child view through the linkToParent attribute. The corresponding request fields are explained in the messaging specification section. Please refer to the fields _parent_Class, _parent_Link, _parent_Id and _parent_Identifier for details.

The sample below demonstrates the explained features.


      <div dojoType="jaces.View" gridStyle="height: 200px;" controller="/sampleCurrency/index" form="Form">
         <div dojoType="jaces.form.StandardToolbar"></div>
         <div dojoType="jaces.body.BoundedForm">
            <input type="hidden" dojoType="dijit.form.TextBox" cellWidth="0" valueCol=".id" />
            <div class="jacesRow">
               <div class="jacesLabel">
                  <label for=".name">Name:</label>
               </div>
               <div class="jacesInputCnt">
                  <input type="text" dojoType="dijit.form.TextBox" valueCol=".name" />
               </div>
            </div>
            <div dojoType="jaces.View" gridStyle="height: 200px;" query="{_selector: 'tree'}" linkToParent="currency"
               controller="/sampleExchangeRate/index" form="Grid">
               <div dojoType="jaces.form.ShortToolbar"></div>
               <div dojoType="jaces.body.BoundedForm">
                  <input type="hidden" dojoType="dijit.form.TextBox" cellWidth="0" valueCol=".id" />
                  <div class="jacesRow">
                     <div class="jacesLabel">
                        <label for=".date">Date:</label>
                     </div>
                     <div class="jacesInputCnt">
                        <input lang="en_US" type="text" dojoType="dijit.form.DateTextBox" cellWidth="120"
                           defaultTo="2014-07-26" class="jacesStanDate" valueCol=".date" />
                     </div>
                  </div>
                  <div class="jacesRow">
                     <div class="jacesLabel">
                        <label for=".rate">Rate:</label>
                     </div>
                     <div class="jacesInputCnt">
                        <input type="text" dojoType="dijit.form.NumberTextBox" cellWidth="120" constraints="{pattern: '0.0000'}" class="jacesStanDate" valueCol=".rate" />
                     </div>
                  </div>
               </div>
            </div>
         </div>
      </div>
   

Custom Building dojo together with jaces

Basics

If still not knowing, please refer to dojo custom build reference to get to know about dojo custom builds.

jaces source code has already a custom build profile. If there is a need to rebuild the whole package, please follow the steps below:

  1. Modify /jaces/web-app/js/release.profile.js to create your layers as you require.
  2. Run /runCustomBuild.bat to run the build.

View API

Public Attributes



   // controller: String
   //      The path to the controller that the view is connected.
   //      Each view is connected a single controller to simply the application developed on jaces.
   controller: '',

   //   criteria: [private] Object
   //      Keeps the form values to be submitted to the server
   criteria: null,
   
   //   config: [private] Object
   //      Holds the view's dynamic attributes.
   config: null,

   // actions: object
   //      Action mappings of the actions that the view may submit.
   //      The scaffolding has for predefined actions; list, delete, insert, update.
   //      If the controller implements these actions under different names,
   //      actions mapping might be used to map the default action to the proper implementation.
   //   example:
   //      actions="list: 'list', insert: 'insert'"
   actions: '',

   //   form: String
   //      determines whether the bounded form will be used
   //      as the form implementation or the view will start in grid.
   //      
   //      Possible values are Grid and Form.
   //      Form will make jaces.View to use the bounded form whereas
   //      Grid will make jaces.View to use the Grid view.
   //       
   form: '',

   // sorting: String
   //      Sort is the initial sorting clause. For example the following setting sorts record
   //      by the column name in descending order.
   //
   //      Example:      
   //      |   sorting="name desc"
   //      
   //      The configuration might be given with a hidden widget placed into the view.
   //      Example:
   //      |   <input type="hidden" dojoType="dijit.form.TextBox" id="view.sorting" valueCnf="name desc">
   //      
   sorting: '',
   
   // filtering: String
   //       Filter is the initial where clause to limit the records that will be displayed when the view opens.
   //      For example the following setting only shows records whose name contains the letters "Jo".
   //
   //      Example:      
   //      |   filtering="name like 'Jo%'"
   //
   //      The configuration might be given with a hidden widget placed into the view.
   //      Example:
   //      | <input type="hidden" dojoType="dijit.form.TextBox" id="view.filtering" valueCnf="name like 'Jo%'">
   //
   //      The value can be any valid GORM where clause. You may also refer to fields in other forms by using jaces expressions.
   //      To get information about jaces expression, refer to the jaces expressions article.
   filtering: '',
   
   // query: Map
   //      Constant request parameters that the view adds to every HTTP request.
   //      Sometimes there are constant values to be sent to the controller in every action.
   //      The <code>query</code> attribute is used to set out those constant values.
   query: null,

   //   maxRecords: integer
   //      Pagination size.
   //      Maximum number of records that the connected data store returns at a time.
   //      The value is the page size and is being used to manage pagination logic.
   //      If the view is connected to a jaces scaffolded controller, you can put any value
   //      since the template handles varying number of page sizes.
   //      Otherwise, the number depends on the data store’s decision.
   maxRecords: '',
   
   //   rowsCount: integer
   //      The number of items found when the request a list request.
   //      Be aware that the number should indicate the total number of items
   //      matching the search criteria, not the number of items returning
   //      in the resulting page if the pagination is used.
   rowsCount: 0,
   
   // showToolbar: boolean
   //      If false, jaces hides the tool bar.
   //      Defaults to true
   showToolbar: '',
   
   // showNavigator: boolean
   //      If true, jaces puts navigation buttons to the bottom of the view.
   showNavigator: '',
   
   // isDeletable: boolean
   //      indicates whether records can be deleted on the target view
   //      defaults to true
   isDeletable: '',

   // isInsertable: boolean
   //      indicates whether new records can be created in the target view.
   //      defaults to true   
   isInsertable: '',

   // isEditable: boolean
   //      indicates whether records can be edited in the target view.
   //      defaults to true   
   isEditable: '',
   
   //   linkToParent: String
   //      The GORM relation’s name from the subview to the parent view. The value is used to create master-detail views.
   linkToParent: '',

   //   insertMode: boolean
   //      When set to true, the form does not fetch records.
   //      Reverts to true when switched between the form and grid view
   insertMode: false,

   //   hiddenMode: boolean
   //      When set to true, the view does not start up the form.
   hiddenMode: false,
   
   //   fetchAllowed: boolean
   //      When set to true, the form is allowed to fetch
   fetchAllowed: true,
   
   //   indirectSelection: boolean
   //      The attribute is connected to the grid’s EnhancedGrid.indirectSelected attribute and
   //      If true puts a check box into the grid.
   indirectSelection: false,
   
   //   gridStyle: CSS string
   //      CSS style is applied to the grid when the view is switched to the grid.
   gridStyle: '',

   //   gridClass: CSS class
   //      CSS class is applied to the grid when the view is switched to the grid.
   gridClass: ''


Public Methods




   getView: function(){
      // summary:
      //      The parent view if exists. Null otherwise.

   restart: function(){
      //   summary:
      //      Reconfigures the view by looking up child widgets.
      //      The method is used as the view starts up and might be
      //      used later on too when a child widget is added to the
      //      view after the start up.

   reset: function(args){
      //   summary:
      //      Reset the form for entering a new record.

   refresh: function(criteria){
      //   summary:
      //      Makes the view go into the initial state by resetting the navigation state
   
   requery: function(criteria){
      //   summary:
      //      if the search criteria is given the view is reloaded with the given
      //      criteria. Otherwise the form is gathered to prepare the search criteria
      //      and this is reloaded.

   filter: function(){
      //   summary:
      //      The view goes into the filter mode
      //      where the user can enter the search criteria into the form.
   
   applyFilter: function(){
      //   summary:
      //      The entered values are applied to do the filtering.
   
   removeFilter: function(){      
      //   summary:
      //      The view goes the initial state, clering all filtering
      //      criteria.
   
   sort: function(sortType){
      //   summary:
      //      Sorts the records either ascending or descending.
      //      The direction is given as the method parameter.
      //      The sort field is the last focused field.   

   persistState: function(){
      //   summary:
      //       Convenience method for update.
      //      The method first calls update on children if they are dirty.   
      //   


Events




   
   onOpen: function(){
      // summary:
      //      Called after the form fetch completes, all fetched items are appended and the navigation
      //      state is settled.
      // tags:
      //      callback

   
   onCurrent: function(){
      // summary:
      //      Called when an item becomces active.
      //      For read-only lists, like dojango grid, the form does not have to make this callback. However
      //       it is mandatory for read/write forms and grids.
      // tags:
      //      callback

   
   

Form API

Public Attributes



   //   recycleable: true
   //      if true, forces the view to recreate the form object
   //   before every refresh.
   recycleable: true,
   
   //   lateGathering: boolean
   //      forces the form to do gathering before sending requests to the
   //   controller action.
   lateGathering: true,
   

   //   additivity: boolean
   //      If false, all form items are destroyed after every pagination.
   //      If true, form items remain in the course of pagination.
   additivity: true,   

Public Methods




   getField: function(name){
      //   summary:
      //       Accessing to the field value of the item
      //      Deprecated. Use getValue instead.

   getValue: function(name){
      //   summary:
      //       Accessing to the field value of the item
   
   setValue: function(name, value){
      //   summary:
      //       Setting the field value of the item

   getCurrentItem: function(){
      //   summary:
      //      Current data item being shown/edited in the form.
   
   getCurrentData: function(){
      //   summary:
      //      Current data being shown/edited in the form.
   
   isDirty: function(){
      //   summary:
      //      Lets the form user know whether the form contains values
      //      that have not been persisted.
      //
      //      Defaults to false
      //
      //      The child form may override the behaviour as it needs.

   validate: function(){
      // summary:
      //      returns if the form is valid - same as isValid - but
      //      provides a few additional (ui-specific) features:
      //
      //      1. it will highlight any sub-widgets that are not valid
      //      2. it will call focus() on the first invalid sub-widget
   
    fill: function(defaults){
       //   summary:
       //      Populates the current item's data to the form
       //      widgets using the defaults given as the
       //      method argument.
       //

   gather: function(item, checkNull){
      //   summary:
      //      gathers values from the form widgets.   
   
   newItem: function(){
      //   summary:
      //      creates a new item.   
   
   remove: function(params){
      //   summary:
      //      sends a remove request to the delete action,
      //      removes the record from the items, and moves
      //      the navigation to the next item available.

   update: function(params){
      //   summary:
      //      deprecated, use <code>persistState(params)</code> instead.
   
   persistState: function(params){
      //   summary:
      //      sends an update reques to the update/insert action.   
   
   persistWhenDirty: function(params){
      //   summary:
      //      The dirty form is persisted after the user leaves the form
      //      This class is not bounded to a scaffolded controller.
      //      therefore persist method is blank.
      //      Sub classes overwrite it as they want.
      //
   
   gatherCriteria: function(){
      //   summary:
      //      Iterates through the form widgets, gathers their values and puts into
      //      view's criteria attributes to be used for listing fetches.

   requery: function(criteria){
      //   summary:
      //         

   request: function(args){
      //   summary:
      //      Passes the values gathered from the form widgets to the backing controller action.
      //   
      //   
      //   action:
      //      action's name to be executed on.
      //   callback:
      //      callback function.
      //   showResult:
      // true/false   
      //   fill:
      //      makes the method fill the view widgets
      //      with the values returned from the controller
      //   fillAll:
      //      makes the method fill the widgets with the values returned from the controller
      //      and reset a widget if no value comes back for it.
      //   method:
      //      POST, GET
      //      Note: xdomain calls do not support POST. GET is added to the parameters
      //      in order to support xdomain calls.
      //
      //      In addition to the above arguments, all arguments of xhr are also available here.

   dynaRequest: function(params){
      //   summary:
      //      Passes the values in <code>params</code> to the backing controller action.
      //
      //   action:
      //      The target action's name that is executed on the controller.
      //   refresh:
      //      If true, a listing fetch is invoked after the request is made.
      //
      //      In addition to the above arguments, all arguments of xhr are also available here.      
   
   getView: function(){
      //   summary:
      //      returns the view of the form.   

Server-side components for Grails developers

Scaffolding Controller

jaces server-side components for Grails allow binding forms to GORM entities. With the help of an enhanced version of Grails scaffolding, jaces forms have update/delete/insert/list server-side features with a simple controller having two lines of code. A sample controller is given below.



package org.jaces.side.samples

class SampleCurrencyController {

   
   def scaffold = true
   def scaffoldingService
   
}

The line

def scaffold = true

decorates the controller with update/delete/insert/list methods for the entity SampleCurrency.

The line

def scaffoldingService

is required by the scaffolding methods and is mandatory.

Scaffolding Plugins

jaces allows developers to hook into the following points through the use of Scaffolding Plugin Services.

  1. beforeDelete
  2. afterDelete
  3. beforeList
  4. afterList
  5. afterListFlatenned
  6. beforeInsert
  7. beforeInsertInstancePopulated
  8. afterInsert
  9. afterInsertAndResultFlattened
  10. beforeUpdate
  11. beforeUpdateInstancePopulated
  12. afterUpdate
  13. afterUpdateAndResultFlattened
All hooking classes must
  1. at least implement one of those methods.
  2. be created as Grails services under /grails/services
  3. be given in the plugin array of the scaffolding controller
The below sample might be used for logging updates on watched entities to an audit table.



package org.jaces.side.audit

import java.util.Map

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession

import org.jaces.scaffolding.Context;
import org.jaces.scaffolding.SoftPluginCategory
import org.jaces.side.portal.Parameters;
import org.jaces.side.security.Right;
import org.springframework.web.context.request.RequestContextHolder;

class AuditPluginService extends RequestContextHolder{

   def auditService

   public void beforeUpdate(Context c){
      record(Operation.U, c)
   }

   public void beforeInsert(Context c){
      record(Operation.I, c)
   }

   public void beforeDelete(Context c){
      record(Operation.D, c)
   }

   public void beforeList(Context c){
      record(Operation.Q, c)
   }

   private void record(Operation o, Context c) {
      
      def targetId = c.reply == null ? c.instance == null ? null : c.instance.id : c.reply.id
      def targetName = c.controller.getClass().getName().split("\\.")
      targetName = targetName[targetName.length - 1]
      targetName = targetName.substring(0, Math.min(targetName.size(), 20))
      
      auditService.log(...)
   }

   private HttpServletRequest getRequest() {
      return currentRequestAttributes().request
   }

   private Map getParams() {
      return currentRequestAttributes().params
   }
}


The plugin is listed in the plugin attribute of the watched controllers.



class CustomerController {

   def scaffold = Customer
   def scaffoldingService

   def plugins = [
      "auditPluginService"
   ]

   ...

Installing Plugin and Opening the Demo Page

  1. Download https://code.google.com/p/jaces/source/browse/trunk/grails-jaces-1.8.0.228.zip
  2. Create an empty Grails project by running create-app EmptyAppName.
  3. Install jaces by running install-plugin PATH_TO_DOWNLOADED_ZIP\grails-jaces-1.8.0.228.zip
  4. Comment the below line in grails-app/conf/BuildConfig.groovy


                 //runtime ":resources:1.1.6"
             

  5. Add the below line to grails-app/conf/Config.groovy. This is line is needed because JSON renderer should render a valid Javascript Date in responses.


                
                grails {
                   converters { json.date = "javascript" }
                }
                   
             

  6. Run the application
  7. Open the page http://localhost:8080/EmptyAppName/jaces/t_help to see the demo page.

How is it embedded?

jaces is embedded into GSPs through its GSP layout. Examine /jaces/grails-app/views/jaces/help/t_help.gsp for a sample.


   
         meta name="layout" content="jaces_empty"
   
   

Embedding jaces into Spring MVC Applications


   
      Coming soon...
   
   

References

  1. dojo
  2. Django Framework
  3. Dojango @ https://code.google.com/p/dojango/
  4. jaces Source @ https://code.google.com/p/jaces/
  5. Grails