List

From WebOS101

Jump to: navigation, search

Contents

Introduction

Mojo's list widget allows for the display of lists of information and widgets. List contents can be static or dynamic. Lists are also a common source of problems for programmers new to Mojo widgets. These problems are caused partly because the documentation available on Lists is somewhat incomplete or out of date and partly because Lists are so powerful.

Like most widgets List widgets require a fragment of HTML, a model and attributes. They must be instantiated before they can be displayed. Unlike most other widgets Lists also supports three template files: An optional list template, an item template, an optional dividerTemplate and an optional emptyTemplate.

List Setup

Attributes

To set up a list you must define the attributes that the list will have.

this.listAttributes = 
{
itemTemplate: "sceneName/rowTemplate",
listTemplate: "scaneName/listTemplate",
swipeToDelete: false,
fixedHeightItems: true,
reorderable: false
};

In this code, sceneName refers to the name of the directory your view is in and rowTemplate/listTemplate are the names of your template files (see below) without the .html extension.

Model

A list's model contains the dynamic information about the list. If you know your list's items during setup you can set them into the model's items property. If this list will be dynamic, you can adjust the model later.

this.listModel = 
{
listTitle: $L("Name List"),
items: [{"name":"Bob"},{"name":"George"}]
}

Note that anything you define in the model will be available to the list template for substitution.

List Template

A list template is not required for most applications. Two items are passed to a list template: listTitle and listElements. You can include styling information in your listTemplate if you don't wish to include it in your view's HTML or if you wanted to add additional classes to the palm-list div:

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

The list template is placed in a file located in your scene's view directory and has a .html extension.

Item Template

An item template is required to display information in the list. This template specifies what a typical row looks like. When the list is displayed (and updated) data is automatically put into the template to display each row. The row data is pulled from the model's items property. Each row in the value should be a structure containing data with for each of the items in the template.

<div class="palm-row">
<div class="truncating-text">#{name}</div>
</div>

In the example above, model.items must have a "name" member. For example: model.items = [{"name":"Bob"},{"name":"George"}];

Note that since Mojo 1.1 HTML is automatically escaped in templates. If your model's items will contain HTML that you want rendered on the screen then you must either turn of HTML escaping in your entire app (not recommended) or disable it on an item-by-item basis. To disable HTML escaping for a field add a '-' before the property name:

#{-someHTML}

The item template is placed in a file located in your scene's view directory and has a .html extension.

Empty Template

The empty template is displayed when there are no items in a list. The documentation states that it cannot be used with static lists but it can be made to work by calling setLength() after the widget is created. The template can contain any HTML you wish to display when the list is empty.

View HTML

Like other widgets, you can define where in your scene a list appears in the view for your scene. The simplest way to include a list is as follows:

<div class="palm-list"> 
<div id="listWidget" x-mojo-element="List"></div>
</div>

Instantiating the List

Like all widgets, a list must be properly set up before it can be displayed. The normal way to set up a list is to create it during your scene assistant's setup function:

this.controller.setupWidget("listWidget", this.listAttributes, this.listModel);

Template Styles

Discuss the various stylings that can be applied to lists and their rows. x-mojo-tap-highlight?

Working With Lists

List Events

List events all have the following additional fields on the event argument to the callback: {model:this.controller.model, item:dataObj, index: index + this.renderOffset, originalEvent:event}

Event Parameters passed to the listener function
Mojo.Event.listChange none
Mojo.Event.listTap event.item
Mojo.Event.listAdd event.item
Mojo.Event.listDelete event.item, event.index
Mojo.Event.listReorder event.toIndex, event.fromIndex

Examples

Handling a tap on the list:

this.handleListTap = this.handleListTap.bindAsEventListener(this);
this.controller.listen('listWidget', Mojo.Event.listTap, this.handleListTap);
...
MyAssistant.prototype.handleListTap = function(event) {
this.tappedRowValue = event.item;
};

Reordering a list:

this.reorderItemHandler = this.reorderItem.bindAsEventListener(this);
this.controller.listen('myList', Mojo.Event.listReorder, this.reorderItemHandler);
...
FoldersAssistant.prototype.reorderItem = function (event) {
Mojo.Log.info("reorder event %j", event.item, event.toIndex, event.fromIndex);
 
this.myListModel.items.splice(event.fromIndex, 1);
this.myListModel.items.splice(event.toIndex, 0, event.item);
 
// do something with reordered items array
};

Dynamic Lists

Lists are very rarely static. Usually, the information changes in response to various events. A very common theme is requesting the list's contents via an ajax call. To change the contents of a list outside of the setup function you must change list's model's items property then call modelChanged:

MyAssistant.prototype.ajaxSuccess = function(response) {
this.listModel.items = response.responseJSON;
this.controller.modelChanged(this.listModel);
}

However, if the list exceeds 20 items, only the first 20 will be re-rendered after calling modelChanaged. In other words if you are staring at items 25-35, tap on an item to update that item, you will see a white screen until you scroll (allowing the list to be re-rendered). The workaround for this is to use mojo.noticeUpdatedItems(offset, listModel).

