Formly for Angular is an AngularJS module which has directives to help customize and render JSON based forms. The directive originated from a need to allow our users to create surveys and distribute them easily. Currently we've can render the form data from JSON and assign a model to form so we can receive the submitted data.
<formly-form model="formData" fields="formFields"></formly-form>-
Required to use Formly:
-
Angular
-
Dev dependencies to build Formly
-
npm
-
Install with Bower or npm
$ bower install angular-formly --saveor$ npm install angular-formly --save -
Include the javascript file in your index.html, Formly without any form templates. You can create your own or use some of our prebuilt templates which cover basic form types, then extend with your own as needed.
<script src="bower_components/angular-formly/dist/formly.min.js"></script>
and
angular.module('yourModule', ['formly']);
or
angular.module('yourModule', [require('angular-formly')]);
While it is recommended to create your own templates for ultimate customization and flexibility, there are prebuilt templates you can use:
- Vanilla Template: no fancy styling, just plain html
- Bootstrap Templates: bootstrap compatible forms, form-groups, etc.
- LumX Templates: LumX compatible components
Regardless of which flavor you use (or if you use no flavor at all), you can create your own templates with formlyConfigProvider.
This is the recommended approach if you want to customize your templates at all.
Note: This README.md is for the latest version of formly. There have been some changes in the latest version which is not stable. For documentation on the latest stable version, see the 1.0.0 documentation
Here's an example using the vanilla template properties
You can add a formly-form in your HTML templates as shown below.
<formly-form model="formData" fields="formFields">
<button ng-click="onSubmit()">Hello World</button>
</formly-form>Example data as it would be set in the controller
$scope.formData = {};
$scope.formFields = [
{
//the key to be used in the model values {... "username": "johndoe" ... }
key: 'username',
type: 'text',
label: 'Username',
placeholder: 'johndoe',
required: true,
disabled: false, //default: false
description: 'Descriptive text'
},
{
key: 'password',
type: 'password',
label: 'Password',
required: true,
disabled: false, //default: false
expressionProperties: {
hide: '!model.username' // hide when username is blank
}
}
];
$scope.onSubmit = function() {
console.log('form submitted:', $scope.formData);
};When constructing fields use the options below to customize each field object. You must set at least a type, template, or templateUrl.
typeis the type of field to be rendered. Either type, template, or templateUrl must be set.
null
depends on the template set you're using. See documentation for the specific fieldset you are using.
templatecan be set instead oftypeortemplateUrlto use a custom html template form field. Should be used with one-liners mostly (like a directive). Useful for adding functionality to fields.
Note: This can be used to add HTML instead of a form field.
Examples:
template: '<p>Some text here</p>'template: '<hr />'
undefined
templateUrlcan be set instead oftypeortemplateto use a custom html template form field. Set a path relative to the root of the application. iedirectives/custom-field.html
undefined
By default form models are keyed by location in the form array, you can override this by specifying a
key.
undefined
By default, the
modelpassed to theformly-fielddirective is the same as themodelpassed to theformly-form. However, if the field has amodelspecified, then the specifiedmodelis used for that field (and that field only). Also, a deep watch is added to theformly-fielddirective's scope to run theexpressionPropertieswhen the specifiedmodelchanges.
undefined
expressionPropertiesis an object where the key is a property to be set on the main field config and the value is an expression used to assign that property. One special case isdatawhich is an object that behaves the same asexpressionPropertiesexcept the value is assigned tofield.datarather than justfield. The expression can be a function or string expression and will be evaluated usingformlyEvalfromformlyUtilssee below for more information.
undefined
datais reserved for the developer. You have our guarantee to be able to use this and not worry about future versions of formly overriding your usage and preventing you from upgrading :-)
undefined
modelOptionsis used to make your templates easier to work with. Noramlly, you would have to do this in each of your templates:ng-model="model[options.key || index]". However, if you like, you can take advantage ofng-model-optionsvia themodelOptionsproperty. This will allow you to dong-model="value" ng-model-options="options.modelOptions"not necessarily less verbose, but a little easier to understand. To accomplish this, eachformly-fieldadds avaluefunction on the scope. It is a traditional getter/setter for you to use in your templates. For more information on ng-model-options, see these egghead lessons.
{ getterSetter: true, allowInvalid: true }
watcheris an object which has at least two properties calledexpressionandlistener. Thewatch.expressionis added to theformly-formdirective's scope. If it's a function, it will be wrapped and called with the field as the first argument, followed by the normal arguments for a watcher, followed the watcher'sstopfunction. If it's not defined, it will default to the value of the field. Thelistenerwill also be wrapped and called with the field as the first argument, followed by the normal arguments for a watch listener. You can also specify a type ($watchCollectionor$watchGroup) via thetypeproperty (defaults to$watch) and whether you want it to be a deep watch via thedeepproperty (defaults tofalse).
How the api differs from a normal $watch:
// normal watcher
$scope.$watch(function expression(theScope) {}, function listener(newValue, oldValue, theScope) {});
// field watcher
$scope.$watch(function expression(field, theScope, stop) {}, function listener(field, newValue, oldValue, theScope, stop) {});
undefined
validatorsis an object where the keys are the name of the validity (to be passed to$setValidity) and the values are functions or expressions which returns true if it is valid. Templates can pass this option to theformly-custom-validationdirective which will add a parser (or validator, see note) to thengModelcontroller of the field. The validator can be a function or string expression and will be evaluated usingformlyEvalfromformlyUtilssee below for more information. Note: Formly will utilize the$validatorspipeline (introduced in angular 1.3) if available, otherwise it will fallback to$parsers. If you are using angular 1.3, you can also use the$asyncValidatorspipeline by adding the propertyisAsync = trueto your validator function.
undefined
The resulting form element has the class formly and each field has the class formly-field.
Formly uses angular's built-in validation mechanisms. See the angular docs for more information on this. (Note, if you're using Angular 1.3, formly utilizies the new $validators and $asyncValidators pipelines, otherwise, it falls back to good old $parsers. Either way, your API is the same, though you can't do asynchornous validation with 1.2.x).
The form controller is bound to what you specify as the form attribute on the formly-form directive. Make sure to specify a name on any ng-model in your custom templates to ensure that the formControl is added to the options. If you're using Angular 1.3, the name attribute is interpolateable (you can use {{id}}). If you are stuck on 1.2.x, you can use the formly-dynamic-name directive where the value is an expression which would return the name (so, formly-dynamic-name="id"). Formly will add a formControl property to the field, and you can reference that in your template with options.formControl to get access to properties like $invalid or $error. See the bootstrap templates for an example.
You can also specify custom validation in your JSON. See the field called validators for more information on this. If you wish to leverage this in a custom template, use the formly-custom-validation directive and pass options.validators to it.
You can configure formly to use custom templates for specified types (your own "text" template) by injecting the formlyConfigProvider in your app's config function. The formlyConfigProvider has the following functions:
Allows you to set a template
formlyConfigProvider.setTemplateUrl('radio', 'views/custom-formly-radio.html');
formlyConfigProvider.setTemplateUrl('checkbox', 'views/custom-formly-checkbox.html');
// the same can be accomplished with
formlyConfigProvider.setTemplateUrl({
radio: 'views/custom-formly-radio.html',
checkbox: 'views/custom-formly-checkbox.html'
});Allows you to get the template
formlyConfigProvider.setTemplateUrl('radio', 'views/custom-formly-radio.html');
formlyConfigProvider.getTemplateUrl('radio') === 'views/custom-formly-radio.html'; // trueWork pretty much the same as the their url counterparts, except they accept an actual template string rather than a url.
Allows you to set a template for your formly templates. You can have a default (used by all templates), named template wrappers, and typed template wrappers (used by fields with the specified type). All template wrappers must follow these rules
- Use
<formly-transclude></formly-transclude>in them to specify where the field template should be placed. - Have at least one, and only one of
urlortemplate - Not override another by name or type
For example:
// simple argument api
formlyConfigProvider.setTemplateWrapper('<div>This is the default because <formly-transclude></formly-transclude> there is no name specified</div>');
formlyConfigProvider.setTemplateWrapper('<div>This is not the default because <formly-transclude></formly-transclude> there is a name specified</div>', 'theName');
// object api
formlyConfigProvider.setTemplateWrapper({
name: 'inputWrapper', // optional. Defaults to name || types.join(' ') || 'default'
template: 'the template with <formly-transclude></formly-transclude> in it', // must have this OR url
url: 'path/to/template.html', // the resulting template MUST have <formly-transclude></formly-transclude> in it and must have url OR template (not both)
types: 'stringOrArray' // this can be a string or an array of strings that map to types specified by setTemplate and setTemplateUrl
});
// array api
formlyConfigProvider.setTemplateWrapper([
{ /* same configuration as the object api */ },
{ /* same configuration as the object api */ },
{ /* same configuration as the object api */ },
{ /* same configuration as the object api */ }
]);See the website for examples on usage
Formly gives some useful warnings when you attempt to use a template that doesn't exist or there's a problem loading a template. You can disable these warnings via formlyConfigProvider.disableWarnings = true
Please see the Wiki for tips and tricks from the community.
There are four places where you can put expressions. The context in which these expressions are evaluated is important. There are two different types of context and each is explained below:
-
watcher - expression and listener can be functions or expression strings. This is a regular angular
$watch(depending on the specifiedtype) function and it is created on theformly-formscope, despite being applied to a specific field. This allows the expressions to run even if the field's scope has been destroyed (via an ng-if like when the field is hidden). The function signature differs from a normal$watchhowever. See above for more details. -
expressionProperties & validators - these expressions can be functions or expression strings. If it's a function, it's invoked with the arguments
$viewValue,$modelValue, andscope. The scope in this case, is the field's scope. If it's an expression string, it is evaluated using$scope.$evalwith a locals object that has$viewValueand$modelValue(however, in the case ofexpressionProperties,$viewValuewill simply be the$modelValuebecause they don't have a hook into thengModelControllerbut we want to keep the api consistent).
- Perhaps integrate with angular-form-builder
Please see the CONTRIBUTING Guidelines.
A special thanks to Nimbly for creating/sponsoring Angular-Formly's development. Thanks to Kent C. Dodds for his continued support on the project.