Forms angular comes with a small number of forms that can be used - sometimes with small amendments - to meet a vast number of requirements. They all use RESTful routes and handle querying and updating the server.
For each model the following routes are supported:
These routes are set up by making a call such as:
myDemoApp.config(['formRoutesProvider', function (formRoutes) {
formRoutes.setRoutes([
{route:'/index', options:{templateUrl: 'partials/landing-page.html'}},
{route:'/get-started', options:{templateUrl: 'partials/get-started.html'}}
], '/index');
}]);which (in the first parameter) declares two routes explicitly - the standard model routes are created automatically. The
second parameter specifies the 'otherwise' route.
If you prefer HTML5Mode (which gives routes without the /#) or if you want to use AJAX crawling then you can configure forms-angular like this:
formsAngular.config(['urlServiceProvider',function(urlService) {
urlService.setOptions({html5Mode: true, hashPrefix: '!'});
}]);
if you are using HTML5Mode ensure that your index.html file has
<base href="/">
in the header.
Whatever options are used, forms-angular will create the correct routes using
urlService.buildUrl(path)
which will add in hashes and prefixes to the path as required. This service can be used in your application so that
you can easily switch between modes without having to manually change all the internal links.
The form-input directive expands the schema into nice looking data capture form, but that is only a small part of the story. The basic edit form also gives you:
Buttons to perform the usual Save, Cancel, New and Delete operations. forms-angular handles all the back-end stuff for you.
The form button customisation is currently limited to over-riding the default enabled state of the buttons by defining functions in a controller for the model (or model and form). See here for an example.
The listing routes (of the format /#/:model) are used to build a page containing a list of documents in the collection, showing the list fields. You can specify a sort order by adding a listOrder value to the model as shown in this model. Alternatively you can specify a sort order at run-time using the o parameter as in this example.
Listing routes support filters, such as
/#/b_using_options?f={"surname":"Smith"}
and calls to the aggregation framework, provided they project to an array of docs that contains an _id property which is used to select from the model. For example the (rather unpalatable)
/#/f_nested_schema?a=[{"$unwind":"$exams"},{"$sort":{"exams.score":1}},{"$group":{"_id":{"id":"$_id"},"bestSubject":{"$last":"$exams.subject"}}},{"$match":{"bestSubject":"English"}},{"$project":{"_id":"$_id.id"}}]
selects all students who did better in their English exam than any other subject. To find out how to use the aggregation framework refer to the MongoDB docs.
These can be combined (though there appears to be a problem unless the filter precedes the aggregation).
By default the list order is the MongoDB natural order. The default list order for a table can be set by specifying a listOrder option in the model definition (see g_conditional_fields for an example).
The smallest form is the search form, in the navbar at the top of this page. It is implemented as a directive - global-search. When you enter text into it the controller makes a call to the server which looks for matching data by searching the indexed fields in each model for values starting with the search string.
There are a number of options that can be added to model exports to modify the search behaviour. Examples can be found in the model definitions files for f_nested_schema and g_conditional_fields.
DataFormHandler.getResource('person').options.localisationData = [{from: 'person/customer', to: 'person/client', context: 'resource'},{from:'Customer', to:'Client', context: 'resourceText'}];
The form-input directive, which is the core component of forms-angular, takes one mandatory attribute - schema - which is documented at the top of the page and some optional attributes.
The optional attributes that can be passed for form-input are:
| formstyle | Associated Bootstrap class | forms-angular class | Restrictions |
|---|---|---|---|
| horizontalCompact / compact (default) | form-horizontal | compact | |
| vertical | |||
| horizontal | form-horizontal | ||
| inline | form-inline | Array fields not supported | |
Forms and sub forms that are created by the directive will have these classes added in the appropriate place.
Additional functionality can be added by using "model controllers" which have the name of a model followed by Ctrl (or the name of the model followed by the name of the custom form followed by Ctrl. There is an sample model controller here which is used in the examples in this section. The NavCtrl controller handles the model controllers, so don't remove it.
The BaseCtrl scope has a variable called modelNameDisplay which is used in several places in the demo app. It defaults to the model name in title case, but can be over-ridden in the model controller.
The menu can be added to where required by the models (see an example here) The options can be configured to appear when records are being listed, edited or created. The top level text is taken from the model controller's dropDownDisplay variable, if present. If not present it will fall back to the modelNameDisplay (see above) and if that is not present the model name.
Sometimes menu options only apply to a subset of records in a collection. In this case they can be hidden by specifying an isHidden($index) function. For example to hide the option when a field has a certain value:
$scope.contextMenu = [{
text: 'Do something',
fn : function() {// some code},
isHidden: function() {
return $scope.record._id ? $scope.record.field === 'value' : true;
},
...
}];
The form-input directive broadcasts a formInputDone message when it has processed a control. This can be acted on by the model controller. In our example we add a change handler to a select2 control which changes background color of a control group when the eye colour is changed. Try it, and then see how it is done at the bottom of this controller.
There are hooks before and after CRUD events as follows:
In all onBefore... cases passing an error back will stop the event completing. There is a trivial example of how a data event hook might be used in this controller, which shows how such event handlers are set up.
You can also call onRecordChange function(data, old) which is useful for updating calculated fields etc.
It is possible to apply additional attributes to all elements of a certain type by passing it once in the form-input declaration. The available types are Control Group, Field or Label.
This can be achieved in two ways. Either as an attribute of the form-input element:
<form-input schema="formSchema" add-all-group="injected-element='with parameters'">
or via the controller by making it an attribute of scope:
$scope.addAllGroup="injected-attribute"
The three versions of this are:
For example if wished to inject a directive called 'hide-on-empty' to every individual control group then you would add:
add-all-group="hide-on-empty"
to the form-input declaration.
If declared in a controller then it will be applied to all child controllers. If declared in the form-input element the scope is limited to the individual form's scope. In this way a single declaration at the root scope is seem by all controllers.
Due to the parse method, in order declare multiple classes each class must be prefixed with 'class=' e.g.
<form-input schema="formSchema" add-all-group="injected-element='with parameters' class=myclass class=my-second-class">
On the server side there are hooks around data events as follows:
There are examples of both in this model