The most visible part of forms-angular is the form-input directive which takes a schema in the scope and creates inputs. These schemas can be very simple as in the example below:
<div ng-init="name=[ {id:'f_surname',label:'Surname',name:'surname',type:'text'}, {id:'f_forename',label:'Forename',name:'forename',type:'text'} ]"> <form> <form-input schema="name" /> </form> <div>
This simple bit of markup creates a small schema and stores it in the $scope as name(you wouldn't normally it like this, but it illustrates things well) then in a form invokes the form-input directive, telling it to use the schema just created.
Unfortunately displaying the resulting form stops the page scrolling to the internal links, so we have decided to omit it - there are plenty of examples accessible elsewhere on the site.
Mongoose is a popular package that simplifies using MongoDB with Node JS. Mongoose requires that a schema is created for each 'model'. As you will have seen in the Get Started section, forms-angular can convert Mongoose schemas into the schemas that the form-input directive uses, which saves a lot of coding by hand and makes it very easy to keep your front and back ends in sync.
As many models as you want can be registered with forms-angular by calling the addResource
method of the forms-angular object as shown in the
Get Started section. If you have more than a few models it is a good idea to put them in their own
directory and loop through it.
A trivially simple schema looks like this:
var ApplicantSchema = new Schema({ surname: {type:String, required:true, index:true}, forename: {type:String, index:true}, });
Each schema element (or field) has a type and optionally a number of other properties. Many of these properties are used by forms-angular to generate appropriate behaviour on the front end. The appropriate input types are used, and client side validation keeps round trips down (we cannot say "to a minimum" as work remains to be done in this area).
You can get an idea of what forms-angular does with a vanilla Mongoose JS schema by looking at this schema and the form generated from it.
For comprehensive information about Mongoose schemas visit the Mongoose JS website.
In a couple of places forms-angular uses the concept of 'list fields' - fields that generally allow the user to quickly see what they are dealing with - for example in the case of a person the forename and surname would be list fields. They don't have to be unique, they just have to be useful. List fields are used:
A field may be specified to be a list field by adding a truthy value for a list key
in the schema element: forename: {type:String, list:true}
List fields can be generated on both the front and back end, and it is done as follows:
The mark-up of generated forms can be influenced by use of the form object in the
schema type: surname: {type:String, form:{label:'Family Name', size:'large'}}
The form object can have the following keys:
This example schema and this form shows many of these options in use.
Sometimes it is hard within a Mongoose schema to detail exactly how you want the associated form or forms to be laid out. One particular class of layout features that currently cannot be requested within a Mongoose schema are containers. Hopefully this will be addressed in a forthcoming release but if you need them now then you need to create a schema by hand (or dynamically in code).
An example of a use of containers would be:
<form-input ng-init="names=[ {containerType:'fieldset',title:'Name',content: [ {id:'f_surname',label:'Surname',name:'surname',type:'text'}, {id:'f_forename',label:'forename',name:'forename',type:'text'} ] } ]" schema="names"></form-input>
Other containers are tab, well, well-large and well-small.
If you want to "roll your own" you can do something like:
<style> .redBorder {border:solid 2px red;} p.bigRed {color: red; font-size: large} </style> <form-input ng-init="names=[ {containerType:'redBorder',title:'Name',titleTagOrClass:'bigRed',content: [ {id:'f_surname',label:'Surname',name:'surname',type:'text'}, {id:'f_forename',label:'forename',name:'forename',type:'text'} ] } ]" schema="names"></form-input>
Where container type is seen as a class for a div and a titleTagOrClass of h1..h6 is interpreted as a heading tag and anything else as a class to be applied to a paragraph (as in the example). If a title is specified without a corresponding titleTagOrClass then <h4> is used as a default.
It is easy to create custom form schemas which are a subset of the whole schema by specifying the fields to include and any options. See the static in this example. The custom form schemas are invoked as follows:
Custom schemas are a good way to allow access to only part of a document, for instance if a customer record contained operational and accounts information an accounts user would require a different view of the data (that would include account balance) than a user from operations (who would not have access to the account balance). Consider the following:
var AddressSchema = new Schema({
type: { type: String, default: 'Home', enum: ['Delivery', 'Invoice', 'Historic'] },
street: String,
town: String,
postalCode: String
}, {_id: false});
and in the customer model
addressList: {type: [AddressSchema], mergeKey: 'type', form: {pane: 'Address', labels: 'Addresses'}}
We need a way of having the delivery address appear on one form (for the operation user) and the invoice address on the finance form, even though they are in the same field.
forms-angular has the concept of a subkey for this scenario. The custom schema for the operations user would contain:
addressList: {subkey: {keyList: {type: 'Delivery'}, containerType: 'well-small', title: 'Address', titleTagOrClass: 'h5'}}
while that for finance might include
addressList: {subkey:
[
{keyList: {type: 'Invoice'}, containerType: 'well-small', title: 'Invoice To:'},
{keyList: {type: 'Delivery'}, containerType: 'well-small', title: 'Deliver To:'}
]
}
Note that subkey can be an object or an array of objects. Where there are multiple sub docs that match the subkey the first will be used.