Libraries and classes
Key files / folders:
control.js
- defines the parent class for all controls and a bunch of class level methods for defining information about each control (label, icon etc)control/*.js
- the individual controls to form the base set of controls transpiled into the final codebase, each inheriting fromcontrol.js
.control_plugins/*.js
- additional controls which also inherit fromcontrol.js
that remain outside the transpiled code but are able to be individually included as required.form-builder.js
- the library & code for the form builder interfaceform-render.js
- the library & code for rendering formData json/xml created by formbuilderhelper.js
- reusable methods that are used throughoutform-builder.js
layout.js
- the layout engine that produces each row of the form, and determines how the label, help text, and control widget will each fit together.sanitizer.js
- Script injection and DOM clobbering protection library for formbuilderutils.js
- resuable methods thare are used in bothform-builder.js
andform-render.js
Controls
A 'control' in formbuilder parlance is a 'widget' or form input on the form. It allows the user to interact with the form.
Each control is represented by a class which inherits from the control
class defined in control.js
. A control class may be used by multiple types of controls.
For an example in of how to create a new control, check out the Readme.md in the control/
directory.
For an example in of how to create a new control plugin, check out the Readme.md in the control/
directory.
The parent class defined in control.js
has two types of methods:
* object level methods which are used to manipulate and create an instance of that control on a form
* static class level methods which are used to define and interact with that type of control.
Key class level methods
The following methods are static methods designed to be called on the class. E.g. let controlClass = control.getClass(type);
register
- one of the core methods defined bycontrol.js
. Any new control needs to register thetypes
andsubtypes
it represents. See 'creating a new control' below for more details.getClass
- retrieve the registeredcontrol
class for a specifiedtype
getRegistered
- used to retrieve an array of registeredtypes
(orsubtypes
of a specifiedtype
)- Child methods - these class level methods only rea lly make sense being called on individual child control classes. Those are grouped below. E.g.
let textLabel = control.getClass('text').label('text');
get definition
- child method - a getter method to define a static property for eachcontrol
class. This method returns an object containing information about that types it represents, accessible through thecontrolClass.definition
property. See 'Control definitions' below for more detail.mi18n
- child method - a wrapper to themi18n
that retrieves the translation for a specified lookup key - but checks i18n overrides or mi18n lookup key mapping in the class definition. (See Control definitions below for more details).label
- retrieves the label for a specified typeactive
- ensure the specified type is not defined as inactiveicon
- retrieve the icon for this type
Key object level methods - most of these (except on
are designed to be defined for a child class)
configure
- this method allows you to manipulate the configuration of the control after the constructor has done the standard configuration. Handy for definingjs
orcss
properties (string or array) to have external files preloaded once into the interface.build
- the primary method for any control. Process the configuration inthis.config
and return a DOM element representing this control widget.onRender
- allows you to define code that will be executed when the element returned bybuild
has been inserted into the interface - see any of the rich text editor controls for an example
Control definitions
To define information about this control, static get definition()
method will allow us to define information about this new class. The object returned by get definition
getter method supports the following properties:
-
mi18n
- this property allows you to map a lookup (generally thetype
orsubtype
) to a defined mi18n lookup. By defaultform-builder
will look use the type or subtype to look up translations, but this property allows you to map those to different lookup keys. Seecontrol/text.js
for an example. -
i18n
- used primarily by control plugins. This allows you to encapsulate the translations within the plugin itself. While best practice is to use the mi18n library, a control plugin will generally need to be self contained. If any translations cannot be found defined here,control.js
will fall back to looking up themi18n
object.
// option 1 - define multiple translations
i18n: {
'fr-FR': {
myType: 'Mon Type',
mySubType: 'Mon sous-type'
},
'default': {
myType: 'My Type',
mySubType: 'My sub type'
}
}
// option 2 - simpler approach to just define 1 translation (for the common situation where a class defines just 1 type or subtype)
i18n: {
'fr-FR': 'Mon Type',
'default': 'My Type'
}
icon
- the icon to use in the list of controls for thiscontrol
. If your control class supports multiple types, this should be defined as an object with thetype
being the key for each icon.
// simple icon definition for a class that only supports 1 type (or uses the same icon for all types)
static get definition() {
return {
icon: '🌟',
i18n: {
default: 'Star Rating'
}
}
}
-
inactive
- array of inactive types that shouldn't appear in formBuilder interface (but still be supported for rendering purposes) - seecontrol/select.js
for an example -
defaultAttrs
- used primarily by control plugins. This allows you to define custom attributes for the control plugin without needing to modify the formBuilder options. Follows the style oftypeUserAttrs
and can be overwritten as such.
static get definition() {
return {
defaultAttrs:{
'Extra Content': {
'label': 'extracontent',
'value' : '',
'type': 'textarea'
}
}
}
}
disabledAttrs
- used primarily by control plugins. This allows you to disable default attrs for a control plugin where attributes are not used by the plugin or serve no purpose.
static get definition() {
return {
disabledAttrs: [
'description',
'placeholder',
],
}
}
The label for a control in the list of form builder controls should be defined as the translation for the type
. E.g. if you want to rename the label for a textarea, you would update the mi18n translation for textarea
, or define an override in the i18n
property.
Layouts
The layout
class controls how each 'row' for the form will be output. Each row of the form has a label, some optional help text, and a form control / widget. The layout class will stick these DOMElements
together and wrap them in other appropriate DOMElements. It centralises the code to determining how these elements fit together and/or interact, and allows us to override this as necessary for a specific use case.
The default layout puts an help text into a '?' tooltip, and embeds this within the label, which is rendered inside a
The layout class defines multiple templates
which are used to render different types of controls. The primary predefined templates are default
(typical control), noLabel
(controls only the control should be returned), or hidden
where the 'naked' control should be output (with no wrapping divs). The build
method of a control
can indicate which template should be used to render that particular control. E.g. control/hidden.js
defines the hidden
template to be used.
It is possible to override a layout template by passing an object of overriding templates
to the constructor. Both formBuilder.js
and formRender.js
support an option layoutTemplates
which achieves this. Each layout template should be a function that receives a variety of parameters (by default templates receive field, label, help and data). The last parameter for any template will be the object of data for this 'row' of the form.
For more control, it is possible to create a class that inherits from the layout
class and pass this class as a layout
option to formBuilder
of formRender
.
Customising main layouts
layoutTemplates: {
default: function(field, label, help, data) {
help = $('<div/>')
.addClass('helpme')
.attr('id', 'row-' + data.id)
.append(help);
return $('<div/>').append(label, field, help);
}
}
Customising label & help layouts
layoutTemplates: {
help: function(helpText) {
return $('<div/>')
.addClass('help')
.append(helpText);
},
label: function(label, data) {
// cheeky styling
return $('<label class="bright" style="margin-top:15px;"/>')
.attr('for', data.id)
.append(label);
}
}
Key methods
constructor
- defines the default templates, merges in override templates, default configurationconfigure
- if you have defined a custom layout class this can be used to manipulate the object configuration post constructingbuild
- one of the primary methods that creates the control, label, help and then joins them using a template.label
- put together a labelDOMElement
help
- put together a helpDOMElement