-
Notifications
You must be signed in to change notification settings - Fork 0
Application Components
Application Components describe externalized content and modules, ideal for encapsulating reusable code and creating dynamic user interface widgets. Components can be created with no binding, or be bound to HTML nodes or XHTML Components.
Application Components are similar to Modules in that they provide a collection point for customized code. Unlike custom script written for the Module Service, Application Component script is structured and automatically instruments several Hemi services.
Application Components debuted many years and versions ago in Engine for Web Applications version 0.8 Demonstration #7, circa 2003.
In the original architecture, engine configurations were used to load external content through declarative constructors. Over time, those features were separated between Application Spaces and Templates through Application Components. While Application Spaces continue to support external content, the Application Component Template feature is the recommended method for connecting HTML artifacts with custom script and framework features, and mashing XHTML fragments and scripts into widgets. Templates debuted in Engine for Web Applications version 1.1 Demonstration #16.
Application Components features include:
- Quick-Fix. May be automatically loaded via XHTML Components.
- Transaction Registration. Components may share the same transaction bus as its name or participant declaration. This allows for loose coupling of related components.
- Assertive Instrumentation. Including a coded event handler or transaction name automatically instruments the component or its container for the corresponding event or callback.
- Templates for binding XHTML nodes with script.
- Application Spaces may be created automatically for templates. Refer to Applictation Spaces For how Application Components and Spaces relate.
- Automatic cleanup of XHTML Component references, Spaces, and Data Stacks.
- Automatic instrumentation of HTML Form fields with the Form Service.
- Lightweight Bean storage for associating data objects with components.
- Lightweight EL evaluation for using tokens within Templates and Fragments to match object, form, and bean values.
Application Components may import component definitions from an external XML format.
The following syntax includes the default descriptors and callbacks of an external component definition.
-
id : The id of the component definition. When using a Quick Fix, the id value must be the same as the component name in the format component.id.xml.
-
participant-id : Optional. The name of the transaction to which this component belongs. Defaults to the id.
-
component_init : Optional. Invoked when the component is initialized.
-
component_post_init : Optional. Invoked after the component and any children have been initialized.
-
component_destroy : Optional. Invoked when the component is destroyed.
The Application Component API includes a number of helpful methods:
- getContainer : If bound to an XHTML Node or HTML Node, returns the underlying HTML Node.
- getContainerComponentId : If bound to an XHTML Component, returns the Hemi Framework object identifier which may be used to lookup the object in the registry.
- getReferenceId : If loaded within an Application Space, returns the space identifier.
- loadTemplate : If bound to an XHTML Component, or an HTML container is specified via the getTemplateContainer virtual method, loads the specified XML into the container.
Components are also automatically configured with other Framework-provided APIs, including:
- Base framework object API: getObjectId(), getObjectType(), etc
- [Transaction Participant API]http://www.whitefrost.com/Hemi/api/hemi.transaction.html#addServiceAPI): serveTransaction, etc.
- Event Handler Scope API : scopeHandler
- A Data Stack is configured for each component instance.
When a component is bound to an HTML Node or XHTML Component, declaring an event handler causes a listener to be created for that handler.
<![CDATA[
_handle_click : function(e){
var oContainer = this.getContainer();
var evt = Hemi.event.getEvent(e);
var oSrc = Hemi.event.getEventSource(evt);
}
]]>
Application Components receive the injected Transaction API from the Transaction Service addServiceAPI method. The injected API includes special handling for serveTransaction and doTransaction. Therefore, instead of developers having to write against the Transaction API from components, they only need to know one method: this.serveTransaction.
<![CDATA[
component_init : function(e){
/// Serve packet to like instances
this.serveTransaction("available");
/// Serve packet to unlike instances
this.serveTransaction("alsoavailable",this,1,"otherComponents");
},
_handle_available : function(oService, oPacket){
/// Notification from other components serving the "available" statement for this particular transaction participant list.
/// oPacket.data.src is defined by the injected serveTransaction API
/// BUT - this object doesn't receive it's own callback
}
]]>
In the previous example, if the component id is myComponent, then every instance of myComponent except the invoker would be served the packet for the myComponent transaction. In the second call, the packet for "otherComponents" would be served to all participants. If those components define doTransaction, or handle_alsoavailable, those components would receive notification.
Application Components can be loaded several ways.
Components that have no UI and need not be bound to an HTML node, such as the component.manager.xml component, may be loaded via script. Depending on how the component is to be referenced, it may be desirable to include the Application Space binding.
Using the convenience method provided by the createApplicationComponent method, or the bindComponent from the Application Component API:
/// Preferred/Easiest
var oComponent1 = Hemi.app.createApplicationComponent("manager", 0, Hemi.app.space.service.getPrimarySpace(), "manager");
/// OR
var oComponent2 = Hemi.app.comp.bindComponent(0, "manager", "Templates/component.manager.xml",0,1);
The primary drawback of using the createApplicationComponent convenience method is it directs the component loading to be synchronous so that the returned object is completely loaded. Conversely, the bindComponent method may or may not be synchronous. Another difference in the previous example is that without including an XHTML component that had been bound to an Application Space, this component is outside the context of a Space.
Without using the createApplicationComponent method, the other option is a direct implementation where the Application Space is configured:
var sId = "manager";
var sPath = Hemi.hemi_base + "Components/component." + sId + ".xml";
var oSpace = Hemi.app.space.service.getPrimarySpace();
var oComponent = Hemi.app.comp.newInstance(sId, 0, oSpace.getObjectId());
oComponent.setAsync(false);
oComponent.loadComponent(sPath, sId);
Bound components have the immediate benefit of being able to quickly define event handlers and having easy access to the container (ie: the HTML Node).
Given some component, my-component, saved in Hemi/Components/component.my-component.xml:
<application-components>
<application-component id = "my-component">
<![CDATA[
_handle_click : function(){
Hemi.xml.setInnerXHTML(this.getContainer(), "Clicked!");
}
]]>
</application-component>
</application-components>
The easiest way to bind a component to a node is to use a Quick Fix via the XHTML Component and Application Spaces.
<p component = "my-component"></p>
If the component definition resides in a directory other than Components/, the acid and appcomp_path attributes may be used:
<p acid = "my-component" appcomp_path = "/SomeDir/my-component.xml"></p>
Otherwise, components may be bound via script, such as:
<p id = "oPara">Para</p>
<script type = "text/javascript">
var oComponent = Hemi.app.comp.bindComponent(document.getElementById("oPara"), "my-component", "Templates/component.my-component.xml",0,1);
</script>
Or, if using the createApplicationComponent method, an XHTML Component is automatically created for the DOM node.
<p rid = "oPara">Para</p>
<script type = "text/javascript">
var oComponent = Hemi.app.createApplicationComponent("my-component", document.getElementById("oPara"));
</script>
To load a component via reference id:
<p rid = "oPara">Para</p>
<script type = "text/javascript">
var n = "someReferenceId";
var oSpace = Hemi.app.space.service.getPrimarySpace();
var oSpaceObject = oSpace.getSpaceObjectByName(n)
var oXhtmlObject = oSpaceObject.object.getContainer();
var oComponent = Hemi.app.comp.bindComponent(oXhtmlObject, "my-component", "Templates/component.my-component.xml", 0, 1);
</script>
In the following example, a DIV element is assigned an application component (using the XHTMLComponent's Quick Fix feature). (Note: If the component does not exist in the hemi_base/Components/ directory with the explicit format of component.name.xml, the acid/appcomp_path configuration must be used instead.
<div rid = "some_div" component = "example">Some Div</div>
<application-components>
<application-component id = "example"><![CDATA[
_handle_click : function(){
Hemi.xml.setInnerXHTML(this.getContainer(), "Clicked!");
}
]]></application-component>
</<application-components>
Therefore, the topology includes the Application Component.
<script type = "text/javascript">
Hemi.message.service.subscribe("onspaceconfigload", function (sName, oSpace) {
if (!oSpace.is_primary) return;
// Get the SpaceObject which indexes "some_div" to the Framework object.
var oObjectRef = oSpace.getSpaceObjectByName("some_div");
// Get the Framework object representing the DIV
var oXHTMLComponent = oObjectRef.object;
// Get the Application Component
var oApplicationComponent = oXHTMLComponent.getApplicationComponent();
// Retrieve the DIV node.
var oDiv = oXHTMLComponent.getContainer();
});
</script>
The Hemi JavaScript Framework ships with several components, most notably:
- component.testable.xml : Connects an HTML node with a set of unit tests.
- component.wideselect.xml : A buffered select control for displaying large sets of items.
- component.calendar.xml : A calendar control that loads a custom calendar widget for an HTML INPUT field if that field does not support an HTML 5 calendar control.
- component.tree.xml : The tree component is used to decorate a list control as a tree control, and includes instrumentation with the Data IO service.
- component.tree_context.xml : The tree_context component is a reference example of how transaction participation shares data between otherwise unlinked components.
- component.content_viewer.xml : The content_viewer component is a good reference example of transaction participation and using the Data IO service.
- component.window.xml : The window component is used to render a dynamic window in the Web page.
- component.manager.xml : The manager component is used to manage window state.
Refer to Templates and Fragments.