Views Life Cycle - Sage/argos-sdk GitHub Wiki

#Views

Views are the most important UI construct of the Mobile app, as the user always sees and interacts with a single view. At the highest/SDK level we have the View class from which the List, Detail and Edit views inherit - each with their own methods and differences.

##Life Cycle

###Creation Each View should be contained within its own js file, this enables the ApplicationModule loading system (and build) to only grab the specific files needed. When using define() to declare the View, it should inherit from one of the main Views (List, Detail, Edit):

define('Mobile/SalesLogix/Views/Account/Detail', ['Sage/Platform/Mobile/Detail'], function() {
    return dojo.declare('Mobile.SalesLogix.Views.Account.Detail', [Sage.Platform.Mobile.Detail], {
        // view code
    })
});

Then it must be added to the list of required objects within the ApplicationModule.js -- it is the second parameter of define(). This states that the file should load before defining (running) ApplicationModule.

define('Mobile/SalesLogix/ApplicationModule', [
    'Mobile/SalesLogix/Views/Account/Detail'
], function(){...})

Within Application Module you will need to register an instance of your view:

loadViews: function(){
    this.registerView(new Mobile.SalesLogix.Views.Account.Detail());
}

Note that View constructors will mixin a passed object into the View itself - this is useful for declaring multiple instances with different ID's:

this.registerView(new Mobile.SalesLogix.Views.Account.List({
    id: 'account_related'
}));

Congratulations! You now have a View that is added to the app upon initialization. Next we will go over the creation and initialization cycle.

###Initialization