MyAssistant.prototype.ajaxResults = function(indexOfListItemTapped, transport) {
 
//some code for parsing your requested data
 
var i = indexOfListItemTapped;
this.listModel.items[i].newInfo = someNewDataToUpdateIntoMyListItem;
$('myListId').mojo.noticeUpdatedItems(0, this.listModel.items);
}
 
// 'myListId' can also be passed as event.srcElement.id
// this is handy if you have multiple lists using the same listTapHandler

List formatters

In the attributes for the List widget, you can specify a formatters property. This property is an object which maps model properties to formatter functions.

List formatter example

This is a pretty good example of how you can use formatters in your application. The formatters property has many more advanced uses than what is shown here. The HTML for the item template should append the string Formatted to the original property name.

Also a formatter does not have to exist in the model as a property, you can create one with whatever name you choose. Its template variable will only exist with 'Formatted' appended to its name. You can see it below as 'example'.

JavaScript
this.list = {
'attributes': {
'itemTemplate': 'scene/item-template',
'formatters': {
 
// this becomes #{categoryFormatted} in the template
'category': function(value, model) {
return 'Category: <b>'+model.category.toUpperCase()+'</b>';
},
 
// this becomes #{titleFormatted} in the template
'title': function(value, model) {
return 'Title: <i>'+model.title.toUpperCase()+'</i>';
},
 
// this becomes #{commentsFormatted} in the template
'comments': function(value, model) {
// you dont always have to check against 'value'
// you can dig directly into the current instance in the model
 
if (model.comments == 1) {
return 'Only '+model.comments+' Comment.'
}
 
else if (model.comments > 1) {
return model.comments+' Comments.';
}
 
else {
return 'No comments yet.';
}
},
 
// this becomes #{exampleFormatted} in the template
// but #{example} will not exist since it is not in the model
'example': function(value, model) {
// since example is not a property of the model value == undefined
 
if (model.category == 'sports') {
return 'WAHOO!! SPORTS!!<br/>';
} else { return; }
}
}
},
'model': {
'listTitle': $L('Articles'),
'items': [
{
'title': 'this is a new article title',
'category': 'news',
'body': 'Gabba Gabba Hey! Gabba Gabba Hey!',
'comments': 12
}, {
'title': 'this is another news article title',
'category': 'sports',
'body':'zzzzzzzZZZzzzzzzzzzzzZzzzzzzz',
'comments':0
}]
}
}
};
item-template.html
<div class="palm-row">
<!-- unformatted -->
<div class='entry'>
<span class='title'>#{category}: #{title}</span>
<div class='entry_body'>#{example}#{body}</div>
<span class='comments'>#{comments}</span>
</div>
 
<!-- formatted -->
<div class='entry'>
<span class='title'>#{categoryFormatted}: #{titleFormatted}</span>
<div class='entry_body'>#{exampleFormatted}#{body}</div>
<span class='comments'>#{commentsFormatted}</span>
</div>
</div>
HTML Output
<div class="palm-row">
<!-- unformatted -->
<div class='entry'>
<span class='title'>news: this is a new article title</span>
<div class='entry_body'>Gabba Gabba Hey! Gabba Gabba Hey!</div>
<span class='comments'>12</span>
</div>
</div>
 
<div class="palm-row">
<!-- formatted -->
<div class='entry'>
<span class='title'><b>News</b>: <i>This is a new article title</i></span>
<div class='entry_body'>Gabba Gabba Hey! Gabba Gabba Hey!</div>
<span class='comments'>12 Comments.</span>
</div>
</div>
 
<div class="palm-row">
<!-- unformatted -->
<div class='entry'>
<span class='title'>sports: this is another news article title</span>
<div class='entry_body'>zzzzzzzZZZzzzzzzzzzzzZzzzzzzz</div>
<span class='comments'>0</span>
</div>
</div>
 
<div class="palm-row">
<!-- formatted -->
<div class='entry'>
<span class='title'><b>Sports</b>: <i>This is another news article title</i></span>
<div class='entry_body'>WAHOO!! SPORTS!!<br/>zzzzzzzZZZzzzzzzzzzzzZzzzzzzz</div>
<span class='comments'>No comments yet.</span>
</div>
</div>

Dividers

Adding Widgets to Lists

One of the most powerful features of lists is the ability to embed other widgets within the List widget. There are some limitations associated with doing so. You don't instantiate the widgets inside the list widget individually. After including the appropriate HTML markup within the list item template call instantiateChildWidgets on the List widget:

this.controller.instantiateChildWidgets(listWidget);

The model for the child widgets share the model of the list. For example, if you include a checkbox in your list widget you must specify a value property for each item in List.

For more information see the List Widget With Checkbox Example.

If you want drawers in a list, that gets a little tricky. The Pseudo-Drawers In A List example provides a nice way to simulate drawers using basic CSS and HTML.

Useful Links

Retrieved from "http://webos101.com/List"
Personal tools