Skip to content

Creating A Detail View

tremendo edited this page Dec 12, 2011 · 3 revisions

Prerequisites

It is recommended that you read through the Creating An Application Module guide as we will be using it as our starting point.

Determining What To Show

The first step to creating a detail view is to determine what resource, and what properties, to show. If you do not already know what data you want to see, one good way to find out is to explore the SData feeds directly.

Creating The View

First, we must create a file to contain the view's source. If you are using the folder structure outlined by the Creating An Application Module guide, we place all views inside of the ...\src\views folder, under a sub-folder for each resource, named the singular of the resource kind. For example, if the resource kind for the view is accounts we would place the source file for the view in the ...\src\views\account folder. For a list view, we name this file Detail.js, though you can change the name if you would like.

In the Creating An Application Module guide, we decided on a base namespace that would be used for all of our classes. For the views, we generally contain those within a unique namespace for each resource, i.e. Mobile.Sample.Account. Once you have decided on a namespace, you can declare it in the file, e.g.:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {});

Next, you will need to declare the view class, e.g.:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
	return dojo.declare('Mobile.Sample.Views.Account.Detail', [Sage.Platform.Mobile.Detail], {
		id: 'account_detail',
		editView: 'account_edit'
	});
});

All detail views should, either directly or indirectly, inherit from the platform class Sage.Platform.Mobile.Detail, which handles most of the work required to setup and use the view. Also, as you can see, we've defined some basic properties on the view's prototype, which are:

  • id: The default id for the view, if none is passed as part of the options to the constructor. Each view instance must have a unique id.
  • editView: The view to be shown when a user clicks the edit button; This view will be passed the current entry.

Setting Up The SData Query

While the Sage.Platform.Mobile.Detail class handles most of the interaction with SData, we do need to provide it some information, namely, the resource kind to query and the properties to select. We do this by declaring some additional properties on the view's prototype:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
    return dojo.declare('Mobile.Sample.Account.Detail', [Sage.Platform.Mobile.Detail], {
		id: 'account_detail',
		editView: 'account_edit',

		resourceKind: 'accounts',
		querySelect: [
			'Name',
			'Manager/Name'
		]
	});
});
  • resourceKind: The resource kind the view is responsible for and the default used for querying SData.
  • querySelect: An array of properties to select from the resource.

For more information on these properties, please refer to the SData Specification.

Displaying The Data

Now that we've defined what we want to query SData for, we can now create a layout to display the data. To do this we need to override the createLayout method in order to return our new layout, e.g.:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
    return dojo.declare('Mobile.Sample.Account.Detail', [Sage.Platform.Mobile.Detail], {
		/* ... snip ... */

		createLayout: function() {
			return this.layout || (this.layout = [{
			}]);
		}
	});
});

This is a bit more complicated that what we've seen so far, but what is going on here is that we are returning this.layout if it exists and has been set, and if not, we are creating a new layout, assigning it to this.layout, and then returning it. The main reason for this is performance; We only generate the layout once, and then cache it for all subsequent calls to createLayout. Also, the customization engine looks at the result of createLayout to determine if customizations need to be reapplied. Since we are returning the same object for all subsequent calls, the customization engine knows that the layout has not changed, thus customizations do not need to be reapplied.

Now, let's add some items to the layout:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
    return dojo.declare('Mobile.Sample.Account.Detail', [Sage.Platform.Mobile.Detail], {
		/* ... snip ... */

		createLayout: function() {
			return this.layout || (this.layout = [{
				title: 'Details',
				children: [{
					property: 'Name',
					label: 'name'
				},{
					property: 'Manager.Name',
					label: 'manager'
				}]
			}]);
		}
	});
});

A layout, at its most basic, is an array of sections (that contain rows). Above, the first section in the layout array, is indicated by the title and children properties it contains. The children property contains a sub-layout. In the above example, we've defined two rows that display data. There are numerous properties you can define for row layout items, but property and label are the most basic for displaying data. Some of the most common properties are:

Sections

  • title: The title for the section.
  • list: True to display the section as a list, instead of label and property rows. More on this later.

Rows

  • property: The dot-path name of the property to display. The name of the row/field.
  • label: The label to display.
  • renderer: A function that is passed the value for the row and returns the rendered result.
  • tpl: A template that is called to render the value for the row.

Displaying Related Data

Now, let's add a link to some related data for the current entry. In order to do this, we're going to add a 2nd section to the layout, e.g.:

define('Mobile/Sample/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
    return dojo.declare('Mobile.Sample.Account.Detail', [Sage.Platform.Mobile.Detail], {
		/* ... snip ... */

		createLayout: function() {
			return this.layout || (this.layout = [{
				title: 'Details',
				children: [{
					property: 'Name',
					label: 'name'
				},{
					property: 'Manager.Name',
					label: 'manager'
				}]			
			},{
				title: 'Related Items',
				list: true,
				children: [{
					icon: 'content/images/icons/project.png',
					label: 'Projects',
					where: this.formatRelatedQuery.createDelegate(
						this, ['Account.id eq "${0}"'], true
					),
					view: 'project_related'
				}]
			}];
		}
	});
});

We've added a new section and set the list property to true in order to change how the items are displayed; Instead of being displayed as lable/value pairs, the items will be displayed as a list, with the icon and the label being shown.

The where property is passed to the indicated view, specified by the view property, in order to filter the results, and can be either an SData query string, or a function that returns an SData query string. In the example above, we are creating a delegate to the formatRelatedQuery function, which takes the following as parameters: an entry, a format string, and a property name (defaults to $key). When formatRelatedQuery is called, the code calling it only passes in the entry, which is where createDelegate comes in.

The 1st parameter to the createDelegate ensures that this, i.e. the scope, is the same when the method is called. The 2nd parameter is a list of additional arguments that will be supplied when the delegate is called; Here we are specifing the format string as the only additional argument. Lastly, the 3rd parameter indicates that we want to append the additional parameters to the original call, so the final result will be that the formatRelatedQuery function is called with the current entry, passed in by the calling code, and our format string, which we specified while creating the delegate.

Registering The View

Before the view can be used, it must be registered with the application. This should be done in the loadViews method of your application module, 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);

			this.registerView(new Mobile.Sample.Account.Detail());
		}
	});
});

Adding The View To The Development Environment

In order to test out the view in the development shell, a reference to the view's source and configuration in our sample must be added to the packages required before initializing the application, e.g.:

    <!-- Dojo -->
    <script type="text/javascript" src="../../argos-sdk/libraries/dojo/dojo/dojo.js" data-dojo-config="parseOnLoad:true, async:true"></script>
    <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' }, // Argos Sample
			{ name: 'configuration/sample', location: '../argos-sample/configuration' },
			{ name: 'localization/sample', location: '../argos-sample/localization' }
			]
     });
    </script>

Adding The View To The Build Script

Just like we did for the ApplicationModule.js in the Creating An Application Module guide, we have to add the new view to the fileIncludes section of the build script, e.g.:

"fileIncludes": [{
	text: 'Detail.js',
	path: '../src/Views/Account/'
},{
  "text": "ApplicationModule.js",
  "path": "../src"
}]