Lists

The List is the most powerful component in the Mojo framework; much of the framework design has been centered on creating a fast, flexible and feature-rich list widget. You should keep in mind that Mojo's design started with the design of a dynamic List widget to support the Contacts application's list view and was heavily optimized around that type of scene. The HP webOS user experience makes extensive use of lists in many applications; given the form factor and the navigation model, most applications will incorporate a list widget in one way or another. To get the most out of Mojo you need to fully understand the capabilities of List.

The other widgets: FilterList is derived from the List widget, so you should refer back to the List widget documentation for information on APIs.

List

Objects are rendered into list items using provided HTML templates, and may be variable height and/or include other widgets. Lists can be static, with the list items being provided to the widget directly as an array, or they can be dynamic, with the application providing a callback function that is invoked as additional items are needed for display. Lists can be manipulated in place, with the framework handling deletion, reordering, and addition functions for the application. The List widget will manage which items to render as the user scrolls through the content. In this way, the List will not need to render all items at one time, therefore saving memory in situations where there is a lot of data to display.

The list widget is declared in your scene's view file with a DIV declaration that like all widgets includes a unique id property for the list (x-mojo-element="List"). Like other widgets, you call this.controller.setupWidget from within your scene assistant with attribute and model objects. There are quite a few attribute properties that you can include but only the itemTemplate is required. The list's model must include an items array that contains a list of objects to display in the list or you must supply an itemsCallback. Each object represents a list entry and the properties in the object are substituted for references in the list's itemTemplate. When a list item is selected, a Mojo.Event.listTap event is generated, which you need to handle if any action is required.

Templates

All Lists are all built using HTML templates to lay out and format the list container and the individual rows. You would normally include these templates as separate HTML files in your scene's view folder (where your scene view file is located), but you can specify each template's pathname, which allows you to share templates between scenes or organize them in other ways. Pathnames are specified with relative notation ../<dir>/ where <dir> is the directory for the current scene's view file. Within the template, you will reference properties from the list's data.

The listTemplate is optional; it defines the path to an HTML template for the list's container, which if missing will simply put the list items into the scene without any container. The listTemplate, if present, can have only one top-level element.

The itemTemplate is required; its value is the path to an HTML template for rendering individual list items. To reference specific items you would include strings that wrap the property's name from the object defined in the Items array as #{property}. List rows must have only one top-level element.

For example, if you had a list where each entry was a Neil Young CD with the year it was issued, you would code the template as follows in your itemTemplate HTML file:

<div class="title left">#{data}<span class="year">#{year}</span>

To correspond to the properties in the list model:

this.neilYoungModel = { items: [
  {data:"On The Beach", year:"1974"},
  {data:"Tonight's The Night", year:"1975"},
  {data:"Harvest", year:"1972"},
  {data:"Greendale", year:"2003"},
  {data:"Broken Arrow", year:"1996"},
  {data:"Everybody Knows...", year:"1969"},
  {data:"Zuma", year:"1975"},
  {data:"Unplugged", year:"1993"},
  {data:"Harvest Moon", year:"1992"}
]};

The data property is added into the HTML to replace the #{data} string and the year property replaces the #{year} string. With the addition of styles that we'll discuss in a bit, you'd get a list that would look something like the diagram below:

image

Dynamic Lists

If you set itemsCallback to a callback function for supplying list items, then you don't need to provide the items array objects at setup time. Whenever the framework needs to load items (speculatively or for display), it will call the function itemsCallback (listWidget, offset, limit), where:

Argument Type Description
listWidget Object The DOM node for the list widget requesting the items
offset Integer Index in the list of the first desired item model object (zero-based)
limit Integer Count of the number of item model objects requested

It is understood that the requested data may not be immediately available. Once the data is available, the given widget's noticeUpdatedItems() method should be called to update the list. It's acceptable to call noticeUpdatedItems() immediately if desired, or any amount of time later. Lengthy delays may cause various scrolling artifacts, however. The method should be called as listWidget.mojo.noticeUpdatedItems(offset, items), where:

