The rendering process is fully customizable. By default, Bootstrap-based HTML is rendered, but this can be changed either by providing HTML templates, or by overriding any of the rendering functions using the options.
Render hints can be specified per-field and influence how a field is rendered. All of the render hints supported by default can be defined on the :ref:`backend <renderhints>`, but it is also possible to define them on the frontend.
This can be done through the the field_options
option passed when creating a form:
new Supler.Form(container, {
field_options: {
'secretField': {
'render_hint': 'password'
},
'friends[].bio': {
'render_hint': {
'name': 'textarea',
'rows': 10,
'cols': 20
}
}
}
});
A render hint can be just a name (string), or an object with a name
property and additional parameters (like
the textarea example).
The generated HTML can be customized by providing templates, which will be used during the rendering process. By default Supler looks for templates nested inside the element that will contain the form. For example:
<div id="form-container">
<div supler:fieldTemplate supler:fieldPath="lastName">
<div class="formGroup">
<i>{{suplerLabel}} <span style="font-size: xx-small">(extra information)</span></i>
{{suplerInput}}
{{suplerValidation}}
</div>
</div>
</div>
If the templates are defined somewhere else, you can provide additional ids of the elements from which templates
should be read by defining the field_templates
option:
new Supler.Form(container, {
field_templates: [ 'idOfElementWithTemplates1', 'idOfElementWithTemplates2' ]
});
The templates are stacked top-to-bottom, that is the templates that are defined higher will take precedence, if multiple templates match a given field.
The templates are applied to all matching fields. The matchers should be specified as attributes of the template. Currently the following matchers are allowed:
supler:fieldPath="..."
where field path can be e.g.cars.model.name
supler:fieldType="..."
where type can be e.g.string
,integer
,double
,static
etc.supler:fieldRenderHint="..."
where the render hint can be e.g.textarea
,password
,radio
etc.
That way templates for a specific field or field type can be specified.
- re-define the template for rendering fields
<div id="form-container">
<div supler:fieldTemplate>
// html with placeholders:
// {{suplerLabel}}
// {{suplerDescription}}
// {{suplerInput}}
// {{suplerValidation}}
</div>
</div>
- re-define how labels are rendered
<div id="form-container">
<div supler:fieldLabelTemplate>
// html with placeholders:
// {{suplerLabelForId}}
// {{suplerLabelText}}
</div>
</div>
- re-define how descriptions are rendered
<div id="form-container">
<div supler:fieldDescriptionTemplate>
// html with placeholders:
// {{suplerDescriptionText}}
</div>
</div>
- re-define how validations are rendered
<div id="form-container">
<div supler:fieldValidationTemplate>
// html with placeholders:
// {{suplerValidationId}}
</div>
</div>
- re-define how a field's input without possible values is rendered
<div id="form-container">
<div supler:fieldInputTemplate>
// html with placeholders:
// {{suplerFieldInputAttrs}}
// {{suplerFieldInputValue}}
</div>
</div>
This should always be combined with a filter to make sense. The attributes will contain normal attributes such
as id
, name
, as well as supler-specific meta-data. If {{suplerFieldInputValue}}
is used,
the attributes won't include the field value (useful e.g. for textarea fields). Otherwise the attributes will
contain the value mapping.
- re-define how a field's input with possible values is rendered
<div id="form-container">
<div supler:fieldInputTemplate supler:singleInput="true|false" supler:selectedAttrName="selected" supler:selectedAttrValue="selected">
// html with placeholders:
// {{suplerFieldInputContainerAttrs}}
// must contain an element with the "supler:possibleValueTemplate" attribute;
// that element will be repeated for each possible value. Placeholders:
// {{suplerFieldInputAttrs}}, {{suplerFieldInputValue}}, {{suplerFieldInputLabel}}
</div>
</div>
To properly render a field with possible values, Supler needs to know if the element is rendered as a single input (e.g. drop-down) or multiple inputs (e.g. radio/checkboxes).
Also, if an element is already selected, it must have an additional attribute, which will be added to the possible
value template. The attribute name & value are specified using supler:selectedAttrName
and
supler:selectedAttrValue
.
- re-define how a field overall is given (without separating into label/input/validation)
<div id="form-container">
<div supler:fieldFlatTemplate>
// html with placeholders:
// {{suplerFieldInputAttrs}}
// {{suplerFieldLabelForId}}
// {{suplerFieldLabelText}}
// {{suplerFieldDescriptionText}}
// {{suplerFieldValidationId}}
</div>
</div>
- re-define how a subform is rendered
<div id="form-container">
<div supler:subformDecorationTemplate>
// html with placeholders:
// {{suplerSubformLabel}}
// {{suplerSubform}}
// {{suplerSubformContainerAttrs}}
</div>
</div>
- re-define how a subform element is rendered (as-list rendering)
<div id="form-container">
<div supler:subformListElementTemplate>
// html with placeholders:
// {{suplerSubformElement}}
// {{suplerSubformElementContainerAttrs}}
</div>
</div>
- re-define how a subform element is rendered (as-table rendering)
<div id="form-container">
<div supler:subformTableTemplate>
// html with placeholders:
// {{suplerSubformTableHeaders}}
// {{suplerSubformTableCells}}
</div>
</div>
The table headers are a series of <tr><th>
tags.
The table cells are a series of <tr><td></td><td></td>..></tr>...
tags.
- re-define the order of fields
<div id="form-container" supler:fieldOrder="x, y, z">
</div>
Rendering can also be customized by providing customizations using javascript instead of HTML templates. You can
override any of the methods available on RenderOptions
(see below for a complete list) using field options:
new Supler.Form(container, {
field_options: {
'bio': {
'render_options': {
renderLabel: function(forId, label) { return '<div>some html</div>'; }
}
}
}
});
It is also possible to match using render hints, instead of field names/paths. You need to prefix the field option name
with render_hint:
. For example, to provide custom javascript rendering options for all fields with render hint
date
:
new Supler.Form(container, {
field_options: {
'render_hint:date': {
'render_options': {
renderLabel: function(forId, label) { return '<div>this is a date</div>'; }
}
}
}
});
To override how particular types of form elements are rendered globally, simply provide a method in the render_options
option passed to Supler.Form
; you can even provide a whole alternative implementation of the RenderOptions
interface:
var formContainer = document.getElementById('form-container');
var form = new Supler.Form(formContainer, {
render_options: {
renderStringField: function(label, id, validationId, name, value, options, compact) {
return someHtml;
}
}
});
form.render(formJson); // formJson is received from the server
Methods available for overriding:
// basic types
renderTextField: (fieldData: FieldData, options: any, compact: boolean): string
renderHiddenField: (fieldData: FieldData, options: any, compact: boolean): string
renderTextareaField: (fieldData: FieldData, options: any, compact: boolean): string
renderMultiChoiceCheckboxField: (fieldData: FieldData, possibleValues: SelectValue[], options: any, compact: boolean): string
renderMultiChoiceSelectField: (fieldData: FieldData, possibleValues: SelectValue[], options: any, compact: boolean): string
renderSingleChoiceRadioField: (fieldData: FieldData, possibleValues: SelectValue[], options: any, compact: boolean): string
renderSingleChoiceSelectField: (fieldData: FieldData, possibleValues: SelectValue[], options: any, compact: boolean): string
renderActionField: (fieldData: FieldData, options: any, compact: boolean): string
// templates
// [label] [input] [validation]
renderField: (input: string, fieldData: FieldData, compact: boolean) => string
renderLabel: (forId: string, label: string) => string
renderDescription: (description:string) => string
renderValidation: (validationId: string) => string
renderRow: (fields: string) => string
renderForm: (rows: string) => string
renderStaticField: (label: string, id: string, validationId: string, value: any, compact: boolean) => string
renderStaticText: (text: string) => string
renderSubformDecoration: (subform: string, label: string, id: string, name: string) => string
renderSubformListElement: (subformElement: string, options: any) => string;
renderSubformTable: (tableHeaders: string[], cells: string[][], elementOptions: any) => string;
// html form elements
renderHtmlInput: (inputType: string, value: any, options: any) => string
renderHtmlSelect: (value: number, possibleValues: SelectValue[], options: any) => string
renderHtmlRadios: (value: any, possibleValues: SelectValue[], options: any) => string
renderHtmlCheckboxes: (value: any, possibleValues: SelectValue[], options: any) => string
renderHtmlTextarea: (value: string, options: any) => string
// misc
additionalFieldOptions: () => any
inputTypeFor: (fieldData:FieldData) => string