Customizing An Existing Application - Sage/argos-sdk GitHub Wiki

Prerequisites

It is recommended that you read through the Creating An Application Module guide, the Creating A List View guide, and the Creating A Detail View guide, as we will be expanding on the information presented.

Methods Of Customization

There are essentially three types of customizations: registered customizations, prototype customizations, and complete customizations. The first, registered customizations, are customizations that are registered with the application and are applied to the layout of the detail and edit views. Support for this type of customization for list views is planned for a future release.

The second type of customization, prototype customizations, are customizations that are applied to a class's, generally a view's, prototype, and either extend the functionality of the view, or override it.

The last type of customization, complete customizations, are complete custom views that live on their own. This type of customization will be discussed later.

Registered Customizations

The most common type of customization, registered customizations, are most commonly registered with the application, via the registerCustomization method, as part of a module's initialization process, specifically in the loadCustomizations method, e.g.:

define('Mobile/Sample/ApplicationModule', [
    'Sage/Platform/Mobile/ApplicationModule'
], function() {
    return dojo.declare('Mobile.Sample.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, {
        loadViews: function() {
            this.inherited(arguments);

            //Add a quick action to Account Detail
            this.registerCustomization('detail', 'account_detail', {
                at: function(row) { return row.action == 'addNote'; },
                type: 'insert',
                where: 'before',
                value: {
                    value: this.helloWorldValueText,
                    label: this.helloWorldText,
                    icon: '../argos-sample/content/images/icons/Hello_World_24.png',
                    action: 'showHelloWorld'
                }
            });
            
        },
        loadCustomizations: function() {
            this.inherited(arguments);
        }
    });
});

The registerCustomization method takes three parameters: the customization set, the view id, and the customization object. The first parameter, the customization set, has two parts (second is optional):

  1. Base View Type. A view type that supports registered customizations, i.e., detail, edit and list.

  2. Optional, Sub Customization. A sub customization is one that is not directly related to the View layout, but one of its supporting, or sub pieces. Currently available are /tools and /hashTagQueries. If omitted then it will act on the View itself.

The second parameter is the identifier (id) of the view to be modified. A falsy can be passed for this parameter in order to register a customization for all views in a given set, though this use case is uncommon.

The last parameter to the registerCustomization method, is the customization object and it describes where the change should be made, how it should be made, and what to use for the change. The at parameter defines where the change should be made. The value of this property should always be a function that returns a truthy value when a layout row of interest is found. When customizations are being applied, the view's layout is processed top to bottom, with each row of the layout being passed to the at method, in sequence, and nested sections after all surrounding rows. For example, consider the following layout:

+ layout
	+ sectionA
		- rowA
		+ sectionB
			- rowB
		- rowC
		- rowD
	+ sectionC
		- rowE

The at method for any customization would then receive the layout rows in the following order:

sectionA -> rowA -> rowC -> rowD -> sectionB -> rowB -> sectionC -> rowE

After the location for the change has been determined, the type of change to make is specified by the type property on the customization object and can be one of four values: remove, replace, modify, insert.

The first type, remove, is fairly self explanatory; It will remove the matched layout row. It is worth noting that remove is a "full stop" operation, i.e. any other change to that specific layout row will be ignored.

this.registerCustomization('detail', 'account_detail', {
	at: function(row) { return row.name == 'LeadSource.Description'; },
	type: 'remove'
});

The second type, replace, is also a "full stop" operation, but instead of removing the row from the layout, it completely replaces the layout row with the value specified in the customization object's value property.

The third type, modify, takes the value, and applies it on top of the layout row, overwriting any properties with the same name.

this.registerCustomization('detail', 'account_detail', {
	at: function(row) { return row.name == 'Fax'; },
	type: 'modify',
	value: {
		label: 'fax num'
	}
});

The last type, insert, does exactly that; it inserts the value as a completely new row in the layout. Also, with this type, some additional control as to where the change should be made is provided, via the where property, which accepts the values before or after, the latter of which is the default if none is specified. With the value of before, the new row will be inserted before the row matched by at, and with after, it will be inserted after the matched row.

this.registerCustomization('detail', 'account_detail', {
	at: function(row) { return row.name == 'Type'; },
	type: 'insert',
	where: 'before',
	value: {
		name: 'Region',
		label: 'region'
	}
});

To go in further detail of the at() function: The passed parameter row is the corresponding object in the createLayout() function of the view. Each object (or row) has multiple properties in which you can act on, however, every object will always have a name property that is unique to that layout.

Prototype Customizations

The second type of customization, prototype customizations, are customizations that are applied to a class's prototype in order to extend the functionality of the view or override it. This type of customization is usually accomplished by use of the dojo.extend function, e.g.:

