Customizable and extensible forms generator for AngularJS. License under MIT License.
Form directive render form by configuration:
<formus-form name="form.name"
model="form.data"
fieldsets="form.fieldsets"
config="form.config">
</formus-form>
$ bower install -S angular-formus
Add a <script>
to your index.html
:
<script src="/bower_components/angular-formus/dist/formus.min.js"></script>
and add Formus module as dependency to Your application:
angular.module('app', ['formus']);
Example:
form = {
name: "systemParametersForm",
fieldsets: {
fields: [{
"name": "movePayments",
"label": "Move Payments Straight To Cash",
"input": "checkbox"
}, {
"name": "cancelPendingAfter",
"label": "Automatically Cancel Pending Mode Bookings after",
"input": "textbox",
"suffix": "days"
}]
},
config: {
class: 'some-css-class',
submit: {
title: 'Save',
handler: function() {
console.log('I\'m submitted');
}
}
}
}
Form must has one general field, which contain others fields. Example of field declaration:
{
name:'nameOfFieldInModel', // string
label: 'label', // string
fields: [] //array of objects
input:'typeOfInput' // string
}
Fields can contain nested fields.
All field attributes are optional except input
.
If attribute name
isn't set, that field must contain nested fields (attribute fields
).
You can add custom attributes, they are available in view at object config.
Linker can be overriden. E.g:
{
name:'name',
linker:function($scope, $element, $attr, $http /**Some other services**){}
}
- fieldset (container for other fields)
- textbox
- texarea
- select
- checkbox
- radio
- checklist
Formus supports customization of all module elements.
To change input template just register it in FormusTemplatesProvider
:
app.config(['FormusTemplatesProvider', function (FormusTemplatesProvider) {
FormusTemplatesProvider.setTemplateUrl('color', 'views/formus/inputs/color.html');
FormusTemplatesProvider.setTemplateUrl('group', 'views/formus/inputs/group.html');
FormusTemplatesProvider.setTemplateUrl('file', 'views/formus/inputs/file.html');
FormusTemplatesProvider.setTemplateUrl('gallery', 'views/formus/inputs/gallery.html');
}]);
Formus supports data validation. Validators can be attached to every field.
{
name: 'title',
input: 'textbox',
validators: {
required: true /** Key - validator name, Value - options **/
}
}
Available validators is contained in FormusValidator
service.
Example of adding custom validator:
app.config(['FormusValidatorProvider', function (FormusValidatorProvider) {
FormusValidatorProvider.set('numeric', function (value, config, arg) {
if (value) {
if (!tools.validateNumerical(value)) {
return config.label + ' must be numerical';
}
if (typeof(arg) === 'object') {
if ((angular.isDefined(arg.min)) && (value < arg.min)) {
return config.label + ' must be > ' + arg.min;
}
}
}
return null;
});
}]);
Validator function is called by $injector and has three special parameters:
value
- field valueconfig
- field configargs
- validator options
This validator can take options as object with property min
, e.g:
validators: {numeric: {min:0}}
Available validators:
- required
- url
Formus emit an event after validation:
$rootScope.$on('Formus.validatedForm', function(event, name, isValid) {
/*Some stuff*/
});
To validate form without submit:
$rootScope.$broadcast('Formus.validateForm', 'myFormName');
For showing errors that return from server you can use attribute errors
in formus-form
directive.
Errors must be object with properties named as form fields and errors it's array of strings, e.g:
{
name:['Name must be longer']
}
FormusHelper
has special method for extracting errors from response.
Example of setting errors:
form.config.submit.handler = function() {
return save().then(function() {
$state.go('^.list');
}).catch(function(response) {
form.errors = FormusHelper.extractBackendErrors(response);
});
};
All methods of FormusHelper
can be overridden, e.g:
app.config(['FormusHelperProvider', function(FormusHelperProvider) {
FormusHelperProvider.setMethod('nameOfMethod', function() {
/* Implementation */
});
}]);
FormusConfig
service allows to set default configurations for every type of input.
Setter take two params:
- name of filed
- callback which must return config object
app.config(['FormusConfigProvider', function (FormusConfigProvider) {
FormusConfigProvider.set('datetime', function () {
return {
minView: 0,
startView: 0,
dataType: 'number',
dateFormat: 'shortDate'
}
});
}]);
You can create form with nested fields:
form = {
fieldsets: {
fields: [
{"name": "val0", "label": "Text Value", "input": "checkbox"},
{"name": "someExtend", "fields": [
{"name": "val1", "label": "Nested Value #1", "input": "textbox"},
{"name": "val2", "label": "Nested Value #2", "input": "select","items": [
{"value":1, "title":"opt 1"},
{"value":2, "title":"opt 2"}
]
},
]
}
]
}
};
As result get model:
{
val0: true,
someExtend:{
val1: 'some text',
val2: 2
}
}
You can use dot notation:
form = {
fieldsets: {
fields: [
{"name": "base.first", "label": "Some Value", "input": "textbox"},
{"name": "base.second", "label": "Some Value#2", "input": "textbox"},
{"name": "other.value", "label": "Some Value#3", "input": "checkbox"},
]
}
};
Result model:
{
base:{
first: 'text val',
first: 'text val2',
},
other:{
value: false
}
}
For comfortable work with large number of forms you can use FormusContainer
.
This service provide global storage for form configs.
Configuration:
var formsConfiguration = {form1:{/**...**/}, form2:{/**...**/}};
app.constant('FORMS_CONFIG', formsConfiguration);
app.config(['FormusContainerProvider', 'FORMS_CONFIG', function (FormusContainerProvider, FORMS_CONFIG) {
FormusContainerProvider.setContainer(FORMS_CONFIG);
}]);
After configuration you can use FormusContainer
in controller:
var myCtrl = function($scope, FormusContainer){
$scope.form = FormusContainer.get('form1');
}
Default configuration of form can be set using FormusConfig
.
app.config(['FormusConfigProvider', function (FormusConfigProvider) {
FormusConfigProvider.set('form', function () {
return {
name: 'default-name',
fieldset: { fields: [] },
data: {},
config: {
buttons: [],
submit: { title: 'Save', handler: function() {} }
}
}
});
}]);
To extend a form configuration you can specify attribute parent
. Formus will search container for form with that name and use its configuration.
{
//form containter
parent: {
config:{
submit:{title:'Find'}
}
},
child: {
parent:'parent'
}
}
You can set custom linkers for special input types:
app.config(['FormusLinkerProvider', function (FormusLinkerProvider) {
FormusLinkerProvider.setLinker('gallery', function ($scope, $element, fileUpload) {
$scope.uploadImage = function () {
if (fileUpload.isValid($element.find('.imgFileInput'))) {
alert('Select file');
} else {
fileUpload.upload($element.find('.imgFileInput')).then(function (response) {
$scope.model.push(response.data.url);
});
}
};
$scope.removeImage = function (index) {
$scope.model.splice(index, 1);
};
$scope.afterLoadTemplate = function () {
if (!Array.isArray($scope.model)) {
$scope.model = [];
}
}
});
}]);
Every field in form is wrapped in special directive, it name formusWrapper
.
Is possible to override it template by using FormusTemplates
service:
app.config(['FormusTemplatesProvider', function (FormusTemplatesProvider) {
FormusTemplatesProvider.setTemplateUrl('wrapper', 'views/formus/inputs/my-custm-wrapper.html');
}]);