Argument Type Description
offset Integer Index in the list of the first object in 'items'; usually the same as 'offset' passed to the itemsCallback
items Array An array of the list item model objects that have been loaded for the list

Formatters and Dividers

The formatters property is simply a hash of property names to formatter functions, like this:

{
  timeValue: this.myTimeFormatter,
  dayOfWeek: this.dayIndexToString, ...
}

Before rendering the relevant HTML templates, the formatters are applied to the objects used for property substitution. The keys within the formatters hash are property names to which the formatter functions should be applied.

The original data passed in is not modified, and the formatted properties are given modified new names and added to your list's data model so that the unformatted value is still accessible from inside the HTML template. Formatted values have the text "formatted" appended to their names. In the example above, the HTML template could refer to #{timeValueFormatted} in order to render the output from the myTimeFormatter() function. Formatter functions receive the relevant property value as their first argument, and the appropriate model object or items element as their second.

You can add formatted dividers to a list (something particularly useful for long lists) by specifying the function in the dividerFunction property and a template to dividerTemplate. If there is no template specified, then the framework will use the default, a single-letter alpha divider (list-divider.html, styled with the .palm-alpha-divider class).

The divider function works similar to a data formatter function. It is called with the item model as the sole argument during list rendering, and it returns a label string for the divider. For example, this function dividerAlpha generates list dividers based on the first letter of each item:

dividerAlpha = function(itemModel) {
  return itemModel.data.toString()[0];
};

The list uses the property #{dividerLabel} to determine where to put the divider content.

Rendering Lists

The following properties can be used to fine-tune the list behavior:

List Manipulation

If the addItemLabel property is specified, then an additional item is appended to the list. Tapping it will cause a Mojo.Event.listAdd event to be fired on the widget DIV.

If the reorderable property is specified, then a tap-and-hold on a list item will allow it to be moved to a new position in the list. A Mojo.Event.listReorder event is fired on the widget DIV, which includes the item being moved, as well as the old and new indexes. The indexes are passed as properties of the event object, event.toIndex and event.fromIndex.

If the swipeToDelete property is specified, then dragging items horizontally will invoke special delete UI, allowing the operation to be confirmed or canceled. If confirmed, a Mojo.Event.listDelete event is fired on the widget DIV, which includes the item being removed, event.item, and its index, event.index.

Deleted items that are unconfirmed have a deleted property set in the model. The name of this property can be specified using the deletedProperty property, and Mojo.Event.propertyChange events will be sent when it is updated. If unspecified, the property deleted will be used. For dynamic lists, it can be important for the app implementation to persist this value in a database. Otherwise, swiped items will be 'automatically undone' when they are removed from the cache of loaded items.

If the dragDatatype property is specified, then items will be allowed to be dragged to other lists with the same dragDatatype value. When this happens, the item's old list will receive a Mojo.Event.listDelete event, and the new list will get a Mojo.Event.listAdd event. In this case, the listAdd event will have the item and index properties specified, indicating that a specific item should be added at a specific location.

List entries can be editable by using an INPUT tag instead of a DIV in the itemTemplate. Whenever an entry is edited, a Mojo.Event.listChange event will be fired.

Styling and Highlighting

The example list used above in Templates wrapped the List widget with some specific styles to get the visual appearance shown in the figure. You should review Style Matters for a complete discussion of Mojo styling, but we can do a brief review of List styles here.

There are three levels of styles at work in that example:

The HTML would be divided between the scene's view file and the two templates as follows:

scene-assistant.html

<div class="palm-group">
  <div class="palm-group-title" id="shakey-toggle">Neil Young Albums</div>
  <div class="palm-list">
      <div id="neilYoungAlbums" x-mojo-element='List'></div>
  </div>
</div>

listTemplate.html

<div class="palm-list">#{listElements}</div> 

itemTemplate.html

<div class="palm-row" x-mojo-tap-highlight="momentary">
  <div class="title left">#{data}<span class="year">#{year}</span></div>
</div>

You may have noticed the addition of the x-mojo-tap-highlight='momentary' in the top level div of the item template. This will cause the list item to highlight when tapped. The gesture subsystem will apply the 'selected' class to the div when it should be highlighted. You will get the default highlight appearance or you can override that appearance using your style sheet.