All Views are dojo dijit widgets and inherit that lifecycle fully (http://dojotoolkit.org/documentation/tutorials/1.6/understanding_widget/).

A number of things occur when you create an instance of your view:

  1. constructor - called from new widget(). Sets up basic connections (see _Widget).
  2. postscript - happens before "creation" of the widget. Fires create().
  3. create - first it sets up connections. Sets a unique ID then starts the real lifecycle.
  4. postMixinProperties - called before the template/rendering happens after mixing in any properties.
  5. buildRendering - see _Templated. The View gets rendered (using the defined widgetTemplate) and added to the DOM. Any attach-events are now parsed and connected.
  6. postCreate - widget has been created and placed. Child widgets (if applicable) will now be parsed/created and go through the same cycle.
  7. startup - fires after any DOM fragments have been added and any potential child widgets have been created and started. So a user goes to the page, everything gets loaded, views are registered, each view goes through the above process - then the Application fires the init() function of each view.
  8. init - like a secondary startup(). Will possibly be removed once all Views move their init() code into its startup().

The only two of the above that would normally be customized are postCreate() and startup(). For an example see the base List View. In postCreate() it creates an instance of a search widget and adds it as a child. In startup() it passes options and creates a connection to the now initialized search widget.

Rule of Thumb:

Extend postCreate() for adding child widgets. Extend startup() for establishing connections, setting options.

By extend, meaning use appropiate javascript chaining to make sure that the correct functions are called. Normally when mixing objects the new object properties will override any existing ones: (http://dojotoolkit.org/reference-guide/dojo/declare.html#inheritance-info)

var a = { test: true};
var b = { test: false};
var c = dojo.mixin(a, b); // b gets mixed into a
c.test; // returns false

Now, what dojo does that is special is inherited(), this relies on the declare() call, tying to its objects that were "mixed in". Calling this.inherited() will actually call the superclass (parent if you will) code of that same function:

define('Mobile/SalesLogix/Views/Activity/Edit', ['Sage/Platform/Mobile/Edit'], function() {

    return dojo.declare('Mobile.SalesLogix.Views.Activity.Edit', [Sage.Platform.Mobile.Edit], {
        postCreate: function(){
            this.inherited(arguments); // calls the parent, which may in turn calls its parent and so on
            
            // my code that happens after all the Edit "class" postCreate, which called View.postCreate, and so on to _WidgetBase.postCreate
        }
    });
});

Next in the lifecyle is how transitions work.

###Transitioning

First we will go over the sequence of events, then how to move to a new View.

All the transition, showing, hiding, animation is handled by the ReUI framework which is a fork of iUI (http://www.iui-js.org/). It starts with a View calling the show() method of another View.

  1. show (View) - checks if onShow returns true, checks if refreshRequired should be set, sets the Toolbar title and then calls ReUI show
  2. show (ReUI) - first it handles the page history and url hash, then as the animation starts and completes it fires various events to the from and to Views
  3. load (to page)
  4. blur (from page)
  5. focus (to page)
  6. unload (from page)
  7. beforetransition (from page)
  8. beforetransition (to page)
  9. (animation happens)
  10. aftertransition (from page)
  11. aftertransition (to page)
  12. activate (to page) - only happens when pressing back or browser back

Those are the low level events at the ReUI level, they are listened and get mapped to the following View level functions (that you can extend):

  1. onBeforeTransitionAway (from page)
  2. onBeforeTransitionTo (to page)
  3. onTransitionAway (from page)
  4. onTransitionTo (to page) - recreates the top toolbar (calls the to page createToolLayout(), then loads toolbar customizations) and checks if refreshRequired is true and calls refresh()
  5. refresh (to page) - if invoked from onTransitionTo if refreshRequired = true
  6. onActivate (to page) - if transition was started by back button

To navigate to a view you need to know the target view's id then you can invoke the following to start the transition process:

var view = App.getView('targetId'); // get a reference to the view
var options = {}; // construct any options (specific to your target view)
view.show(options);

For options there are several properties that work for all views:

  • reverse: True if the transition is a reverse transition (right/down), False otherwise.
  • track: False if the transition should not be tracked in history, True otherwise.
  • update: False if the transition should not update title and back button, True otherwise.
  • returnTo: a negative integer describing what the history item position should be, typically used with -1

Example of returnTo:

A -> B (current) -> C

If you're at view B and call App.getView('C').show({returnTo:-1});. Then C will be shown:

A -> B -> C (current)

If the user presses back it will return to A:

A (current) -> B -> C

A live example: adding an activity from a Calendar View:

Calendar -> Activity Type Picklist -> Edit Activity

The picklist calls activity edit with returnTo -1, so that when the activity is saved (and back is called) the user goes back to Calendar.

All other view options are tailored to the view it is going to, like entry going from Detail to Edit, or currentDate between Calendars.

That is it for the base View. All other functionality is within each particular implementation.

##List

A List View has many different pieces - the following are the most commonly extended properties and functions:

Properties:

  • Templates: all templates are Simplates which is a custom templating engine: https://github.com/SageScottsdalePlatform/simplate
  • widgetTemplate (viewTemplate in v1.2/Ext) - the main container.
  • rowTemplate (itemTemplate in v1.2/Ext) - the list item template for every row.
  • itemTemplate (contentTemplate in v1.2/Ext) - the contents of the rowTemplate, change this in every List View based on your entity properties (and how to display them).
  • id: unique name of the view.
  • expose: boolean, set to false if you don't wish this List to show up under Home View Configuration.
  • resourceKind: the SData resource kind that all requests will be made to.
  • querySelect: an array of strings indicating what fields the SData request should return.
  • queryInclude: an array of strings of child properties to be included in the SData request.
  • queryOrderBy: the order expression to return SData results.
  • queryWhere: the default where expression for SData requests.
  • pageSize: the amount of items per request to be returned.
  • enableSearch: set to false to hide the Search widget.
  • detailView: the id of the detail view associated (will be navigated to when a list item is selected).
  • insertView: the id of the edit view associated (will be navigated to when add new item is selected).
  • editView: if no insertView is specified then this id will be navigated to.
  • hashTagQueries: hash object, the key is the hash tag sans symbol and the value is the where expression to be passed to SData.
  • titleText: Text to be set in the top toolbar.

Methods:

  • postCreate() - as described in Initialization.
  • startup() - as described in Initialization.
  • createToolLayout() - returns the this.tools object which has tbar for topbar defined:
createToolLayout: function() {
    return this.tools || (this.tools = {
        'tbar': [{
            id: 'new', // this will be used for aria as well as identification in customizations
            action: 'navigateToInsertView', // the function to be called upon click
            security: App.getViewSecurity(this.insertView, 'insert') // optional tie-in to securedActions/roles
        }]
    });
}

To remove all tools, set tbar: [] as an empty array.

  • formatSearchQuery() - returns a string to be used in the SData where expression.
formatSearchQuery: function(query) {
    return dojo.string.substitute('AccountNameUpper like "${0}%"', [this.escapeSearchQuery(query.toUpperCase())]);
}
  • processFeed(feed) - the result of the SData query will be sent to processFeed. If you wish to bypass the default layout rendering (or do something else with the feed) then look into this function.
  • requestData() - called from refresh() (which is called onTransitionTo), this will construct a SData query and fire it, if you wish to not make an SData call then look into this function.
  • refreshRequiredFor(options) - This gets passed the show options (see Transitioning) this should return true or false if the view should be refresh based upon what is in the options. By default it checks for changes in where, resourceKind, resourcePredicate.
  • refresh() - calls requestData.
  • clear() - empties any selections, empties any SData results and clears its DOM.

##Detail The Detail View is simpler than List and most of the functionality is designed to be customized through registerCustomization.

Properties:

  • id: unique name of View
  • security: a string of the securedAction required to view this entity. Ex: 'Entities/Account/View'
  • titleText: the default top toolbar title, will probably be overwriten during show() as List views pass the items $descriptor as a title.
  • editView: string id of the Edit view when selecting Edit.
  • querySelect: an array of strings of fields to select from the SData query. Note that these same fields will be passed to Edit view (as Edit view does not make a new request).
  • resourceKind: the SData resource kind that all requests will be made to.

Methods:

  • postCreate() - as described in Initialization.
  • startup() - as described in Initialization.
  • createLayout() - this is the crucial function, all Detail views should override this and return their own custom layout.
  • createToolLayout() - returns the this.tools object which has tbar for topbar defined (see List description for example).
  • processLayout(layout, entry) - this constructs the DOM content for all the rows defined in createLayout().
  • processEntry(entry) - this saves the SData result, applies any layout customization and calls processLayout.
  • requestData() - creates the SData requests and fires it, look into this if you don't want to use SData.
  • refreshRequiredFor(options) - This gets passed the show options (see Transitioning) this should return true or false if the view should be refresh based upon what is in the options. By default it checks for a change to key.
  • refresh() - rechecks security, if good calls requestData()
  • clear() - replaces all content with the emptyTemplate markup.

The layout of Detail is processed at every refresh. This is the opposite of the Edit layout which is made once and only the values change on refresh.

See the Detail View Layout wiki for all the layout options.

##Edit

Edit Views have a dual-purpose: for editing and for inserting. Many of the functions within Edit Views have conditional logic or are coded in a way to support both cases.

Interesting tidbits about Edit Views:

  • Layout is only processed and rendered once.
  • No SData requests are made except for the entity levels defaults ($template).
  • The majority of the interaction logic is within the Fields, not the Edit View itself.

Properties:

  • id: unique name of View.
  • insertSecurity: a string of the securedAction required to insert this entity. Ex: 'Entities/Account/Add'.
  • updateSecurity: a string of the securedAction required to edit this entity. Ex: 'Entities/Account/Edit'.
  • titleText: the default top toolbar title, might be overwritten for Detail->Edit as the Detail view will pass its title in the options.
  • querySelect: an array of strings of fields to select from the SData query. Note! This field is only for inserting new items as it only makes an entity $template request. For Detail->Edit all values are passed from Detail to Edit.
  • resourceKind: the SData resource kind that the entity $template request will be made to. As noted in querySelect this only applies to getting the defaults ($template) when inserting new items.

Methods:

  • postCreate() - as described in Initialization.
  • startup() - as described in Initialization.
  • createLayout() - this is the crucial function, all Edit views should override this and return their own custom layout.
  • createToolLayout() - returns the this.tools object which has tbar for topbar defined (see List descriptiion for example).
  • processLayout(layout, layoutOptions) - this constructs the DOM content for all the fields defined in createLayout, note this only happens once at Initialization for Edit views.
  • setValues(values, initial) - loops through all fields and sets their value if a match is found for name in the passed values object, if initial is true it will set the value as the initial (for dirty logic). Note this function is called several times during a refresh.
  • getValues(all) - if all is true, returns all values, else it loops the fields and skips include:false and exclude:true, and any hidden fields are also skipped.
  • insert() - called by pressing save and options.insert = true. This will construct an SData request with the form values without any $key or identification causing it to insert a new record.
  • update() - called by pressing save and options.insert = false or undefined. This will construct an SData request with the form values with any $key and $etag causing it to update the matching record.
  • refresh() - this will: 1) clear all field values; 2a) if options.inserting = true, fire the SData $template request; else 2b) process and set the values from options.entry as initial values and if passed, set options.changes values as dirty values.