The "plain"
Integration Technology
While LaxarJS allows you to adapt external MVC frameworks for writing widgets, sometimes that is not actually needed.
Especially when creating activities, often there is no reason to use something like Angular or Vue.js.
The "plain
" technology is for widgets that simply rely on what the browser has to offer.
Preliminary readings:
When to Use the "plain"
Technology?
The "plain"
integration technology is supported by LaxarJS out-of-the-box without having to add any adapter to a project.
This minimizes the footprint of widgets -- great if they are to be used from projects that may already be using one or more integration technologies.
Also, a reduced number of dependencies will likely improve compatibility.
For example, if a widget relies on "angular2"
, it might not work within projects that use upcoming major versions of Angular.
Also, "plain"
is a great choice for activities:
These do not benefit as much from MVC frameworks, except maybe from their utility services (such as $http
provided by AngularJS).
Finally, using "plain"
may be the best option if you are interfacing with the DOM directly, for example when
- you would like to mainly use
canvas
,video
or even WebGL for your UI, - you would like to programmatically manipulate the DOM, using custom animations or maybe
D3
charts.
In these use cases, even well-designed view frameworks sometimes just get in the way.
When to Avoid the "plain"
Technology?
When development speed of your project is of more concern than raw download or execution speed, you may want to refrain from using the "plain"
adapter.
This is especially true for widgets containing complex business UI with a lot of form controls, tables, show/hide animations and so on -- these view frameworks exist for a reason.
Also, if you are sure that all of your projects will be created with, say, Vue.JS in the near future, it may be much simpler to just stick with it.
Finally, if you are adding some functionality to an existing project, you should probably simply use whatever most of the project is using, even more so if that functionality is not intended for reuse.
Creating a "plain"
Activity
To keep things simple, let us create an activity that simply logs a message as soon as it receives the beginLifecycleRequest
event.
For an activity, you just need two files:
- the
widget.json
descriptor, - the controller implementation module.
In the widget descriptor, make sure to set integration.technology
to "plain"
and integration.type
to "widget"
.
Also, pick a name
; we will use "my-activity"
for this example of a widget.json
:
{ "name": "my-activity", "integration": { "type": "activity", "technology": "plain" } }
You can also add a features
configuration schema, which we will skip here, as it is not specific to the integration technology that is used.
Next, create your implementation module, my-activity.js
:
export const injections = [ 'axEventBus', 'axLog' ]; export function create( eventBus, log ) { eventBus.subscribe( 'beginLifecycleRequest', () => { log.warn( 'OK, now what?!' ); } ); }
That is it: activity modules just need to export their named injections
along with a create
function that has a matching signature.
Creating a "plain"
Widget
Let us now explain how to create a widget by using the following example: We will display a simple text along with a counter. For some reason, we will increment the counter anytime the user clicks on the page.
Again, we start with a widget.json
descriptor.
This time, let us throw in a feature configuration schema as well:
{ "name": "my-widget", "integration": { "type": "widget", "technology": "plain" }, "features": { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "label": { "type": "object", "properties": { "text": { "type": "string", "default": "Click Me!" } } } } } }
The HTML Template
Put your template under default.theme/my-widget.html
, just like you would do for other technologies:
<h1>My Widget says <span class="my-widget-label"></span></h1>
Note that since this is a plain widget, no preprocessing or interpolation of values takes place.
The CSS stylesheet
Unless you use a custom theme for your new widget, LaxarJS will be looking for styles in default.theme/css/my-widget.css
:
.my-widget h1 { font-style: italic; }
Styles work exactly as they do for other integration technologies, including the ability to namespace by the widget name, as shown here. If your widget does not need custom styles, simply omit the file.
The Widget Controller
What is missing now is connecting feature configuration and widget template from within the implementation module my-widget.js
:
export const injections = [ 'axWithDom', 'axFeatures', 'axEventBus' ]; export function create( withDom, features, eventBus ) { let numClicks = 0; document.body.addEventListener( 'click', handleClick ); eventBus.subscribe( 'endLifecycleRequest', () => { document.body.removeEventListener( 'click', handleClick ); } ); return { onDomAvailable: render }; function handleClick() { ++numClicks; render(); } function render() { withDom( dom => { dom.querySelector( '.label' ).textContent = numClicks === 0 ? features.label.text : `Clicked ${numClicks} times`; } ); } }
The controller looks similar to that of an activity, except for the additional return statement and for the injection axWithDom
that the "plain"
technology adapter makes available for widgets only.
-
the
onDomAvailable
method returned by the widget'screate
function will be called as soon as the widget DOM was inserted into the page for the first time. If your widget is part of an initially hidden widget area such as a popup window,onDomAvailable
may be called much later thancreate
, or never. -
the
axWithDom
hook guards its callback against the DOM being unavailable while a widget's container area is hidden. The callback will only be executed if the widget DOM is actually attached to the page.
If render
was run only during onDomAvailable
, the axWithDom
injection would not be needed.
However, render
may be run anytime the user clicks anywhere within the document body.
Guarding render
in this way ensures that we do not run into null
-reference problems in case our containing widget area is hidden.
Creating a "plain"
Control
While controls for other technologies directly integrate with their respective component model, using a plain control offers little over directly using import
(or require
) to add a UI library such as the Drop Tooltip or the Velocity Animations Engine to your widget.
Still, wrapping libraries in "plain"
controls has two benefits:
- LaxarJS will automatically load their CSS stylesheet taking into account the application theme,
- controls for
"plain"
can be used by widgets written in any integration technologies.
This also makes it worthwhile to create controls just for styling, effectively giving you CSS components that are loaded as needed by the widgets within your application, and can be overwritten per theme.
So, whenever you create or integrate a non-trivial piece of UI, consider wrapping it as a plain control, in particular if there are associated CSS styles.
The Control Descriptor
Like widgets, controls need a control.json
descriptor containing their name
and integration.technology
.
The Control Implementation Module
LaxarJS does not impose restrictions on control implementation modules, except that they must be named as determined by the descriptor.
Whatever you export
from your control module will be accessible from widgets using your control.
Usually, you should export some kind of constructor or a create
function that widget authors can then invoke with a DOM node and/or customization options.
The Control Stylesheet
LaxarJS will automatically bundle CSS styles for controls if they are located in the right place.
Using the default.theme
, styles for a control my-control
would be read from the control folder under default.theme/css/my-control.css
.
Note that controls do not receive themed HTML.
However, they can use import
or require
to load HTML through the raw-loader
provided by webpack.
Accessing a Control from a Widget
Widgets of any implementation technology can use the axControls
injections to obtain their control implementation modules.
Using axControls.provide( name )
, they can obtain the modules by name
like in the controls
section of their widget descriptor.
More Information