This articles shows a Contacts component for Web Runtime widgets, with the following features:
Contents |
The Contacts component is a customizable user interface component that allows listing, visualization, filtering and handling of contacts retrieved from the device phonebook.
In order to use the the Contacts component, these steps are necessary:
First thing to do, is to include the ContactsComponent JavaScript file (available here: Media:Wrt_ContactsComponent.zip) in the widget's code:
<script language="javascript" type="text/javascript" src="ContactsComponent.js"></script>
Before actually creating the Contacts component, it is necessary to define a HTML element that will contain the component's data. An example is given by the following DIV element:
<html>
<body>
[...]
<div id="contacts_view">
</div>
[...]
</body>
</html>
In order to create a new Contacts component, the ContactsComponent constructor must be called with the following arguments:
A sample instantiation of a ContactsComponent is visible in the following code snippet:
var contacts = new ContactsComponent(
document.getElementById('contacts_view'),
'{LastName} {FirstName}',
handleContact
);
Contacts' data visualization is customizable through a template string, that specifies the exact information to show for each contact. Template strings can include custom HTML code, in order to allow further customization. Sample template strings are the following:
For a complete list of allowed template keys, check out this Forum Nokia Library page:
The handler function is called when the user clicks on a contact element. The component will pass to the handler the contact data as argument. The data format of the contact information is the one returned by the Contacts Service API, and is detailed in this Forum Nokia Library page:
A sample handler function, that alerts the chosen contact's first name, is the following:
function handleContact(contact)
{
alert(contact.FirstName.Value);
}
When an error occurs in the Contacts component, a custom handler function can be called to appropriately show the error information to the user. In order to set a custom error handler, the setErrorHandler() method must be called with the handler function passed as argument. The error handler, when called, receives as argument the error text message. A sample error handler, that alert the error message, is the following:
function handleError(errorMessage)
{
alert(errorMessage);
}
And in order to set this handler to the Contacts component created before, the following code will work:
contacts.setErrorHandler(handleError);
Once created, the component must be initialized, so that it actually loads and shows the contacts' information, and this is done by the initialize() method. The initialize() method accepts an optional string argument: if defined, this argument will be used to filter the data loaded from the device phonebook. If no argument is passed to the initialize() method, all the contacts will be loaded and shown, as in the code snippet below:
contacts.initialize();
Once initialized, all the loaded contacts are shown to the user. In order to allow the user to dynamically filter the contacts, it is possible to set an input field as filter for the displayed contacts. Once it is set as filter, the component automatically uses the input field value as filter for the displayed contacts, matching its value with a customizable set of fields. Default searched fields are the contact's first and last name. So, to set an input field as filter for the contacts component:
<input type="text" name="contacts_filter" id="contacts_filter" />
contacts.setFilterInput(document.getElementById('contacts_filter'));
contacts.setFilterFields(new Array('FirstName', 'MobilePhoneHome'));
When applying the dynamic filter, the global filter, passed as argument to the initialize() method is still active, so both the filters are actually applied to the contact list.
The complete code needed to use and customized the Contacts component is summarized by the following snippet:
var contacts = new ContactsComponent(
document.getElementById('contacts_view'),
'{LastName} {FirstName} <div class="contact_detail">{MobilePhoneGen}</div>',
handleContact
);
contacts.setErrorHandler(handleError);
contacts.setFilterInput(document.getElementById('contacts_filter'));
contacts.setFilterFields(new Array('FirstName', 'LastName', 'MobilePhoneGen'));
contacts.initialize();
function handleError(errorMessage)
{
alert(errorMessage);
}
function handleContact(contact)
{
alert(contact.FirstName.Value);
}
The constructor, as seen in the previous section, accept three arguments:
So, the constructor stores these 3 arguments in three instance variables, and defines the other following properties:
function ContactsComponent(contactsElement, templateString, clickHandler)
{
/* contact item template */
this.contactTemplate = templateString;
/* contact items DOM container */
this.contactsElement = contactsElement;
/* handler function for click event */
this.clickHandler = clickHandler;
/* contacts list */
this.contacts = null;
/* filtered contacts list */
this.textFilter = null;
/* fields used to filter contacts */
this.filterFields = new Array('FirstName', 'LastName');
/* input filter field */
this.filterInput = null;
/* error handler function */
this.errorHandler = null;
}
In order to appropriately manage errors, two functions are defined:
ContactsComponent.prototype.setErrorHandler = function(handler)
{
this.errorHandler = handler;
}
ContactsComponent.prototype.notifyError = function(errorMessage)
{
if(this.errorHandler)
{
this.errorHandler(errorMessage);
}
}
The initialize() method takes care of all the Contacts Service API related calls, in order to load the contacts from the device phonebook.
ContactsComponent.prototype.initialize = function(globalFilter)
{
if(device)
{
var contactsService = device.getServiceObject("Service.Contact", "IDataSource");
var criteria =
{
'Type': 'Contact'
};
if(globalFilter)
{
criteria.Filter =
{
'SearchVal': globalFilter
}
};
var self = this;
var result = contactsService.IDataSource.GetList(
criteria,
function(transId, eventCode, result)
{
self.retrieveContactsHandler(transId, eventCode, result);
}
);
if(result.ErrorCode != 0)
{
this.notifyError(result.ErrorMessage);
}
}
else
{
this.notifyError("Contacts component not supported");
}
}
If some error occurs while trying to load the contacts' data, the notifyError() method is called with an appropriate error message. If the GetList() method of Contacts Service API is correctly called, it'll call the retrieveContactsHandler() function defined as follows:
ContactsComponent.prototype.retrieveContactsHandler = function(transId, eventCode, result)
{
if(eventCode != 4)
{
if(result.ErrorCode != 0)
{
this.notifyError(result.ErrorMessage);
}
else
{
var contact;
var iterator = result.ReturnValue;
this.contacts = new Array();
while((contact = iterator.getNext()) != undefined)
{
this.contacts.push(contact);
}
this.showContacts();
}
}
else
{
this.notifyError("There was an error while retrieving contacts.");
}
}
The handler, in case of errors, also calls the notifyError() method, otherwise it stores in the contacts property all the contacts returned by the Contacts Service API.
Once the contacts' data is stored, the showContacts() method is called in order to actually show the contacts.
When the component needs to show the contacts' data, it has to perform these steps:
ContactsComponent.prototype.showContacts = function()
{
while(this.contactsElement.firstChild)
{
this.contactsElement.removeChild(this.contactsElement.firstChild);
}
var self = this;
for(var i = 0; i < this.contacts.length; i++)
{
if(this.textFilter == null || this.contactMatchesFilter(this.contacts[i]))
{
var contactLabel = this.contactTemplate;
for(key in this.contacts[i])
{
var value = this.contacts[i][key].Value;
var next = this.contacts[i][key].Next;
while(next)
{
value += ', ' + next.Value;
next = next.Next;
}
contactLabel = contactLabel.replace('{' + key + '}', value);
}
contactLabel = contactLabel.replace(/\{.+?\}/, '');
var contactElement = document.createElement('div');
contactElement.contactIndex = i;
contactElement.className = ContactsComponent.CONTACT_CSS_CLASS;
contactElement.innerHTML = contactLabel;
contactElement.onclick = function()
{
if(self.clickHandler)
{
self.clickHandler(self.contacts[this.contactIndex]);
}
}
this.contactsElement.appendChild(contactElement);
}
}
}
The ContactsComponent.CONTACT_CSS_CLASS property contains the CSS class name that is applied to all the contact elements, so allowing further customization:
ContactsComponent.CONTACT_CSS_CLASS = 'contact';
When a text input field is set as filter for the contacts component, it is automatically used by the component itself to filter the loaded contacts. The following methods allow to appropriately manage and use the filter input field:
ContactsComponent.prototype.setFilterFields = function(fieldsArray)
{
this.filterFields = fieldsArray;
}
ContactsComponent.prototype.setFilterFields = function(fieldsArray)
{
this.filterFields = fieldsArray;
}
ContactsComponent.prototype.setFilterInput = function(inputField)
{
var self = this;
if(this.filterInput != null)
{
this.filterInput.onchange = undefined;
}
this.filterInput = inputField;
var inputHandler = function()
{
self.setFilterText(this.value);
}
this.filterInput.onkeyup = inputHandler;
this.filterInput.onkeydown = inputHandler;
this.filterInput.onchange = inputHandler;
}
ContactsComponent.prototype.setFilterText = function(filterText)
{
if(this.textFilter != filterText)
{
this.textFilter = filterText;
this.showContacts();
}
}
ContactsComponent.prototype.contactMatchesFilter = function(contact)
{
var regExp = new RegExp(this.textFilter, "i");
for(var i = 0; i < this.filterFields.length; i++)
{
var current = contact[this.filterFields[i]];
while(current)
{
if(regExp.test(current.Value))
{
return true;
}
current = current.Next;
}
}
return false;
}
These related resources are available for download:
No related wiki articles found