In order to add custom attributes to an HTML element that is generated by GrapesJS, one can simply make use of traits. For example, if you wish to produce an HTML element which contains the tag data-noout
, a trait with the name data-noout
can be added to a custom component.
var domComps = editor.DomComponents;
var dType = domComps.getType('default');
var dModel = dType.model;
var dView = dType.view;
domComps.addType('input', {
model: dModel.extend({
defaults: Object.assign({}, dModel.prototype.defaults, {
traits: [
{
type: 'checkbox',
label: 'Data No Output',
name: 'data-noout'
}
]
}),
}, {
isComponent: function (el) {
if (el.tagName == 'INPUT') {
return { type: 'input' };
}
},
}),
view: dView,
});
The component above will produce an input
component.
The component can now be dragged onto the canvas. By default, this will produce the following HTML:
<input class="input"/>
To add the custom attribute data-noout
, click on the input box. In the component's settings, tick the checkbox labelled Data No Output
Exporting the HTML now results in:
<input class="input" data-noout="true"/>
It is important to note that when the checkbox is now unchecked, the attribute is not removed from the HTML tag, but rather updated to false
.
<input class="input" data-noout="false"/>
GrapesJS makes use of an iframe
in which it displays the formcreator. As such, none of the global scripts and packages are available to GrapesJS. To get around this, one must set the allowScripts
property in the editor init, and then include the script in the scripts
array.
var editor = grapesjs.init({
allowScripts: 1,
canvas: {
scripts: ['https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js']
},
components: `
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity_no="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
`
The above snippet will make jQuery available to the form builder, as well as bootstrap. Notice that bootstrap, or any CSS lib for that matter, is included in the components
.
It is useful to be able to update the classes of a component if you are using a custom CSS library such as Bootstrap.
First, create a custom trait. This example is based on a select input:
editor.TraitManager.addType('my-trait', {
onValueChange() {
var parentModel = this.target.collection.parent.sm;
var component = this.target;
const sm = parentModel.get('SelectorManager');
var traitModel = this.model;
var selectedComponent = this.target;
var label = traitModel.get('value');
var compCls = component.get('classes');
compCls.forEach(element => {
if (element.id == this.model._previousAttributes.value) {
compCls.remove(element);
}
});
if (label) {
var classModel = sm.add({label});
compCls.add(classModel);
parentModel.trigger('targetClassAdded');
}
},
getInputEl() {
if(!this.$input) {
var md = this.model;
var opts = md.get('options') || [];
var input = '<select>';
if (opts.length) {
_.each(opts, el => {
var name, value, style;
var attrs = '';
if(typeof el === 'string'){
name = el;
value = el;
}else{
name = el.name ? el.name : el.value;
value = el.value.replace(/"/g,'"');
style = el.style ? el.style.replace(/"/g,'"') : '';
attrs += style ? 'style="' + style + '"' : '';
}
input += '<option value="' + value + '" ' + attrs + '>' + name + '</option>';
});
}
input += '</select>';
this.input = input;
this.$input = $(this.input);
var target = this.target;
var name = md.get('name');
var val = md.get('value');
if (md.get('changeProp')) {
val = val || target.get(name);
} else {
var attrs = target.get('attributes');
val = attrs[name];
}
if(val)
this.$input.val(val);
}
return this.$input.get(0);
},
});
The main focus here is in the onValueChange()
method. The getInputEl()
method is just a copy-paste from the default Input trait of GrapesJS, found in TraitSelectView.js.
The onValueChange()
removes the previous class added by the trait. It then adds the new class as the value specified for the label.
Once again, creating a custom Input field:
domComps.addType('input', {
model: dModel.extend({
defaults: Object.assign({}, dModel.prototype.defaults, {
traits: [
{
type: 'my-trait',
label: 'Width',
name: 'class',
options: [
{value: '', name: 'None'},
{value: 'col-lg-1', name: 'Small'},
{value: 'col-lg-3', name: 'Medium'},
{value: 'col-lg-6', name: 'Large'},
{value: 'col-lg-12', name: 'Massive'}
]
}
]
}),
}, {
isComponent: function (el) {
if (el.tagName == 'INPUT') {
return { type: 'input' };
}
},
}),
view: dView,
});
This results in the following dropdown list available to the user:
and it generates the following HTML:
<input class="input col-lg-3"/>
It would be useful to have a set of default traits on custom components. It is actually quite simple to achieve this. Firstly, create a new default
type, which contains the traits that you want on all of its children components:
var domComps = editor.DomComponents;
var dType = domComps.getType('default');
var dModel = dType.model;
var dView = dType.view;
domComps.addType('default', {
model: dModel.extend({
defaults: Object.assign({}, dModel.prototype.defaults, {
traits: [
// strings are automatically converted to text types
'id',
'title',
'name',
'placeholder',
{
type: 'select',
label: 'Type',
name: 'type',
options: [
{ value: 'text', name: 'Text' },
{ value: 'email', name: 'Email' },
{ value: 'password', name: 'Password' },
{ value: 'number', name: 'Number' },
]
}, {
type: 'checkbox',
label: 'Required',
name: 'required',
},
{
type: 'checkbox',
label: 'Data No Output',
name: 'data-nooutput'
},
{
type: 'my-trait',
label: 'Width',
name: 'class',
options: [
{ value: '', name: 'None' },
{ value: 'col-lg-1', name: 'Small' },
{ value: 'col-lg-3', name: 'Medium' },
{ value: 'col-lg-6', name: 'Large' },
{ value: 'col-lg-12', name: 'Massive' }
]
}
]
}),
}),
view: dView,
});
Then, overriding the input
component as follows:
dType = domComps.getType('default');
dModel = dType.model;
dView = dType.view;
domComps.addType('input', {
model: dModel.extend({
defaults: Object.assign({}, dModel.prototype.defaults, {
traits: dModel.prototype.defaults.traits.concat([
'test'
])
})
},
{
isComponent: function (el) {
if (el.tagName == 'INPUT') {
return { type: 'input' };
}
},
}),
view: dView
});
This results in the input component with all of the following traits:
If you do not wish to have all the traits, simply do not concat with the dModel.prototype.defaults.traits
array.