Difference between revisions of "AppSuite:Extending the UI"

m
Line 18: Line 18:
 
* Dynamic
 
* Dynamic
  
[[Image:/Users/frankpaczynski/Documents/extension%20poitns.gif|frame|none|alt=|caption image]]
+
-image-
  
 
== Some characteristics ==
 
== Some characteristics ==
Line 27: Line 27:
 
* diversity: extension points support different extension
 
* diversity: extension points support different extension
  
 
-----
 
  
 
== Components ==
 
== Components ==
  
 
The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:
 
The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:
 
 
* extension point system: accessing the outer parts
 
* extension point system: accessing the outer parts
 
* registry: manages extension points, extensions and their state
 
* registry: manages extension points, extensions and their state
Line 50: Line 47:
  
 
* manages extension points, extensions and their state
 
* manages extension points, extensions and their state
 +
  
 
== list points ==
 
== list points ==
Line 55: Line 53:
 
<pre class="language-javascript">// returns array of point ids
 
<pre class="language-javascript">// returns array of point ids
 
var keys = ext.keys();</pre>
 
var keys = ext.keys();</pre>
 +
 +
 
== get/create point ==
 
== get/create point ==
  
Line 64: Line 64:
 
* part of the systems that can be extended, referenced by a unique id
 
* part of the systems that can be extended, referenced by a unique id
 
* defienes as some kind of contract about what it expects its extensions to provide
 
* defienes as some kind of contract about what it expects its extensions to provide
 +
  
 
== attributes ==
 
== attributes ==
Line 69: Line 70:
 
* id
 
* id
 
* description (optional)
 
* description (optional)
 +
  
 
'''''example'''''
 
'''''example'''''
Line 76: Line 78:
 
     descr = point.description = '';
 
     descr = point.description = '';
 
</pre>
 
</pre>
 +
 +
 
== add extension ==
 
== add extension ==
  
 
* important: existing extensions with same id will not be overwritten - use replace insted
 
* important: existing extensions with same id will not be overwritten - use replace insted
 
* example: add extension with id 'date'
 
* example: add extension with id 'date'
 +
  
 
'''''example'''''
 
'''''example'''''
Line 91: Line 96:
 
     }
 
     }
 
});</pre>
 
});</pre>
 +
 +
 
== replace extension ==
 
== replace extension ==
  
 
* important: only extension properties will be replaced (jquery extend)
 
* important: only extension properties will be replaced (jquery extend)
 
* hint: replace can also be executed before extension is intially created with extend function
 
* hint: replace can also be executed before extension is intially created with extend function
 +
  
 
'''''example'''''
 
'''''example'''''
Line 106: Line 114:
 
     }
 
     }
 
});</pre>
 
});</pre>
 +
 +
 
== use extensions==
 
== use extensions==
  
Line 113: Line 123:
  
 
<pre class="language-javascript">mypoint.invoke(name, context, baton);</pre>
 
<pre class="language-javascript">mypoint.invoke(name, context, baton);</pre>
 +
 +
 
'''''example'''''
 
'''''example'''''
  
Line 119: Line 131:
 
//baton's data property contains relevant information about current entity (for example a mail)
 
//baton's data property contains relevant information about current entity (for example a mail)
 
mypoint.invoke('draw', node, baton);</pre>
 
mypoint.invoke('draw', node, baton);</pre>
 +
 +
 
== access extensions ==
 
== access extensions ==
  
Line 137: Line 151:
 
mypoint.count()
 
mypoint.count()
 
</pre>
 
</pre>
 +
 +
 
== disabling and enabling ==
 
== disabling and enabling ==
  
Line 148: Line 164:
 
<pre class="language-javascript">//disable
 
<pre class="language-javascript">//disable
 
ext.point('io.ox/mail/detail/header').disable('receiveddate');</pre>
 
ext.point('io.ox/mail/detail/header').disable('receiveddate');</pre>
 +
 +
 
== underscore equivalents ==
 
== underscore equivalents ==
  
Line 166: Line 184:
 
     e.index = Math.random() * 1000 &gt;&gt; 0;
 
     e.index = Math.random() * 1000 &gt;&gt; 0;
 
}).sort();</pre>
 
}).sort();</pre>
 +
 +
 
== Event Hub ==
 
== Event Hub ==
  
Line 183: Line 203:
 
<pre class="language-javascript">// explicit destroy to clean up.
 
<pre class="language-javascript">// explicit destroy to clean up.
 
mypoint.destroy()</pre>
 
mypoint.destroy()</pre>
 +
 +
 
= Extension =
 
= Extension =
  
 
* adding/replacing functionality during runtime
 
* adding/replacing functionality during runtime
 
* referenced by a unique id
 
* referenced by a unique id
 +
  
 
== Attributes ==
 
== Attributes ==
Line 193: Line 216:
 
* index (optional): numeric value used for specify order of execution (valid values also 'first', 'last')
 
* index (optional): numeric value used for specify order of execution (valid values also 'first', 'last')
 
* functions: as required by the extension point contract
 
