Skip Headers
Oracle® Fusion Middleware Portal Development Guide for Oracle WebLogic Portal
10g Release 3 (10.3.4)

Part Number E14243-06
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

10 Advanced DVT Development

This chapter discusses how to localize, extend, and modify the Dynamic Visitor Tools (DVT).

Note:

The information and examples in this section assume that you are familiar with both JavaScript and the Dojo toolkit. Dojo is used extensively in the DVT implementation.

This chapter includes the following sections:

10.1 Localizing the DVT

This section explains how to localize the out-of-the-box DVT components and how to localize any custom components that you develop (widgets, visual surfaces, alerts, and so on).

10.1.1 Overview

The DVT uses resource bundles for localization. A resource bundle is a JavaScript object in JSON format that specifies properties and values, where the values are the strings to be localized. For example, the DVT bundle Alert.js looks like this:

{
    okBtnLabel: 'Ok',
    cancelBtnLabel: 'Cancel'
}

In this case, OK and Cancel are localizable strings. The property names (also called message keys) are used to reference these localizable values in JavaScript code. For more information, see Section 10.1.3, "Localizing Custom Widgets."

Resource bundles can also contain string templates that allow variable substitution. For example, the pageSizeLabel property shown below includes a variable "${0}":

{
    selectTitle:"Change the number of items to view",
    pageSizeLabel:"Show ${0} Items"
}

All resource bundles for the DVT are located here:

<web_project>/dvt/l10n/com/bea/wlp/dvt/uikit/nls

You create locale-specific folders under the nls folder. The locale-specific folder names follow a standard naming convention. They are lower case, and a hyphen separates the language code from the optional country code. For example, en-us is the code for United States English and en-au is the code for Australian English. Figure 10-1 shows some of the DVT resource bundles under the nls folder in a web project. The nls/ja folder contains resource bundles localized for the Japanese language.

Tip:

For more background information on the way locale-specific files are structured, see the Dojo Toolkit Documentation topic "Internationalization (i18n)."

Figure 10-1 DVT Resource Bundles

Description of Figure 10-1 follows
Description of "Figure 10-1 DVT Resource Bundles "

10.1.2 Localizing the Out-Of-The-Box DVT

If you wish to localize the DVT for a language that is not supported out-of-the-box, do the following:

  1. Copy the <web_project>/dvt/l10n/com/bea/wlp/dvt/uikit/nls folder to your project. To do this, right-click the folder and select Copy to Project.

  2. Create the appropriately named locale-specific folder under the nls folder.

  3. Copy the resource bundles from one of the existing locale folders to the new folder.

  4. Translate the resource bundles.

10.1.3 Localizing Custom Widgets

This section explains how to localize custom widgets that you write for the DVT. All classes that require localized strings must include the _Localizable base class. This class provides a set of localization methods that:

  • Register localization modules

  • Load message bundles

  • Perform string substitutions

To add _Localizable to a class, use the dojo.declare statement in the class that you wish to localize. For example:

