Behaviors - quandis/qbo3-Documentation GitHub Wiki
QBO's javascript library leverages the Mootools framework, which includes a very powerful Behaviors pattern to add dynamic functionality with simple HTML markup. Behaviors is a framework for automating binding of javascript-enabled functionality to HTML elements, with the following advantages:
- multiple behaviors can be attached to 1 element,
- initialization can be deferred until a user clicks on the element (e.g. don't create the menu until you need to)
- onCleanup methods prompt developers to "clean up" memory
For example:
<input type="text" name="PaidDate" data-behavior="Date" data-date-options={"some options"}>will automatically bind the PaidDate form element to offer a date picker UI when a user clicks on it.
QBO offers many behaviors, including:
| Behavior | Description |
|---|---|
DatePicker |
Render a date picker UI associated. |
Depend |
Renders or enables form elements based on the data conditions of other elements. |
Dropdown |
Renders a dropdown list by fetching data from an AJAX API call. |
Filter |
Adds or removes parameters from AJAX calls based on user actions. |
Labels |
Renders labels for all elements (usually rows) via a single AJAX call. |
Math |
Adds or multiplies values from form elements. |
MatrixLookup |
Evaluates a matrix-based rule. |
MethodSignature |
Executes a QBO method signature, and makes the results globally available as JSON. |
MethodElement |
Render UI to enable a user to configure a method signature. |
ObjectBind |
Binds a div tag to a QBO object, to enable rendering of method signatures via AJAX. |
OrderBy |
Enables clickable column headers in a table to change sort order. |
Paginate |
Renders pagination at the bottom of a table of data. |
Popup |
Renders modal dialogs from hidden elements or AJAX calls. |
Remember |
Caches HTML content using localStorage to improve performance. |
Selectable |
Makes table rows (tr tags) selectable by clicking anywhere on the row. |
Visualization |
Renders a chart from the Google Visualization library. |
Behaviors are implemented as follows:
Behavior.addGlobalFilter(function(element, api) {
var something = new SomeObject(...);
var myOption = api.get('someOption');
var options = api.getAs({
optionA: String,
optionB: Number,
optionC: String
});
alert(options.optionA);
... extra code ...
api.onCleanup(function(){
// remote events, extra DOM elements created, etc. to manage memory
});
});If DOM content is created dynamically (either by an AJAX call or via javascript), one should apply behaviors with:
qbo3.behavior.apply(element);For example, creating a new data control with javascript could be done with:
myDate = new Element('input', {
'type': 'text',
'name': 'myDate',
'data-behavior': 'Date',
'data-date-options': {some options here}
}).inject(someLocation);
qbo3.behavior.apply(myDate);HTML select elements can be rendered from the database using the Dropdown behavior:
<select id="Status" name="Status" data-behavior="Dropdown" data-dropdown-options="{{
'url': 'Application/ObjectStatus.ashx/List?output=Json',
'jsonEval': 'ObjectStatusCollection.ObjectStatusItem',
'value': 'ObjectStatus',
'text': 'Label',
'selected': 'MyCurrentValue',
'data': {{ 'Object': 'Organization', 'cache': '300' }} }}">
<option value="">--</option>
</select>The options passed to the behavior comprise:
| Option | Description |
|---|---|
url |
The URL to call to populate the select element. |
jsonEval |
A json expression that, when evaluated, returns an array of 'option' objects corresponding to each select option tag. |
value |
The option object property to use as the option element's value. |
text |
The option object property to use as the option element's HTML. |
data |
Parameters to pass to the url. |
The qbo3.DropdownType object contains the standard options for commonly used drop down lists, including:
- ObjectStatus
- ObjectType
- State
To leverage these predefined options, specify a 'type' in the behavior options. For example:
<select id="Status" name="Status" data-behavior="Dropdown" data-dropdown-options="{{
'type': 'ObjectStatus' ,
'data': {{ 'Object': 'Loan' }} }}">
<option value="">--</option>
</select>The ObjectBind behavior is used to bind an HTML element (typically a div tag) to some dynamically generated content. It is the most commonly used method for fetching data via AJAX from QBO.
<div id="search" class="tab-pane" data-behavior="ObjectBind" data-objectbind-options="{{ 'class': 'qbo3.DecisionObject', 'method': 'Search', 'render': false, 'listen': ['search'], 'cacheKey': 'Decision.Home.Search' }}">.</div><div id="select" class="span12" data-behavior="ObjectBind" data-objectbind-options="{{ 'class': 'qbo3.ContactObject', 'method': 'Select', 'remember': false, 'data': {{'ID': '{ContactID}' }} }}">.</div><div data-behavior="ObjectBind" data-objectbind-options="{{ 'class': 'qbo3.SmartWorklistMemberObject', 'cacheKey': 'SmartWorklistHome-Current', 'method': 'Dashboard', 'listen': ['refreshAll'], 'data': {{'Dimension': 'SmartWorklistID', 'Transform': 'SmartWorklistMember.Dashboard.xslt', 'SortBy': 'SmartWorklist'}} }}">.</div>The key ObjectBind options include:
| Option | Desccription |
|---|---|
class |
(Required) Name of the qbo3.AbstractObject-derived class to use to render the data. |
method |
The name of the method or operation to execute when rendering the data |
data |
Parameters to pass to the server when making the AJAX call |
render |
(Defaults to true) Whether to invoke the class/method immediately (if false, the panel will 'wait' for some other javascript event to cause the panel to make the AJAX call). |
listen |
An array of events to listen for on the behavior api; when any of these events are raised, the panel will make an AJAX call. |
remember |
(Defaults to true) Whether to cache the panel's content in local storage, so when the user revisits the page, the content is fetched from disk instead of over the wire. |
cacheKey |
Name of the key to store the panel's content as. |
maxCacheDuration |
Maximum length content may remain in cache; once this time is exceeded, the cached content will be ignored and refreshed from the server. |
In the examples above:
- The Decision Search panel will call the URL
Decision/Decision.ashx/Search - The Contact Select panel will call the URL
Contact/Contact.ashx/Select?ID={some ContactID} - The Smart Worklist Dashboard panel will call the URL
Decision/SmartWorklistMember.ashx/Dashboard?Dimension=SmartWorklistID&Transform=SmartWorklistMember.Dashboard.xslt&SortBy=SmartWorklist
Note that the ObjectBind behavior does not dictate what is in the data option; this depends on the method/operation being called.
The Labels behavior will display label (CollectionMember) rows based on an AJAX lookup of one or more records. It is designed to work with both select and search panels, using just one AJAX call regardless of the number of source objects being looked up.
For an select panel, add a div tag like this:
<div class="control-group">
<label>Labels</label>
<div class="controls">
<span>
<div data-behavior="Labels" data-labels-options="{{'object': 'Organization'}}" data-label="{OrganizationID}"/>
</span>
</div>
</div>Note that the div tag containing the behavior includes both data-labels-options and data-label attributes.
For a search panel, add the label behavior to the tbody, and a data-label tag to each row:
<tbody data-behavior="Selectable Labels" data-labels-options="{{'object': 'Organization'}}" data-selectable-options="...">
...
<tr>
...
<td>
<div data-label="{OrganizationID}"/>
</td>
...
</tr>
...
</tbody>Options comprise:
| Option | Default | Description |
|---|---|---|
object |
(Required) The object the labels are bound to (e.g. Organization, Contact, Person, Loan, etc.). | |
selector |
div[data-label] |
The css selector used to determine what IDs to get labels for. |
tokenHtml |
{Collection} <i class="icon-remove icon-white"></i> |
The html to render inside the token create for a label. |
tokenCss |
badge |
The css class to add to the token. |
activeCss |
badge-success |
The css class to add to the token if the label is active. |
inactiveCss |
The css class to add to the token if the label is not active. | |
deleteSelector |
i.icon-remove |
The css selector triggering a delete. |
Popup windows are typically used to perform quick editing functions on web pages without redrawing the entire web page. Examples include:
- Adding a new item from a search or list panel
- Modifying an existing item from a search or list panel
- Labeling items with a collection
- Deleting items (in which case, the popup merely confirms the user's desire to Delete items)
For such purposes the qbo3.Popup class and corresponding Popup behavior is used to render the popup. It can be used as follows:
// Create a popup with script directly
// Note we store and cache popups with a global Popup object: qbo3.Popups
var popup = qbo3.Popups.get('myPopupDivTag');
// Create a popup from a remote URL
qbo3.Popups.get('Theme.ashx/Render?Transform=Templates/Contact/CollectionMemberInsert.Popup.xslt');
// Create a popup from a remote URL that will be reused
// Step 1: define a key for the URL
qbo3.Popups.defineKey('MyReusablePopup', 'Theme.ashx/Render?Template=Templates/Contact/CollectionMemberInsert.Popup.xslt&cache=12000');
// Step 2: open the popup via the key
qbo3.Popups.get('MyResuablePopup');
// Create a popup from within a qbo3.AbstractObject-derived class to show information only
qbo3.getObject('MyObjectContainer').popup('Describe');
// Create a popup from within a qbo3.AbstractObject-derived class to send data to the server, and refresh the panel
qbo3.getObject('MyObjectContainer').popup('TagInsert', {method: 'CollectionMemberInsert'});
// Create a popup from within a qbo3.AbstractObject-derived class to send data to the server, and display the results in the panel
qbo3.getObject('MyObjectContainer').popup('SmartSearch', {method: 'SmartSearch', invoke: 'html'});
// Pass form data to the method being called, via the popup.
qbo3.getObject('MyObjectContainer').popup('Confirm', {method: 'MyMethod', formData: {'Foo': 'Bar'}});
// Define commonly used popups
qbo3.Popups.define({
'SmartSearch': 'Theme.ashx/Render?Transform=Templates/Application/SmartSearch.Popup.xslt&cache=12000',
'TagInsert': 'Theme.ashx/Render?Transform=Templates/Contact/CollectionMember.Popup.xslt&cache=12000'
});First and foremost, popups can be rendered simply by render some HTML tag (usually a div tag) with a hidden class. Calling qbo3.Popups.get() will render the popup with effects, based on the BootStrap UI. This is simple, straight-forward, and a variation on a common javascript library theme: show modal popups.
In QBO however, we frequently wish to show fundamentally the same popup across multiple pages or panels. For example, the notion of adding a bunch of items to a Collection may be used for a list of Organizations, Contacts, Documents, Messages or Tasks. Rather than including the HTML markup for adding items to a collection in the Organization, Contact, Document, and Message rendering code, we define a key in qbo3.Popups, from which all these panels can reference it. The components involved include:
-
Bootstrap.Popup.js(part ofmootools.js):- one of many slick modal popup javascript classes (this is a behavior)
- if you want a different modal popup class, you can wrap it with
qbo3.Popup.js, and still leverage the rest ofqbo3.Popupfunctionality.
-
qbo3.Popup.js:- defines the
qbo.Popupclass - defines the
Popupbehavior - defines commonly used Popup URLs (e.g. 'TagInsert': CollectionMember.Popup.xslt)
- defines the
-
{Project}.js: (project-specific javascript files, such asqbo.Contact.js,qbo.Message.js, etc.) defines project specific Popup URLs -
qbo3.AbstractObject:- popup(element, options): shows a popup, binding the popup's save and cancel methods to the popupSave and popupCancel,
- popupSave(data): accepts data from the popup, and invokes a method with the data, then hides the popup
- popupClear(): hides the popup
-
Theme.ashx: the Render method renders an XSLT without making any database calls directly
Often, popups are used as dialogs prior to executing some AJAX call, including:
- Deleting items
- Copying templates
- Prompting for custom parameters to a statement or method
The qbo3.AbstractObject javascript class provides popup, popupSave, popupCancel, and popupClear methods. Usually, all you will need to use is popup.
Deleting items is usually accomplished from a list of records (e.g. a Search page), by adding a menu item like this:
<li onclick="qbo3.getObject(this).popup('Confirm', {{method: 'Delete', addIds: true }});">Items to note:
-
qbo3.getObject(this)will return a javascript class derived fromqbo3.AbstractObject(e.g. aqbo3.MessageObject) -
popup(element, options):-
elementis the name of an id tag, an entry in theqbo3.Popupsobject, or a URL -
optionscontrols how theqbo3.AbstractObjectdeals with the popup, including:-
method: name of the method to call -
addIds: iftrue, the method called will include form elements from the popup, as well asIDs from the calling panel
-
-
Copying a template is usually accomplished from a Summary page, by adding a menu item like this:
<li onclick="qbo3.getObject(this).popup(
'Theme.ashx/Render?Transform=Templates/Decision/DecisionTemplate.Copy.xslt&ID={DecisionTemplateID}', {{
'method': 'Copy',
'invoke': 'json',
'callbacks': function(json) {{
document.location = 'Decision/DecisionTemplate.ashx/Summary?ID=' + json.DecisionTemplateCollection.DecisionTemplateItem[0].DecisionTemplateID;
}}
}}
);">Items to note:
- There is no pre-defined popup in
qbo3.Popups(since this is not reused between pages) -
popup(element, options):-
elementis a relative URL to the popup to be rendered -
options.methodis 'Copy'; the Copy statement will be called if the user clicks Save on the popup -
options.invokeisjson;qbo3.AbstractObject.invokeJson()will be called when the user clicks save on the popup -
options.callbacksdefines whatpopupSaveshould pass toinvokeJsonas callbacks - in this case, we grab the new
DecisionTemplateID, and redirect the user to the newDecisionTemplate
-
The MatrixLookup behavior sets a form element value based on an AJAX call to Matrix/Lookup. For example, assume a Matrix named 'Consultant Rates':
| Consultant | Activity | Rate |
|---|---|---|
| - | - | 125.00 |
| [email protected] | 400.00 | |
| DB Tuning | 200.00 | |
| Project Management | 175.00 | |
| [email protected] | Project Management | 150.00 |
From here, we can create a form containing a Consultant select element, an Activity select element, and a Rate input element:
<form>
<select class="dimension" name="Consultant">...</select>
<select class="dimension" name="Activity>...</select>
<input name="Rate" data-behavior="MatrixLookup" data-matrixlookup-options="{{'matrix': 'Consultant Rates', 'measure': 'Rate', 'dimensions': '.dimension'}}"/>
...
</form>As a user changes the Consultant or Activity select elements, the Rate element will automatically be adjusted to the proper value.
| Options | Type | Description |
|---|---|---|
matrix |
String, required |
Name of the Matrix. |
measure |
String, required |
The output measure to use when setting the behavior element's value. |
dimensions |
String, defaults to .dimensions) |
A CSS selector that determines which form elements are to be passed to the Matrix/Lookup method. |
useSelectText |
Boolean, defaults to true
|
If true, select dimensions will be based on the selected text, rather than the selected value. |
When the behavior is applied, each element returned by the options.dimensions css selector will have a change event handler added to it, causing a Matrix/Lookup AJAX call to be fired. The callback from this call sets the value of the behavior's element.
A 'dependency' includes the following use cases:
- Field A should be enabled only if Field B is not empty
- Field A should be enabled only if Field B, C, ... or N is not empty
- Field A should be enabled only if Fields B, C, ... and N are not empty
- An element should be visible only if Field B is not empty
- An element should be visible only if Field B, C, ... or N is not empty
- An element should be visible only if Fields B, C, ... and N are not empty
Note that the Depend behavior works well with the 'required' validation; if elements are disabled, they are not considered required.
<input type="text" id="FieldA" name="FieldA" value="{//FieldA}"
data-behavior="Depend" data-depend-options="{{'depends': 'FieldB'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>
<input type="text" id="FieldA" name="FieldA" value="{//FieldA}"
data-behavior="Depend" data-depend-options="{{'depends': 'FieldB,FieldC'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>
<input type="text" id="FieldC" name="FieldC" value="{//FieldC}"/>
<input type="text" id="FieldA" name="FieldA" value="{//FieldA}"
data-behavior="Depend" data-depend-options="{{'depends': 'FieldB,FieldC', 'condition': 'and'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>
<input type="text" id="FieldC" name="FieldC" value="{//FieldC}"/>
<input type="checkbox" id="ExtraStuff" value="1"/>
<div data-behavior="Depend" data-depend-options="{{'depends': 'ExtraStuff'}}">
<input type="text" name="FirstName" value="//FirstName"/>
<input type="text" name="LastName" value="//LastName" class="required"/>
</div>
| Option | Required | Description |
|---|---|---|
depends |
Yes | a list of element names that determine whether an element is enabled or not |
condition |
No (default is or) |
and or or
|
| 'resetOnDisable' | No (default is true) |
if true, disabled fields will have their value reset to the original values at rendering time when disabled. |
emptyOnDisable |
No (default is false) |
if true, disabled fields will have their value removed when disabled. |
required |
No (default is true) |
if true, the required validator is removed for disabled fields. |
Example using all options:
<input type="text" id="FieldA" name="FieldA" value="{//FieldA}"
data-behavior="Depend" data-depend-options="{{
'depends': 'FieldB,FieldC',
'condition': 'and',
'resetOnDisable': 'false',
'emptyOnDisable': 'true',
'required': 'true'
}}"/>
QBO uses Mootool's Validators pattern for form field validation. The qbo.Validation.js implements a Validator behavior.
Hard validators require the field meet the validator data conditions, regardless of business conditions. For example, one may not enter 'abc' into a money field, regardless of circumstance. Examples of hard validators include: qbo-money, qbo-int, qbo-numeric, qbo-percent, qbo-phone, qbo-ssn, qbo-zip, qbo-date.
Soft validators require the field meet the validator data conditions, but will allow a user to bypass the requirement in some situations. For example, a complex form with dozens of fields might require a user to gather the data across multiple visits, and save the form between visits. In this case, a required validator will warn the user that the field has not been filled out, but may allow the user to save the form so they can return to it later. Examples of soft validators include: required, equality-validator, range-validator-quantity, range-validator-percent, lessthan-validator, greaterthan-validator.
For a validator to be considered a “soft” validator, it must be included in the
softValidatorsarray within theValidatorbehavior defined inqbo.Validation.js.
It is common for an input to have both hard and soft validators. For instance, an input can be a date field, requiring a hard money validation, and it can be required, which is a soft validation.
<form data-behavior="Validator">
<input name="MyField" class="required qbo-money"/>
<button type="submit" class="pause">Pause</button>
<button type="submit" class="save">Save</button>
</form>In this example, the Pause button will require only hard validators to pass, while the Save button will require both hard and soft validators to pass.
Some elements in a form may have business rules attached to them. For example, an appraisal form might require comparable sales be within a few miles of the subject property, which is usually reasonable, unless the subject property is in a rural area. QBO can enable users to bypass such a business rule with a justification. The justification behavior will allow a user to bypass a validator if they enter a reason the validation cannot be met.
| Validator | Description |
|---|---|
required |
The element value must not be empty upon saving. |
qbo-xml |
The element value must valid xml. |
qbo-money |
The element value must be a valid currency amount. |
qbo-int |
The element value must be a valid integer. |
qbo-numeric |
The element value must be a valid number. |
qbo-percent |
The element value must be a valid percent. |
qbo-phone |
The element value must be formatted as a valid US phone number. |
qbo-ssn |
The element value must be formatted as a valid US social security number. |
qbo-zip |
The element value must be formatted as a valid US zip code (or zip + 4). |
qbo-date |
The element value must be formatted as a valid date. |
qbo-field |
The element value must be start with a letter, and be followed by any letter, number or underscore. |
qbo-clean |
The element value will be stripped of extended characters. |