dojo.extend(Mobile.SalesLogix.Views.Contact.List, {
    itemTemplate: new Simplate([
        '<h3>{%: $.NameLF %}</h3>',
        '<h4>{%: $.AccountName %}</h4>',
        '<h4>{%: Mobile.SalesLogix.Format.phone($.WorkPhone, false) %}</h4>'
    ])
});

The dojo.extend function takes two parameters, the class to operate on, and an object containing properties to be added to the class's prototype. In the example above, we are overriding, i.e. replacing, the itemTemplate property of the Mobile.SalesLogix.Contact.List view in order to include the WorkPhone for the contact. If you used the code above directly, there would be a small issue; the WorkPhone isn't being included in the SData results. In order to do that, we need to override another property on the view, querySelect, but do so in a manner that doesn't replace what's already there. We can do so by adding the following:

dojo.extend(Mobile.SalesLogix.Views.Contact.List, {
    querySelect: Mobile.SalesLogix.Views.Contact.List.prototype.querySelect.concat([
        'WorkPhone'
    ]),
    itemTemplate: new Simplate([
        '<h3>{%: $.NameLF %}</h3>',
        '<h4>{%: $.AccountName %}</h4>',
        '<h4>{%: Mobile.SalesLogix.Format.phone($.WorkPhone, false) %}</h4>'
    ])
});

As you can see, we added the querySelect property. Now, this syntax may look a bit strange, but what we are doing here is taking the existing querySelect array, Mobile.SalesLogix.Contact.List.prototype.querySelect, and adding the WorkPhone value to it, and replacing it on the prototype. Notice that while we pass Mobile.SalesLogix.Contact.List to the dojo.extend function, when referring to the prototype directly in our code, we have to use Mobile.SalesLogix.Contact.List.prototype. The reason for the difference is that the dojo.extend function takes care of referencing the prototype internally when applying our overrides.

More Examples

Hash Tags

The following customization adds an additional hash tag filter to List search:

this.registerCustomization('list/hashTagQueries', 'opportunity_list', {
    at: true,
    type: 'insert',
    value: {
        tag: 'g500k',
        query: 'SalesPotential gt "500000"'
    }
});

As you can see it uses a sub-customization set to let the registerCustomization know we are editing the hash tags. The at() function is simply true since hash tags are not ordered and it doesn't matter where the tag is inserted.

For hash tags the value object should have tag and query properties. The tag will be what the user types after a hash (#) to filter a list. Query will be the SData where? query, following normal SData URL query syntax.

If you were to remove or modify an existing tag, you would use something similar to the following:

this.registerCustomization('list/hashTagQueries', 'opportunity_list', {
    at: function(hash) { return hash.key == 'won'; },
    type: 'remove'
});

Note that in the at() function we are looking at the key property, this is because the key will always refer to the base hash text whereas tag may be the localized text depending on your system.

Tools

Here is an example of adding a new button to the toolbar on the Account Detail View:

this.registerCustomization('detail/tools', 'account_detail', {
    at: function(tool){ return tool.id === 'edit'; },
    type: 'insert',
    where: 'after',
    value: {
        id: 'customButton',
        icon: '../argos-sample/content/images/icons/Hello_World_24.png',
        action: 'showHelloWorld',
        security: App.getViewSecurity(Mobile.SalesLogix.Views.Account.Detail.prototype.editView, 'update')
    }
});

As with the hash tag customization, the toolbar customization uses a sub customization of detail/tools to let the program know we want to modify the toolbar of the View. The at() function in this case passes a tool object. A tool object has four properties as we see in the value, but the id is also seen in the HTML <div data-tool="{id}"> in case you need to quickly identify an existing tool. The value has:

  • id - a unique name;
  • icon - relative path to an image;
  • action - the function to execute when clicked (in the current View scope); and
  • security - A String with the Secured Role/Action Access needed to perform the associated action. - App.getViewSecurity(view, access) returns the Access string of a given view ID and optionally the security access type. Currently only Edit Views have security access types as they may act on update and insert actions.
Home View

A common request is to alter the Home View items via a customization, we can do this using the prototype way of overriding the view:

var originalDefViews = Mobile.SalesLogix.Application.prototype.getDefaultViews;
dojo.extend(Mobile.SalesLogix.Application, {
    getDefaultViews: function() {
        var views = originalDefViews.apply(this, arguments) || [];
        views = views.concat(['myCustom_list']);
        return views;
    }
});

In order to do this we save the original Home View function getDefaultViews, then override it but still calling the original function. This populates the views array with the default view ids which you may then add (concat), subtract (splice), and otherwise modify to your liking - returning the modified array.

Complete Customizations

As mentioned previously, the topic of complete customizations are beyond the scope of this document, but will be covered at a later date. These are the more advanced custom views that are outside the realm of a standard List, Detail, or Edit view, and generally inherit from the base View class directly.

If you are interested in creating custom List or Detail views, please refer to the Creating A List View and Creating A Detail View guides.

⚠️ **GitHub.com Fallback** ⚠️