Creating An Application Module - Sage/argos-sdk GitHub Wiki
An application module is a way to encapsulate additional application functionality and customizations without needing to modify the host applications code.
It is recommended you have the following base folder structure:
+---mobile
+---argos-sdk
+---products
+---argos-saleslogix
+---argos-sample
\---<< other products and modules >>
You will want to create the base folder for your module inside of the products
folder. The name of the folder should be prefixed with argos-
as the primary build scripts currently require it to be. The folder structure inside of the base folder is entirely up to you, though we use, and recommend, the following:
+---products
+---<< prefixed module name, i.e. argos-sample >>
| .gitignore
| README.md
| index-dev-<< module name >>.html
| module-fragment.html
| module-info.json
|
+---build
| release-template.jsb2
| release.cmd
| release.jsb2
| release.sh
|
+---configuration
| \---<< module name >>
| development.js
| production.js
|
+---content
| +---images
| \---css
| << prefixed module name >>.css
|
+---localization
| en.js
|
\---src
| ApplicationModule.js
|
\---Views
The rest of the guide will assume that you have used the folder structure above.
In order to test your module, you'll need to create a development shell to run it in. The best way to do this is to copy the index-dev.html
from the product for which your module is target, place it in the base folder, and rename it to index-dev-<< module name>>.html
, i.e. index-dev-sample.html
.
Before we can go any futher, we need to decide on the namespace that we will use for all classes inside of the module. In our own modules, we use Mobile.<< name >>
, i.e. Mobile.Sample
or Mobile.SalesLogix
. Once you have decided on a namespace, use it to setup our references - correlating to the define()
file paths, e.g.:
<script type="text/javascript">
require({
baseUrl: "./",
packages: [
{ name: 'dojo', location: '../../argos-sdk/libraries/dojo/dojo' },
{ name: 'dijit', location: '../../argos-sdk/libraries/dojo/dijit' },
{ name: 'Sage/Platform/Mobile', location: '../../argos-sdk/src' },
{ name: 'Mobile/SalesLogix', location: 'src' },
{ name: 'Mobile/Sample', location: '../argos-sample/src' },
{ name: 'configuration/sample', location: '../argos-sample/configuration' },
{ name: 'localization/sample', location: '../argos-sample/localization' }
]
});
</script>
Please make sure the relative folder paths are originating from the target product folder and not from the custom module.
Then to finish off the index-dev-sample.html
file we need to point the initialization code to require (load) our custom module:
<script type="text/javascript">
(function() {
var application = 'Mobile/SalesLogix/Application',
configuration = [
'configuration/sample/development'
];
require([application].concat(configuration), function(application, configuration) {
var localization = [
'localization/en',
'localization/saleslogix/en',
'localization/sample/en'
];
require(localization.concat('dojo/domReady!'), function() {
var instance = new application(configuration);
instance.activate();
instance.init();
instance.run();
});
});
})();
</script>
The configuration/sample/development
file will be covered further in this document and localizaton/sample/en
will be covered in the localization article.
Now that our dev file is finished it must be either symlinked to, or copied to, the product's base folder. The .gitignore
file for the product should be aware of these development shells, and they should not be included in any commits for the product.
To setup a symlink (recommended), you can do the following (from the product's folder):
Windows:
mklink index-dev-sample.html ..\argos-sample\index-dev-sample.html
Unix:
ln -s ../argos-sample/index-dev-sample.html index-dev-sample.html
If the product's development shell is updated, changes should be easily merged into your shell, or, if no merge is desired, the shell can be easily recreated.
Open up the ...\src\ApplicationModule.js
file. First you will need to declare the ApplicationModule class, like so:
define('Mobile/Sample/ApplicationModule', [
'Sage/Platform/Mobile/ApplicationModule'
], function() {
return dojo.declare('Mobile.Sample.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, {
// customization code
});
});
Those familiar with dojo, or any AMD type loading system, will find the above normal, but to expand upon it lightly: the define()
call provides 1) a file path to the file; 2) dependencies and 3) a factory function.
- The file path base url is defined in the
index-dev-<< module name>>.html
file, then you add the appropriate folder levels then the file name. Breaking down this sample:
- `Mobile/Sample/ApplicationModule` = `../argos-sample/src/ApplicationModule.js`
- Just remember to reference your namespace setup in the `index` file and add the folder levels.
-
Dependencies is an array of
define()
file paths that must be loaded before our attached function runs. InApplicationModule
we will need to have its base class loaded, as well as any additional javascript files we add - such as new Views. -
The factory function will run once all dependencies are loaded and will return an
export
object as the main object of thisdefine()
so that other files may use this object as one of their dependencies.
The dojo.declare()
call defines a namespace, any base classes to inherit from, and lastly an object that will be mixed-in. Mix-in in this context means to copy (and overwrite) any properties from the passed object to the new object.
Now we can move onto implementation.
In most general terms, when we are implementing a module, we are looking to do two things: create new views and customize existing ones. The Sage.Platform.Mobile.ApplicationModule
class provides hooks for us to do both of these things. We'll want to override the loadViews
and loadCustomizations
methods of the ApplicationModule class, 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);
},
loadCustomizations: function() {
this.inherited(arguments);
}
});
});
Dojo handles proper inheritance chaining so that you can easily call the superclass method by calling this.inherited()
. Using that call in the above two functions ensures that any code in its base Sage.Platform.Mobile.ApplicationModule
will be called.
Creating and registering new views and customizations will be covered in other articles.
In the ...\build
folder, you'll see four files listed in the structure above: release-template.jsb2
, release.cmd
, release.jsb2
and release.sh
. The release.cmd
(Windows) and release.sh
(Unix/Linux) files start the build process with all the correct options; it is best to copy this file from another module. The release.jsb2
is the actual build project and is what we we need to create for our module. A basic build project a sample module would look like:
{
"projectName": "Sample Module",
"licenseText": "",
"deployDir": "deploy/",
"pkgs": [{
"name": "Sample Module",
"file": "content/javascript/argos-sample.js",
"isDebug": true,
"fileIncludes": []
}],
"resources": [
{
"src": "../content",
"dest": "content",
"filters": ".*(\\.css|\\.jpg|\\.png|\\.gif)"
},
{
"src": "../configuration",
"dest": "configuration",
"filters": ".*\\.js"
}
]
}
Starting with that as our base, we need to add the ApplicationModule.js
file that was created previously. You can do this by adding an item to the fileIncludes
array property:
fileIncludes: [{
"text": "ApplicationModule.js",
"path": "../src/"
}]
The reason that we have to use a relative path for the path
property is that all file includes are based off of the jsb2 file's path. As you add more Views or additional files make sure to continue adding to the fileIncludes[]
array with the name and path.
In the ...\configuration
folder, there are two files, development.js
and production.js
, which contain application configuration for development, and production, respectively. The format for both is the same, as they use the mergeConfiguration()
function to merge in new modules and, optionally, new SData connections:
define('configuration/sample/development', ['configuration/development', 'Mobile/Sample/ApplicationModule'], function(baseConfiguration) {
return mergeConfiguration(baseConfiguration, {
modules: [
new Mobile.Sample.ApplicationModule()
],
connections: {
'crm-sample': {
isDefault: true,
offline: true,
url: 'http://50.16.242.109/sdata/slx/dynamic/-/',
json: true
}
}
});
});
Notice that we add a new instance of our ApplicationModule to the modules list; this is how the application knows to load our module. In the connections
property we can define any and all SData connections we need. The key used here to define the SData connections is the same that the views will need to have as their serviceName
property, unless they will be using the default, application provided connection.
Open the module-fragment.html
file in the base folder and add your stylesheet and final build javascript file path, e.g.:
<!-- Sample -->
<link type="text/css" rel="stylesheet" href="content/css/sample.css" />
<script type="text/javascript" src="content/javascript/argos-sample.js"></script>
This fragment will be injected into all index
files when the module is deployed via AA. If you are not deploying via AA you will then to manually copy and paste the above lines directly into index.aspx
and index-nocache.aspx
at the <!-- {{modules}} -->
marker to get the following result:
<!-- Application -->
<script type="text/javascript" src="content/javascript/argos-saleslogix.js"></script>
<!-- Modules -->
<!-- Sample -->
<link type="text/css" rel="stylesheet" href="content/css/sample.css" />
<script type="text/javascript" src="content/javascript/argos-sample.js"></script>
If you plan to use the static html versions, index.html
and index-nocache.html
, then you forfeit the configuration and localization detection provided by the dynamic adapter. Follow the same instructions for the aspx
versions (above) by injecting the fragment then proceed to specify your configuration and locale directly:
var application = 'Mobile/SalesLogix/Application',
configuration = [
'configuration/production',
'configuration/sample/production'
];
require([application].concat(configuration), function(application, configuration) {
var localization = [
'localization/en',
'localization/saleslogix/en',
'localization/sample/en'
];
In the base folder there is a module-info.json
file. This file reports information about your module to AA so that easier versioning, naming and targeting may be applied. A sample module-info.json
file is as follows:
{
"name": "sample",
"displayName": "Sample Customizations",
"description": "A sample module implementation that shows how to customize the Sage SalesLogix Mobile client.",
"majorVersion": 1,
"minorVersion": 1,
"buildNumber": 0,
"targetProduct": "saleslogix"
}
Please ensure that your file passes JSON
validation (double quotes, properly escaped, etc).
First, save this (gist)[https://gist.github.com/815451] as build-module.cmd
to the same folder where build-product.cmd
and the argos-sdk
reside. After this is done, you can build your module by executing the build-module.cmd
script and passing in the module name as the argument, e.g.:
build-module sample
The output of the build will be in a folder named after the module inside of the deploy
folder, i.e. ...\mobile\deploy\argos-sample
.
In order to deploy your module, you'll need to copy the output of the build, to the folder where the product is deployed. The copy process may complain about existing folders; You'll want to allow it to copy into existing folders.
If the dynamic manifest, index.manifest.ashx
is not being used, you must update the static manifest, index.manifest
, and place the files into it.
A couple more things must be done if your module creates it's own SData connections. First, you'll need to add your SData endpoints to the NETWORK:
section of the manifest files, index.manifest
and template.manifest
, e.g.:
NETWORK:
../sdata/
http://sample-server/sdata/
Next, if your SData host is on another server, you'll need to enable Cross-Origin-Request-Sharing (CORS). You can find a document on how to set it up for IIS at: Setting-Up-CORS.