* functions: as required by the extension point contract
 +
  
 
''*'' example ''*''
 
''*'' example ''*''
Line 204: Line 228:
 
     }
 
     }
 
};</pre>
 
};</pre>
 +
 +
 
== extension subclasses!!!!!!!!!!!!!!!!!!!!!!!!!! ==
 
== extension subclasses!!!!!!!!!!!!!!!!!!!!!!!!!! ==
  
 
* default: object
 
* default: object
 
* special: InlineLinks
 
* special: InlineLinks
 +
  
 
= Baton =
 
= Baton =
  
 
Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.
 
Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.
 +
  
 
== attributes ==
 
== attributes ==
Line 220: Line 248:
 
** disabled: stores disabled extensions (managed via baton.disable(pointId, extensionId))<br />
 
** disabled: stores disabled extensions (managed via baton.disable(pointId, extensionId))<br />
 
* $: used to reference a jquery node object
 
* $: used to reference a jquery node object
 +
  
 
== disable extensions ==
 
== disable extensions ==
Line 228: Line 257:
 
//is disabled
 
//is disabled
 
var isDisabled = baton.isDisabled(pointid, extensionid);</pre>
 
var isDisabled = baton.isDisabled(pointid, extensionid);</pre>
 +
 +
 
''*'' example ''*''
 
''*'' example ''*''
  
Line 241: Line 272:
 
//invoke extension with baton instance
 
//invoke extension with baton instance
 
ext.point(pointid).invoke('draw', node, baton)</pre>
 
ext.point(pointid).invoke('draw', node, baton)</pre>
 +
 +
 
== data exchange ==
 
== data exchange ==
 +
  
 
''*'' example ''*''
 
''*'' example ''*''
Line 283: Line 317:
 
     }
 
     }
 
};
 
};
 +
</pre>
  
  
Line 289: Line 324:
  
 
=== ensure ===
 
=== ensure ===
- ensure that submitted object is instanceof baton
+
* ensure that submitted object is instanceof baton
- return obj if it's an instanceof baton
+
* return obj if it's an instanceof baton
- return new baton instance ***where baton.data*** is extended by obj **or** obj.date (if exists)
+
* return new baton instance ***where baton.data*** is extended by obj **or** obj.date (if exists)
 +
 
 +
<pre class="language-javascript">
 +
baton = ext.Baton.ensure(obj) ```
 
</pre>
 
</pre>
baton = ext.Baton.ensure(obj) ```
+
 
  
 
'''example'''
 
'''example'''
Line 306: Line 344:
 
* return if it's an instanceof baton
 
* return if it's an instanceof baton
 
* return new baton instance '''''where baton''''' is extended by obj '''or''' obj.date (if exists)
 
* return new baton instance '''''where baton''''' is extended by obj '''or''' obj.date (if exists)
 +
  
 
'''example'''
 
'''example'''

Revision as of 11:41, 11 April 2013

Extending the UI

Introduction

Abstractly speaking extension points are an architecture for letting plugins contribute functionality to other parts of the program. They form the core of the OX App Suite plugin architecture. A less detailed hands-on introduction can be found HERE.

Some basics about the extention point concept and advantages compared to inheritance.

inheritance vs. extension points

OX App Suite uses the extension point concept to create extension points that allow a simple and flexible way to extending functionality. When system reaches a part that can be extended it asks a central registry if extensions are registered. In that case these extensions will be executes independent of the providing component (some plugin or OX App Suite itself).

The illustrated example compares inheritance and extension points. The main benefit of using extension points ist that the programm is still the active component and is in controll. This leads to the following advantages:

  • Reduced coupling
  • Increased cohesion
  • Modularity- Re-usability
  • Dynamic

-image-

Some characteristics

  • good fences: extension points unregister corrupt extenders
  • lazy loading: extenders are loaded if they are used
  • fair play: all extenders have equal rights
  • diversity: extension points support different extension


Components

The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:

  • extension point system: accessing the outer parts
  • registry: manages extension points, extensions and their state
  • extension point: part of the systems that can be extended, referenced by a unique id
  • extension: adding/replacing functionality during runtime, referenced by a unique id
  • baton: object used to store context, passed back through callbacks

Extension Point System

//load extension points module
require(['io.ox/core/extensions'], function (ext) {
    //insert code here
});

Registry

  • manages extension points, extensions and their state


list points

// returns array of point ids
var keys = ext.keys();


get/create point

//also registers point if not created yet
var mypoint = ext.point('io.ox/calendar/detail');

Extension Point

  • part of the systems that can be extended, referenced by a unique id
  • defienes as some kind of contract about what it expects its extensions to provide


attributes

  • id
  • description (optional)


example

//get a point and get it's description
var point = ext.point('io.ox/mail/links/toolbar'),
    descr = point.description = '';


add extension

  • important: existing extensions with same id will not be overwritten - use replace insted
  • example: add extension with id 'date'


example

// chainable (returns mypoint)
point.extend({
    id: 'example1', // Every extension is supposed to have an id
    index: 100,     // Extensions are ordered based on their indexes
    draw: function () {
        //draw something
    }
});