wlp_dvt_dojo.declare(
    "com.bea.wlp.dvt.uikit.Alert", 
    [com.bea.wlp.dvt.uikit.UIKitWidget, com.bea.wlp.dvt.util._Localizable], 
{
....

This statement includes _Localizable as a "mixin," making its methods available to the Alert class. For more information on mixins, see Section 10.2, "Extending the DVT with Mixins."

When using the _Localizable class, note the following conventions:

  • The message bundle for a widget must be located under the <web_project>/dvt/l10n folder, in a directory structure patterned after the package of the class. For example, the message bundle for the class com.foo.Bar.js must be placed in: <web_project>/dvt/l10n/com/foo/nls/Bar.js.

  • Within a widget class (or its template), reference message bundles with an alias called messages. For example, for the following message bundle, the expression ${messages.selectTitle} returns the string "Change the number of results per page."

    {     selectTitle:"Change the number of results per page.",
         search:"Enter a '${0}' search query."
    }
    

    For information on customizing the message alias, see Section 10.1.4.2, "Customizing Message Aliases."

10.1.4 Extending _Localizable to Customize L10N Features

Extend _Localizable if you want to customize how L10N modules are loaded and customize the message alias described previously in Section 10.1.3, "Localizing Custom Widgets."

10.1.4.1 Customizing How L10N Modules Are Loaded

DVT localization resource bundles are located by default in <web_project>/dvt/l10n. You can specify a different location by specifying a value for the l10nModules property. You can do this either in the _Localizable constructor or by setting the l10nModules property at the class level, before the constructor. The _Localizable constructor takes this form:

constructor: function(args, node) {
}

The args parameter is an array, and it can take two optional elements, l10nModules and/or messagePropertyAliases (described in the next section). The l10nModules property takes the form of an array of objects. For example:

args[l10nModules] = [{module: "module1", prefix: "path/to/module1"},
{module: "module2", prefix: "path/to/module2"},...]

This form specifies the path where the resource bundles for each specified module are located. Note that the value of prefix is a path relative to the web application root. You can also specify this property as a property of the widget (before the constructor) like this:

l10nModules: [{module: "module1", prefix: "path/to/module1"},
{module: "module2", prefix: "path/to/module2"},
...]

Using the l10nModules property also helps optimize the loading of modules. If a module with a given prefix is already loaded and a request to load it against a different module name is encountered, the DVT localization framework avoids resending a request for the JavaScript bundle file.

10.1.4.2 Customizing Message Aliases

You can change the default value of the messages alias. The messages alias lets you reference resource bundle properties conveniently from within a widget, as explained in Section 10.1.3, "Localizing Custom Widgets." You have two options for customizing the message alias. First, you can simply replace the alias with a new name. This option is useful if your widget already contains a variable called messages. In this case, the alias refers to the existing default location of the message bundles. For example:

messagePropertyAliases = "myMessageAlias"

You can also specify an object array of this form:

messagePropertyAliases: [{alias: "msg1", path: "path.to.bundle1", file:"bundle1"},
    {alias: "msg2", path: "path.to.bundle2"}, file:"bundle2",
...]

For a more complete example, see Section 10.1.4.3, "Handling Parameterized Messages."

The object array form lets you specify multiple aliases referring to multiple different resource bundle files. This option lets you avoid duplicating message bundle files and use existing message bundle files without creating new ones.

10.1.4.3 Handling Parameterized Messages

To use parameterized messages in a widget template, you must override the postMixinProperties() method of _Localizable and substitute placeholders with values.

For example, suppose the com.bea.wlp.dvt.uikit.SearchField widget only needs one localized message and you do not want to define a new message bundle file for that purpose. Instead, the widget can reuse the message bundle file defined for com.bea.wlp.dvt.uikit.ResultsPerPageView. Furthermore, suppose that the message needs to be parameterized. The following code fragments illustrate how to accomplish this customization:

Here are contents of the SearchField template file (/dvt/com/bea/wlp/dvt/uikit/SearchField.html). Notice that the message alias is "msg" and not the default "messages." The technique for changing the default is demonstrated in the SearchField.js file, shown below.

<div class="com_bea_wlp_dvt_uikit_searchBox">
    <input type="text" name="search" title="${msg.search}"/>
</div>

The ResourcePerPageView resource bundle at /dvt/l10n/com/bea/wlp/dvt/uikit/nls/ResultsPerPageView.js.

{
    selectTitle:"Change the number of results per page.",
    search:"Enter a '${0}' search query."
}

The SearchField widget JavaScript file at /dvt/com/bea/wlp/dvt/uikit/SearchField.js. Note that this is where the default message property alias is changed from messages to msg.

wlp_dvt_dojo.declare
(
    "com.bea.wlp.dvt.uikit.SearchField",
    [com.bea.wlp.dvt.util._Localizable, dijit._Templated],
{...
constructor: function(args, node)
{
    // reset messagePropertyAliases
    args[messagePropertyAliases] = 
        [{alias: "msg", path:"dvt.l10n.com.bea.wlp.dvt.uikit",
        file:"ResultsPerPageView"}];
}...

And the SearchField.postMixinProperties() method:

postMixInProperties: function() {
    this.inherited(arguments); // Required: the parent function does a lot of the set up work
    this.msg.search = com.bea.wlp.dvt.util.L10nHelper.substitute(this.msg.search, ["contains"]);
}

The result of this customization is that the SearchField "reuses" a message bundle from another module. In this example, the resulting message is: "Enter a contains search query."

The L10nHelper.substitute() method substitutes placeholders specified in resource bundle messages with values specified in an array. This helper class is located in <web_project>/dvt/com/bea/wlp/dvt/util.

10.2 Extending the DVT with Mixins

You can add functionality to any DVT class by implementing and configuring mixin classes. Mixins provide a way to extend the basic functionality of the DVT without opening and modifying the JavaScript source code provided by WLP. This section includes these topics:

10.2.1 What are Extensions?

The mixin mechanism is implemented as a DVT extension. Extensions execute arbitrary JavaScript code either before or after specified DVT classes are declared. You enable and specify extensions in the <web_project>/dvt/config.js file. The basic form of an extension, as specified in config.js, is:

extension: {
    dvtContentIncludes: [
      {
          pkg:" " /* A package or class name*/ ,
          pre: {
              ext:" " /* Execute some JavaScript code before pkg classes are
                         delcared */ }, 
          post: {
              ext:" "  /* Execute some JavaScript code after pkg classes are
                          declared. */}
          }
      }
    ],
    dvtContentExcludes: [" /* Exclude these classes from pkg. */ "]
},

The extension object specifies which packages or classes to apply the extension to (dvtContentIncludes) and which to exclude (dvtContentExcludes). For example, you can choose to apply an extension to all the classes in a given module, but exclude one or more of them. Therefore, the extension code only applies to the included classes.

The ext property is a string consisting of JavaScript code. This code only has access to global objects and objects defined within the scope of the code. For example, code specified in the pre block can only access global variables. Code running in the post block can access to the same variables that the pre block code can access, plus the newly initialized object specified with the pkg property.

Both the pre and post code blocks also can access a global object com.bea.wlp.dvt.util._ext. This object is populated with a copy of all of the properties in the pre and post block objects and the following properties:

  • module – In the case of the pre block, the name of the module that is about to be initialized. In the case of the post block, the name of the module just initialized.

  • moduleObj – The initialized module object. This property only applies to the post block.

Example 10-1 displays an alert with the name of the module that was just loaded. The alert appears after each DVT class is declared, except the com.bea.wlp.dvt.util.Util class.

Example 10-1 DVT Extension Example

extension: {
    enabled: true,
    dvtContentIncludes: [
        {
        pkg:"com.bea.wlp.dvt",
        post: {
            ext:"alert(com.bea.wlp.dvt.util._ext.post.module);"
        }
    }],
    dvtContentExcludes: ["com.bea.wlp.dvt.util.Util"]
}

As mentioned previously, this extension mechanism is used by the DVT to implement the mixin feature. The next section discusses this implementation in more detail.

10.2.2 How Mixins are Implemented

Mixins are implemented as an extension. The mixin extension is defined by default in the <web_project>/dvt/config.js file. Example 10-2 shows the mixin extension implementation. This implementation makes a request for a <modulename>Mixin module and extends the current module with code in <modulename>Mixin. Note that the mixin extension applies to all DVT modules after they are declared, except com.bea.wlp.dvt.Util, which is excluded.

Tip:

The dojo.extend() method is used to perform the extension. For more information, refer to Dojo toolkit documentation on this method.

Example 10-2 The Mixin Extension

extension: {
        // true or false to enable or disable the extension mechanism 
        enabled: true,
 
        dvtContentIncludes: [
            {
                pkg:"com.bea.wlp.dvt",
                post: {
                    ext:"try{"+
                        "wlp_dvt_dojo.require(com.bea.wlp.dvt.util._ext.module+\"Mixin\", true);"+
                        "var mixinObj = 
                            wlp_dvt_dojo.getObject(com.bea.wlp.dvt.util._ext.module+\"Mixin\");"+
                        "if(mixinObj) {"+
                        "if(com.bea.wlp.dvt.util._ext.post && 
                             com.bea.wlp.dvt.util._ext.post.keepOldProps) {"+
                         "var thisModule = 
                             wlp_dvt_dojo.getObject(com.bea.wlp.dvt.util._ext.module);" +
                         "for( prop in mixinObj) { " +
                            "if(thisModule.prototype[prop]) { " +
                                "if(!thisModule.prototype.getExtendee) { "+
                                    "thisModule.prototype.getExtendee = function() { "+
                                        "if(!this._wlpInherited) { this._wlpInherited = {};};
                                              return this._wlpInherited; " +
                                     "};" +
                                 "} "+
                                 " if(!thisModule.prototype._wlpInherited) {"+
                                    " thisModule.prototype._wlpInherited = {};}" +
                                    "thisModule.prototype._wlpInherited[prop] =
                                         thisModule.prototype[prop];" +
                         "}}};"+
                        "thisModule = wlp_dvt_dojo.extend(thisModule, mixinObj);}"+
                        "}catch(e){console.error(e);}",
                    keepOldProps: true
                }
            }
        ],
 
        dvtContentExcludes: ["com.bea.wlp.dvt.util.Util"]
    },

Place your mixin modules (for example, <modulename>Mixin) in the web application in the same directory as the module you wish to extend. See Section 10.2.4, "Mixin Examples" for more information.

The Mixin extension also defines a property keepOldProps on the post object. If this property is set to true (the default), properties in the module being extended that might be overwritten as a result of the extension process are preserved. You can access these preserved properties using this.getExtendee().<propertyName>.

In summary, if Mixins are enabled, the DVT will request a <modulename>Mixin class when loading the <modulename> class, and extend <modulename> class with the code in the Mixin class. For example, if a class com.bea.wlp.dvt.uikit.UIKitWidgetMixin exists, it will be requested when com.bea.wlp.dvt.uikit.UIKitWidget is loaded.

10.2.3 Enabling Mixins

To enable the mixin feature, you must first enable the extension feature. By default, the extension mechanism is disabled.

  1. In Oracle Enterprise Pack for Eclipse, open the Merged Projects view.

  2. Navigate to <web_project>/dvt.

  3. Right-click the file config.js and select Copy to Project.

  4. Open config.js in the editor.

  5. Locate the extension object and set the enabled property to true.

10.2.4 Mixin Examples

Example 10-3 illustrates a mixin class, PortletItemMixin, that adds "_MP" to every portlet item's title by overriding the PortletItem._getItemArray() method.

Note:

Before you can use mixins, you must enable the extension feature. See Section 10.2.3, "Enabling Mixins."

You can test this mixin example by creating this file with the code in Example 10-3:

<web_app>/WebContent/dvt/com/bea/wlp/dvt/data/item/PortletItemMixin.js

Note:

In practice, WLP only supports reading data from the server, not writing. For instance, WLP supports dojo.data.api.Read, but not dojo.data.api.Write. Example 10-3 illustrates how data that from the server's perspective is read-only can be changed on the client before display.

Example 10-3 Example Mixin Class

wlp_dvt_dojo.setObject("com.bea.wlp.dvt.data.item.PortletItemMixin", {
    _getItemArray: function(rawItemArray) {
        if(wlp_dvt_dojo.isArray(rawItemArray)) {
            var i;
            var itemArray = [];
            for(i=0;i<rawItemArray.length;i++) {
                itemArray[i] = new com.bea.wlp.dvt.data.item.PortletItem(rawItemArray[i]);
                itemArray[i].title[0] = itemArray[i].title[0] + "_MP";
            }
            return itemArray;
        } else {
            throw new Error("_getItemArray expects an array of raw items.");
        }
    }
});

Example 10-4 illustrates a mixin class that prints a console warning and calls the hitchDvtToLaf() method in com.bea.wlp.dvt.injector.BighornInjector. Note the use of this.getExtendee() to access overwritten properties of the extended module. You can access the extended module in the extension ext property using com.bea.wlp.dvt.util._ext.post.moduleObj, as explained in Section 10.2.2, "How Mixins are Implemented."

Example 10-4 Example Mixin Class

wlp_dvt_dojo.setObject("com.bea.wlp.dvt.injector.BighornInjectorMixin", {
    hitchDvtToLaf: function() {
        console.warn("In BighornInjectorMixin")
        this.getExtendee().hitchDvtToLaf.call(this);
    }
});

10.3 Using the Server Data Store

The DVT includes an API for locating and reading data from a data source. This API conforms to the dojo.data.api.Read and dojo.data.api.Identity APIs. A data source can be any source of raw data. For example, a data source could be a file, such as a CSV file, a database, or a web service. This section includes these topics:

10.3.1 Server Data Store Features

This section lists the basic features of the DVT Server Data Store:

  • Fetches data from any server that supports HTTP.

  • Operates in any web container in which Dojo is installed.

  • Fetches data in any data format. You can add support for JSON or other data formats using pluggable data format handlers.

  • Fetches data with any structure through pluggable data type handlers. You can add data type support either at creation or runtime through a registration API. New data types must conform to the com.bea.wlp.dvt.data.item.Item API. Default support is provided for common WLP types, like portlets, remote portlets, books, pages, desktops, layouts, look and feels, menus, shells, and themes.

  • Supports pagination and sorting whether on the server or on the client.

  • Supports search and filtering.

  • Supports customization either through creation in JavaScript code or through HTML using the Dojo parser.

  • Can be used with other Dojo UI widgets to display data.

10.3.2 Using Server Data Store: A Basic Example

This section presents a basic example that demonstrates how to instantiate and use a ServerDataStore object. The code in Example 10-5 creates an instance of ServerDataStore with a URL parameter specifying the location from which to retrieve the data. For more information on the constructor, see Section 10.3.3, "The Server Data Store Constructor."

The fetch() method takes a "request" object that encapsulates the function's parameters. (See Section 10.3.4, "Request Object Parameters" for more information.) First, the query parameter specifies the webapp that contains the data. The queryOptions parameter specifies parameters that filter the query. In this case, queryOptions specifies filtering for the title and description attributes of the data. The filtering occurs on the server, because client side filtering is turned off by default. Filtering can only be performed on one parameter per request. In this example, filtering will only be performed on the title attribute. The count parameter sets the page size of the returned data to 2. The onComplete parameter is a function that is called when all data items on the page are loaded. In this case, onComplete displays the portlet title and its identity on the page.

Example 10-5 Server Data Store Example in JavaScript

var serverDataStore = new com.bea.wlp.dvt.data.stores.ServerDataStore({
    url: "http:/localhost:7001/portal_1/bea/wlp/api/portlet/list"
});
serverDataStore.fetch({
    query: {webapp: "portal_1"},
    queryOptions: {title: "*C*", description: "*c*", ignoreCase: true},
    count: 2,
    onComplete: function(/*Item array*/items, /*Object*/request) {
        var dataDiv = "<div id=\"dataDivPortlet\">";
        //console.log("In test.jsp onComplete items.length " + items.length);
        dataDiv += "<p><b>Portlets</b></p>";
        for(var i=0;i<items.length;i++) {
            dataDiv += serverDataStore.getValue(items[i], "title") +
            "<span style=\"font-size:.8em;color:red\">
            (Identity: " + serverDataStore.getIdentity(items[i]) +")</span>";
        }
    dataDiv += "</div>";
    wlp_dvt_dojo.byId('storePortletData').innerHTML = dataDiv;
    }
});

10.3.3 The Server Data Store Constructor

This section describes properties of an object that you can pass to the ServerDataStore constructor to customize the server data store's behavior.

  • url – (string) Specifies the server URL from which to retrieve the data.

  • clientSidePaging – (boolean) Denotes whether to page data on the server or on the client. If false, every call to the next and previous functions causes a request to be sent to the server URL, configured with the appropriate page number and page size values. If true, the data is fetched only once from the server, and calls to the next and previous functions page over the data set that resides in memory on the client.

  • itemType – (object) Optional object that specifies the data type. This specified type overrides any built-in types. ServerDataStore tries to create items of this specified type. The type must support methods defined in com.bea.wlp.dvt.data.item.Item. Declare the type using dojo.declare with a constructor function.

  • dataFormatHandler – (function) Optional function that handles a given data format with the specified item type of that format. Defaults to a JSON data handler. This function must always return a JSON representation of the data, whatever the input format is.

  • comparatorMap – For details on this optional parameter, see documentation for dojo.data.util.sorter. This parameter only works on the client side. If clientSidePaging is set to false, sorting occurs only on the current page of data.

10.3.4 Request Object Parameters

The ServerDataStore.fetch() method takes a "request" object that includes the following required and optional parameters:

  • query – Specifies attributes that qualify a fetch request. This parameter can contain the following attributes:

    • webapp – (string) The web application from which to retrieve the data.

    • locale – (string) Any string that is supported by a registered dataFormatHandler. Defaults to JSON format.

  • queryOptions – Optional parameter that specifies options that modify the query. Currently, this parameter only supports filtering parameters. If you use the WLP REST API to construct the URL end point for the data store, and server-side filtering is enabled, this parameter takes the following options:

    • title – (string)

    • description (string)

    • ignoreCase (boolean)

    If both the title and description are specified, title takes precedence. This is because filtering is currently supported for one attribute per request.

    If client-side filtering is enabled (setting clientSideFiltering to true in the ServerDataStore constructor), queryOptions can be used to filter any property that the data items contain.

  • onBegin – Specifies a callback function(size, request). If an onBegin function is provided, the callback is only called once, before the first onItem callback is called. The functions parameters are the total number of items identified and the Request object. If the total number is unknown, the size is -1. The size is not necessarily the size of the collection of items returned from the query, because the request might only return a subset of the total set of items with the use of the start and count parameters.

  • onItem – Specifies a callback function(item, request). If specified, the onItem callback function is called as each item in the result is received. The parameters include the item itself and the Request object.

  • onComplete – Specifies a callback function(items, request). If provided, the onComplete callback function is called once, after the last onItem callback is called. Note that if the onItem callback is not present, then onComplete is passed an array containing all items that matched the query and the Request object. If the onItem callback is present, then onComplete is called as: function(null, request).

  • onError – Specifies a callback function(errorData, request). This function is called if an error occurs during the query execution. The onError callback function takes two arguments, an error object and a Request object.

  • scope – If a scope object is provided, all callback functions (onItem, onComplete, onError, and so on) are invoked in the context of the scope object. In the body of the callback function, the value of the "this" keyword is the scope object. If no scope object is provided, the callback functions are called in the context of the dojo.global(). For example:

    onItem.call(scope, item, request)
    

    or

    onItem.call(dojo.global(), item, request)
    
  • start – (integer) Specifies a number of items in the data store to skip over. If the count parameter is also specified, the data store pages across queries by only returning subsets of the hits for each query.

  • count – (integer) Specifies the number of items to be returned from the data store. This parameter overrides the value of the pageSize parameter.

  • sort – Specifies how to sort the items before they are returned. This parameter specifies an array of JavaScript objects that must conform to the following format:

    {
        attribute: attribute || attribute-name-string,
        descending: true|false; // Optional. Default is false.
    }
    

Note:

When comparing attributes, if an item contains no value for the attribute (undefined), then the default ascending sort logic pushes it to the bottom of the list. In the descending order case, such items must appear at the top of the list. The sort parameter can also be a sort function. In this case, the sort function is called. The sort function accepts requestArgs and a list of items, and it returns a sorted list based on some criteria. The function is executed in the context of the data store object.

10.3.5 Server Data Store Examples

Example 10-6 illustrates how to customize the data type that the data store handles. In this example, the itemType attribute is specified in the ServerDataStore constructor. The code for the tests.MyPortletItem class is shown in Example 10-7.

Example 10-6 Server Data Store Example with itemType Specified

var serverDataStorePortletExt = new com.bea.wlp.dvt.data.stores.ServerDataStore({
    url: "http://localhost:7001/portal_1/bea/wlp/api/portlet/list",
    itemType: "tests.MyPortletItem"
});

serverDataStorePortletExt.fetch({query: {webapp: "portal_1"}, count: 5,
    onComplete: function(/*Item array*/items, /*Object*/request) {
        var dataDiv = "<div id=\"dataDivPortletExt\">";
        dataDiv += "<p><b>PortletsExt</b></p>";
        for(var i=0;i<items.length;i++) {
            dataDiv += serverDataStorePortletExt.getValue(items[i], "title") + "</br>";
        }
    dataDiv += "</div>";
    wlp_dvt_dojo.byId('storePortletDataExt').innerHTML = dataDiv;
    }
});

Example 10-7 MyPortletItem Class

wlp_dvt_dojo.provide("tests.MyPortletItem");
wlp_dvt_dojo.require("com.bea.wlp.dvt.data.item.PortletItem");
 
wlp_dvt_dojo.declare("tests.MyPortletItem", com.bea.wlp.dvt.data.item.PortletItem, {
    _getItemArray: function(/*array of raw items*/rawItemArray) {
        // summary:
        //  Given an array of raw items, creates typed objects
        //  This method is item type specific
        if(wlp_dvt_dojo.isArray(rawItemArray)) {
            // create typed PortletItem objects
            var i;
            var itemArray = [];
            for(i=0;i<rawItemArray.length;i++) {
                itemArray[i] = new tests.MyPortletItem(rawItemArray[i]);
                itemArray[i].title[0] = itemArray[i].title + "_MP"; 
            }
            return itemArray;
        } else {
            throw new Error("_getItemArray expects an array of raw items.");
        }
    }
});

The code in Example 10-7 extends the PortletItem class. By doing so, the class conforms to the com.bea.wlp.dvt.data.item.Item API. This class simply adds the string "_MP" to the end of the title of each portlet.

Instead of changing the data type that the data store expects, you can add a data type to the built-in list of data types that the data store can access by using the function:

registerTypes: function(/*array* types)

The types argument is an array of fully qualified type names that must conform to the com.bea.wlp.dvt.data.item.Item API.

Example 10-8 illustrates how to use a mixin with the pluggable data format handler feature of the data store. This example sets up a data store that consumes data in XML format. This data store consumes look and feel data items that are retrieved in XML. This code sets up a data store with a dataFormat handler that handles XML data.

Example 10-8 Using a Mixin with a Data Format Handler

var serverDataStore = new com.bea.wlp.dvt.data.stores.ServerDataStore({
    url: "http://localhost:7001/portal_1/bea/wlp/api/lookandfeel/list",
    dataFormatHandler: function(data, format) {
        if(!data) {
            return null;
        }
 
        if(/xml/i.test(format)) {
            var json = com.bea.wlp.dvt.util.xmlobjectifier().xmlToJSON(data);
            return json;
        }
 
        console.error(this.substitute(this.messages.unsupportedDataFormat, ["xml", format]));
        return null;
    }
});

The mixin used in Example 10-8 sets up a look and feel data item that can handle XML data. Example 10-9 lists the code for the LAFItemMixin class. The ability to handle XML is provided by overriding the isSupported() and getRawItemArray() functions.

Example 10-9

wlp_dvt_dojo.setObject("com.bea.wlp.dvt.data.item.LAFItemMixin", {
    isSupported : function(/*Object*/data, format) {
        if(format == "xml") {
            if(data && data.lookandfeels) {
                if(data.lookandfeels.length > 0 &&
                   data.lookandfeels[0].lookandfeel_details &&
                   data.lookandfeels[0].lookandfeel_details.length > 0) {
                   return {isSupported: true, isEmptyData: false};
                } else {
                   return {isSupported: true, isEmptyData: true};
                }
            } else {
                return {isSupported: false, isEmptyData: true};
            }
    } else {
        return {isSupported: false, isEmptyData: true};
    }
},
    _getRawItemArray : function(/*Object*/data, format) {
 
        // This method is item type specific
        // Each item type knows the structure of data it handles
        // and after parsing out any top level objects, returns the
        // raw array of items
        if(!data) {
            throw Error(messages.invalidDataItem);
        }
        if(!/xml/i.test(format)) {
            return null;
        }
        var arr = data.lookandfeels[0].lookandfeel_details;
        // massage data to remove unwanted attributes
        var returnArray = [];
        for(var i=0;i<arr.length;i++) {
            returnArray[i] = { label: arr[i].markup_name[0].Text,
            title: arr[i].title[0].Text, description: arr[i].description[0].Text,
            created_date: arr[i].created_date[0].Text, modified_date: arr[i].modified_date[0].Text
        };
    }
    return returnArray;
}});