Filter List

When your list is best navigated with a search field, particularly one where you would instantly filter the list as each character is typed into the field, you should use the FilterList widget. It is intended to display a variable-length list of objects, which can be filtered through a callback function, and can handle either a dynamic or static list (for more on this, see List above).

The widget has a text field displayed at the top of the scene, overlaying the list. The list displayed should be the result of applying the contents of the text field against some off-screen data source. When first opened, the user will see the entire contents of the list. As they type, the list should shrink to match what they have typed.

The Filter List is a combination of the Filter Field and List widgets, so see the documentation for those widgets for more information. It will automatically instantiate both of its sub-widgets, so you don't need to define them in your scene HTML as well:

<div id="filterList" x-mojo-element="FilterList"></div>    

To setup a FilterList:

var attributes = {
  itemTemplate: 'filterlist/entry',
  filterFunction: this.filterFunction.bind(this)
};
this.model = {};
this.controller.setupWidget('filterList', attributes, this.model);

The filterFunction, like the itemsCallback property in the base List widget (see above) is provided by you to update the List data. The calling interface is virtually the same, with the addition of an argument filterString so that you define your function definition as filterFunction (filterString, listWidget, offset, limit), where:

filterFunction: function(filterString, widget, offset, limit) {
  var matchingSubset = this.getMatches(filterString, offset, limit);
  listWidget.mojo.noticeUpdatedItems(offset, matchingSubset);
  listWidget.mojo.setLength(matchingSubset.length);
};

The Filter List will display a spinner in the text field while the list is being built and is intended to display an entry count when done. The count to display and actual list length may differ if you are showing multiple items per row, hiding some of the items returned, or want to display a special message instead of the exact length. To set the count to display to the user:

this.controller.get('filterList').mojo.setCount(10);

Creating a List

The list object renders its items by using a model as a data source and a template to format the data. The model specifies one or more pieces of data to display in each list item, and the template has placeholders for the data.

Example:

<div x-mojo-element="List" id="mylist"></div> 

In the list item's template, specify the layout of the data by using placeholders. For example, here is a template for a list where each item displays an image and some text:

<div class="palm-row">
  <img src="#{myimgprop}" />
  <span>#{mytextprop}</span>
</div> 

The scene's setup() method creates the list model. For example, the following model contains JSON objects for each list item, and each object contains data for each field in the template:

this.mylistmodel = { items: [
  { "myimgprop" : "./images/hey.gif", "mytextprop": $L("Localized text string 1") },
  { "myimgprop" : "./images/abc.gif", "mytextprop": $L("Localized text string 2") },
  { "myimgprop" : "./images/wow.gif", "mytextprop": $L("Localized text string 3") },
  ...
] }; 

Use setupWidget() to associate the list with the model.

Example:

this.controller.setupWidget('mylist', \{ ... }, this.mylistmodel); 

If the list items themselves must be scrollable, use a subscroller. Set up the subscroller widget in the template as follows:

<div class="palm-row">
  <div x-mojo-element="Scroller" name="subscroller">
      <img src="#{myimgprop}" />
      <span>#{mytextprop}</span>
  </div>
</div> 

And then, pass the subscroller in setupWidget as follows:

// selector by name, model will be pulled from this.mylistmodel.items\[index\]
this.controller.setupWidget('subscroller', \{ ... });

Controlling HTML Escaping in Templates

HTML escaping causes certain characters in the text ("&", "<", and ">") to be converted into HTML-safe character strings.

HTML escaping in templates is controlled by the value of escapeHTMLInTemplates in the file framework_config.json. If your application does not already have a framework_config.json file, create one in the application directory.

By default, HTML escaping is turned on in the HP webOS SDK.

HTML escaping is important unless you are sure that the content is safe, because unescaped HTML that is interpreted by the application can have unintended consequences.

With HTML escaping turned on, you can add a leading hyphen ('-') to any property reference in the template to prevent its HTML from being escaped.

Example:

#{-myPropertyWithHTML}