replace extension

  • important: only extension properties will be replaced (jquery extend)
  • hint: replace can also be executed before extension is intially created with extend function


example

// chainable (returns mypoint)
mypoint.replace({
    id: 'example1',
    index: 100,
    draw: function (baton) {
        //draw something completely different
    }
});


use extensions

  • invoking point extensions by defining functionname, context and baton
  • node used as context (function is called via apply(node, args))
  • baton forwarded within programmatic flow and used for storing and exchanging data between extensions
mypoint.invoke(name, context, baton);


example

//call 'draw' of all registered extensions (order defined by index attribute)
//node used as context ('draw' function is called via apply(node, args))
//baton's data property contains relevant information about current entity (for example a mail)
mypoint.invoke('draw', node, baton);


access extensions

//returns array containing all extension ids
mypoint.keys();
//returns array containing all extensions
mypoint.all();
//executes callback for a specific extension
//chainable (returns point)
mypoint.get(id, callback);
//disabled extension will return true also;
var exists = mypoint.has(id);

enabled only

//returns array containing all enabled extensions
mypoint.list()
//returns number containing enabled extensions
mypoint.count()


disabling and enabling

var enabled = mypoint.isEnabled();
//chainable (returns mypoint)
mypoint.enable(id);
//chainable (returns mypoint)
mypoint.disable(id);

example

//disable
ext.point('io.ox/mail/detail/header').disable('receiveddate');


underscore equivalents

  • only considers enabled extensions
  • functions returns underscore-chain object of enabled extensions
  • take a look at http://underscorejs.org/ for more details
mypoint.chain()
mypoint.each(callback)
mypoint.map(callback)
mypoint.select(callback) //filter alias
mypoint.inject(callback, memo) //reduce alias
mypoint.pluck(propertyName)

example

// Shuffle extension order
ext.point('io.ox/calendar/detail').each(function (e) {
    e.index = Math.random() * 1000 >> 0;
}).sort();


Event Hub

  • Event Hub based on jQuery's on, off, one, and trigger
  • differences documentated for each function
// attach listener
mypoint.on(type, data, function)
// detach listener
mypoint.off(type, function)
// attach listener for a single execution
mypoint.one(type, data, function)
// trigger event
// difference: allows multiple types separated by spaces.
// difference: actually calls triggerHandler since we are not in the DOM.
mypoint.trigger(types)
// explicit destroy to clean up.
mypoint.destroy()


Extension

  • adding/replacing functionality during runtime
  • referenced by a unique id


Attributes

  • id
  • index (optional): numeric value used for specify order of execution (valid values also 'first', 'last')
  • functions: as required by the extension point contract


* example *

//defining a extension for some extension point that requires a draw function
{
    id: 'example1',
    index: 100,
    draw: function () {
        //draw something
    }
};


extension subclasses!!!!!!!!!!!!!!!!!!!!!!!!!!

  • default: object
  • special: InlineLinks


Baton

Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.


attributes

  • data: usually contains current entity object, also used for data exchange
  • options:
  • flow:
    • disabled: stores disabled extensions (managed via baton.disable(pointId, extensionId))
  • $: used to reference a jquery node object


disable extensions

//disable
baton.disable(pointid, extensionid);

//is disabled
var isDisabled = baton.isDisabled(pointid, extensionid);


* example *

var pointid = 'io.ox/mail/detail',
    extensionid = 'example3',
    node = $('div'),
    baton = ext.Baton();

//disable extension
//returns undefined
baton.disable(pointid,extensionid);

//invoke extension with baton instance
ext.point(pointid).invoke('draw', node, baton)


data exchange

* example *

//extension using baton to store data

{
    id: 'example1',
    index: 100,
    draw: function (baton) {
        //get the currenty process mail object
        var mail = baton.data;
        //append subject to current node referenced as this
        this.append(
            $('div').text(mail.subject);
        )
        //extend mail object to store some flag
        mail.drawn = mail.drawn || {};
        mail.drawn.subject = true;
        //disable extension3
        baton.disabel()
    }
};
{
    id: 'example2',
    index: 200,
    draw: function (baton) {
        //get the currenty process mail object
        var mail = baton.data;
        //use value set by 'example1'
        if(mail && mail.drawn && mail.drawn.subject) {
            //do something
        }
    }
};
{
    id: 'example3',
    index: 300,
    draw: function (baton) {
        //wil not be executed if baton from 'disable example' is used
    }
};


utils

ensure

  • ensure that submitted object is instanceof baton
  • return obj if it's an instanceof baton
  • return new baton instance ***where baton.data*** is extended by obj **or** obj.date (if exists)
baton = ext.Baton.ensure(obj) ```


example

//new baton.data extended by object
ext.Baton.ensure({ id: 2 })


wrapp

  • wrap a baton 'around' submitted object
  • return if it's an instanceof baton
  • return new baton instance where baton is extended by obj or obj.date (if exists)


example

//new baton object extended by obj
ext.Baton.wrap({ id: 2 })