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.jsthat 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.jslayout.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.jsandform-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 thetypesandsubtypesit represents. See 'creating a new control' below for more details.getClass- retrieve the registeredcontrolclass for a specifiedtypegetRegistered- used to retrieve an array of registeredtypes(orsubtypesof 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 eachcontrolclass. This method returns an object containing information about that types it represents, accessible through thecontrolClass.definitionproperty. See 'Control definitions' below for more detail.mi18n- child method - a wrapper to themi18nthat 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 definingjsorcssproperties (string or array) to have external files preloaded once into the interface.build- the primary method for any control. Process the configuration inthis.configand return a DOM element representing this control widget.onRender- allows you to define code that will be executed when the element returned bybuildhas 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 thetypeorsubtype) to a defined mi18n lookup. By defaultform-builderwill look use the type or subtype to look up translations, but this property allows you to map those to different lookup keys. Seecontrol/text.jsfor 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.jswill fall back to looking up themi18nobject.
// 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 thetypebeing 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.jsfor 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 oftypeUserAttrsand 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 labelDOMElementhelp- put together a helpDOMElement