AutoForm is a Meteor package that adds UI components and helpers to easily create basic forms with automatic insert and update events, and automatic reactive validation. This package requires and automatically installs the simple-schema package. You can optionally use it with the collection2 package, which you have to add to your app yourself.
AutoForm 5.0 is now available and has many compatibility breaks, but also many fixes and improvements. This release includes a lot of internal refactoring, so it's a good idea to thoroughly test all your forms after updating to 5.0+. Be sure to check out the change log for full details.
Add-on Package Authors: Please test your package against AutoForm 5.0, and then release an update in which you change your api.use to api.use('aldeed:[email protected] || 5.0.0');
Table of Contents generated with DocToc
- Installation
- Demo
- Example
- Component and Helper Reference
- Objects and Arrays
- Form Types
- Public API
- Non-Collection Forms
- Fine Tuning Validation
- Manual Validation
- Resetting Validation
- The Form Document
- Callbacks/Hooks
- Putting Field Attribute Defaults in the Schema
- Complex Schemas
- Dates
- Theme Templates
- Defining Custom Input Types
- Common Questions
- Should the value of
schemaandcollectionhave quotation marks around it? - Which components should I use?
- Can I reuse the same
quickFormorautoFormfor both inserts and updates? - How can I show an asterisk after the label for required fields?
- What are the various ways I can specify options for a select, radio group, or checkbox group?
- Can I put HTML in my error messages?
- Should the value of
- Examples
- Troubleshooting
- Contributing
In a Meteor app directory, enter:
$ meteor add aldeed:autoform
Submit a pull request to add your package to this list!
Dates and times:
- aldeed:autoform-bs-datepicker
- aldeed:autoform-bs-datetimepicker
- miguelalarcos:afwrap-xdatetime
- notorii:autoform-datetimepicker
- lukemadera:autoform-pikaday
- antalakas:autoform-bs-daterangepicker
- drewy:autoform-datetimepicker
- bookmd:autoform-time-from-now
Selects:
- aldeed:autoform-select2
- aldeed:autoform-bs-button-group-input
- comerc:autoform-selectize
- vazco:universe-autoform-select
- lukemadera:autoform-autocomplete
- rikonor:autoform-image-gallery
WYSIWYGs:
Markdowns:
Autocompletes:
- comerc:autoform-placecomplete
- miguelalarcos:afwrap-xautocomplete
- comerc:autoform-typeahead
- lukemadera:autoform-googleplace
- lukemadera:autoform-autocomplete
Files:
Maps:
Ranges/Sliders:
Payments
Other:
- meteoric:autoform-ionic
- fabienb4:autoform-semantic-ui
- gildaspk:autoform-materialize
- poetic:react-autoform-material-ui
Let's say you have the following Mongo.Collection instance, with schema support
provided by the collection2 package. (Adding autoform to your app does not add
collection2 by default so you need to run meteor add aldeed:collection2 for this example
to work.)
Books = new Mongo.Collection("books");
Books.attachSchema(new SimpleSchema({
title: {
type: String,
label: "Title",
max: 200
},
author: {
type: String,
label: "Author"
},
copies: {
type: Number,
label: "Number of copies",
min: 0
},
lastCheckedOut: {
type: Date,
label: "Last date this book was checked out",
optional: true
},
summary: {
type: String,
label: "Brief summary",
optional: true,
max: 1000
}
}));Be sure to define proper insert security for untrusted code if you've removed the insecure package. Call allow/deny or use ongoworks:security.
<template name="insertBookForm">
{{> quickForm collection="Books" id="insertBookForm" type="insert"}}
</template>That's it! This gives you:
- An autogenerated form that uses bootstrap3 classes.
- Appropriate HTML5 fields for all keys in the "Books" collection schema.
- A submit button that gathers the entered values and inserts them into the "Books" collection.
- Form validation based on the schema attached to the "Books" collection. By default the form is validated when the user submits. If anything is invalid, the form is continually re-validated on keyup (throttled) as the user fixes the issues.
- Default validation error messages that appear under the fields, and can be customized and translated.
An update form is similar to an insert form, except you need to provide the document with the original values to be updated:
<template name="updateBookForm">
{{> quickForm collection="Books" doc=this id="updateBookForm" type="update"}}
</template>This example uses doc=this, assuming that you use something like iron:router's data function to set the template's data context to the book document. This is a common way to do it, but you could also use a helper function that returns the document.
Be sure to define proper update security for untrusted code if you've removed the insecure package. Call allow/deny or use ongoworks:security.
If you want to customize autogenerated forms for all forms, you can easily
do so by writing your own templates. Refer to the templates section. However,
sometimes a certain form has a complex schema or unique UI requirements, in
which case you can use autoForm rather than quickForm, allowing you to
define fields individually.
Here's an example:
<template name="insertBookForm">
{{#autoForm collection="Books" id="insertBookForm" type="insert"}}
<fieldset>
<legend>Add a Book</legend>
{{> afQuickField name='title'}}
{{> afQuickField name='author'}}
{{> afQuickField name='summary' rows=6}}
{{> afQuickField name='copies'}}
{{> afQuickField name='lastCheckedOut'}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
</template>In this example, we added rows=6 to the "summary" field, which will cause it
to be rendered as a textarea instead of a normal text input field.
In the previous example of a custom insert form, we saw how afQuickField can
be used to render a field with simple customizations. Now let's say we need to
fully customize one of the fields. To do this, you can use the following more
specific templates and helpers:
- afFieldIsInvalid
- afFieldInput
- afFieldMessage
Here's an example:
<template name="insertBookForm">
{{#autoForm collection="Books" id="insertBookForm" type="insert"}}
<fieldset>
<legend>Add a Book</legend>
{{> afQuickField name='title'}}
{{> afQuickField name='author'}}
{{> afQuickField name='summary' rows=6}}
{{> afQuickField name='copies'}}
{{> afQuickField name='lastCheckedOut'}}
<div class="form-group{{#if afFieldIsInvalid name='cost'}} has-error{{/if}}">
<div class="input-group">
<span class="input-group-addon">$</span>
{{> afFieldInput name='cost'}}
<span class="input-group-addon">/each</span>
</div>
{{#if afFieldIsInvalid name='cost'}}
<span class="help-block">{{afFieldMessage name='cost'}}</span>
{{/if}}
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
</template>We added a cost field to our form and customized it to display as an input
group with add-ons.
NOTE: The afDeleteButton component that used to be part of autoform is now available as a separate package.
Use this component as a block instead of <form> elements to wrap your form and
gain all the advantages of the autoform package.
The following attributes are recognized:
collection: Required ifschemais not set. Set to one of the following:- The name of a helper function (no quotation marks) that returns an
instance of
Mongo.Collectionthat has a schema defined. - The name (in quotation marks) of a
Mongo.Collectioninstance that has a schema defined and is in thewindownamespace.
- The name of a helper function (no quotation marks) that returns an
instance of
schema: Required ifcollectionis not set. This schema will be used to generate and validate the form prior to submission, so you can specify this along with acollectionif you want to use a schema that is slightly different from the one your collection uses. However, the final object will still have to pass validation against the collection schema. Set to one of the following:- The name of a helper function (no quotation marks) that returns an
instance of
SimpleSchema. - The name (in quotation marks) of a
SimpleSchemainstance that is in thewindownamespace.
- The name of a helper function (no quotation marks) that returns an
instance of
id: Required. This is used as theidattribute on the renderedformelement, so it must be unique within your entire application. It's required because we use it to set up a form-specific validation context and to preserve input values when a "hot code push" happens.doc: Required for an update form, and must have at least an_idproperty. Pass the current document object, retrieved with a call tofindOne()for example. For an insert form, you can also use this attribute to pass an object that has default form values set (the same effect as setting avalueattribute on each field within the form).validation: Optional. See the "Fine Tuning Validation" section.template: Optional. See the "Templates" section.type: Optional. The form type. Default if not provided is "normal". See Form Types.meteormethod: Optional. Whentypeis "method" or "method-update", indicate the name of the Meteor method in this attribute.ddp: Optional. Whentypeis "method" or "method-update", provide an alternative DDP Connection that should be used to call the Meteor method in this attribute.resetOnSuccess: Optional. The form is automatically reset for you after a successful submission action. You can skip this by setting this attribute tofalse.autosave: Optional. Set totrueto enable automatic form submission for atype="update"form. Whenever theform changeevent is emitted, the change will be automatically saved to the database.autosaveOnKeyup: Optional. Set totrueto enable automatic form submission for atype="updateform onkeyupevent. Whenever akeyupevent is emitted on a form field, the change will be automatically saved to the database (throttled to 500ms). It's best to settrimStrings=falsewhen using this option. If you don't, spaces may be deleted while typing.filter: Optional. Set tofalsefor an insert or update form to skip filtering out unknown properties when cleaning the form document.autoConvert: Optional. Set tofalsefor an insert or update form to skip autoconverting property values when cleaning the form document.removeEmptyStrings: Optional. Set tofalsefor an insert or update form to keep empty string values when cleaning the form document.trimStrings: Optional. Set tofalsefor an insert or update form to keep leading and trailing spaces for string values when cleaning the form document.setArrayItems: Optional. Set totruefor an update form that is updating specific array items. Note that there is a quirk of MongoDB that will create objects instead of arrays when this is set totrue, if there is not already an array in the database. So if you set this totrue, be sure that the corresponding array property is nevernullor missing in the database. It must always be an array with 0 or more items.preserveForm: Optional. Set tofalseto disable preserving of form values across hot refreshes. This can sometimes help resolve issues with sticky form values.- Any additional attributes are passed along to the
<form>element, meaning that you can add classes, etc. When providing a boolean attribute, set it totrue(no quotation marks) or a helper that returnstrue.
Use this component to generate an entire form in one line. It takes and requires
all the same attributes as autoForm. In addition, it recognizes the following
attributes:
type: Two additional type values are supported: "readonly" and "disabled".buttonClasses: Set the class attribute for the rendered submit button. Some templates may provide a default class if you don't set this.buttonContent: The submit button content. If you don't set this, "Submit" is used. If you set this tofalse, no submit button is rendered.fields: Optional. Bind an array or specify a comma-delimited string of field names to include. Only the listed fields (and their subfields, if any) will be included, and they'll appear in the order you specify.omitFields: Optional. Bind an array or specify a comma-delimited string of field names to omit from the generated form. All first-level schema fields except the fields listed here (and their subfields, if any) will be included.
Any other attributes you specify will be output as attributes of the <form> element, just like when using the autoForm component. When providing a boolean attribute, set it to true (no quotation marks) or a helper that returns true.
See this demo for examples of what happens when you specify various types of fields in the fields or omitFields attributes.
Renders an input control for the field. The type of control depends on what you set the type attribute to. All of the HTML5 input types plus a few more are built in. Here is the full list of included input types:
- boolean-checkbox
- boolean-radios
- boolean-select
- button
- color
- contenteditable
- date
- datetime
- datetime-local
- file
- hidden
- image
- month
- number
- password
- radio
- range
- reset
- search
- select
- select-checkbox
- select-checkbox-inline
- select-multiple
- select-radio
- select-radio-inline
- submit
- tel
- text
- textarea
- time
- url
- week
There are add-on packages that provide additional input types (widgets, UI controls).
If you don't include a type attribute, the following logic is used to automatically select an appropriate type:
- If you supply the
optionsattribute, aselectinput is used. If your schema expects an array for the field, then it is aselect-multipleinput. If you prefer radios or checkboxes (for example, if it is a short list of options), then addnoselect=trueattribute or simply set thetypetoselect-checkbox,select-checkbox-inline,select-radio, orselect-radio-inline. - Otherwise if the schema type is
Stringand you include therowsattribute, atextareais used. - Otherwise if the schema type is
Number, anumbertype is used. - Otherwise if the schema type is
Date, adatetype is used. - Otherwise if the schema type is
Boolean, theboolean-checkboxtype is used. You may want to specify atypeofboolean-radiosorboolean-selectinstead. If you do so, use thetrueLabel,falseLabel, andnullLabelattributes to set the labels used in the radio or select control. - Otherwise a
texttype is used.
The following attributes are recognized:
name: Required. The name of the schema key this field is for.template(default="bootstrap3"): Specify the name of a different built-in or custom theme template to use.options: An array of options objects (see below). By specifying options, you cause the generated DOM element to be aselectelement with these options, unless you also usenoselect. To use theallowedValuesfrom the schema as the options, setoptions="allowed". To specify a label to be displayed when there is no option selected, setfirstOption="(My Select One Label)".firstOption: Use with theoptionsattribute to specify a string to use for the first option of aselectinput, which shows when nothing has been selected yet. For example,firstOption="(You Should Really Pick Something From This List)". There is a default first option "(Select One)". If you don't want any default option, then dofirstOption=false, but make sure yourselectinput has a defaultvalueor this will result in a confusing UX where it looks like the first option is selected but it isn't.capitalize: Used only when you've setoptions="allowed". Set this totrueto capitalize the labels generated fromallowedValues.noselect: Use in conjunction withoptionsattribute. Set this attribute totrueto render radios or checkboxes for theoptionsinstead ofselect.trueLabel: Set to the string that should be used as the label for thetrueoption for an input with typeboolean-selectorboolean-radios.falseLabel: Set to the string that should be used as the label for thefalseoption for an input with typeboolean-selectorboolean-radios.nullLabel: Set to the string that should be used as the label for the empty value option for an input with typeboolean-selectorboolean-radios.value: Set a specific, potentially reactive, value for the input. If you have also provided adocattribute on theautoFormorquickForm, this value will override the value from thedocobject.defaultValue: Set a reactive default value for the input. If you have also provided adocattribute on theautoFormorquickForm, this value will be used only when thedocobject has no value for this field. This takes precedence over thedefaultValueproperty of the field's schema. (Also,defaultValuefrom the schema is slightly different in that it is never used if you provide adocattribute.)- Any additional attributes are passed along to the generated DOM element, meaning that you can add
class, etc. When providing a boolean attribute, set it totrue(no quotation marks) or a helper that returnstrue. placeholder: As with other attributes, this will be passed along to the generated DOM element, but you can also optionally doplaceholder="schemaLabel"to use the field label defined in the schema as the input's placeholder value.- Each individual input type might accept and use additional attributes.
Here's an example of passing options to generate a select field:
html:
{{> afFieldInput name="year" options=yearOptions}}client.js:
Template.registerHelper("yearOptions", function() {
return [
{label: "2013", value: 2013},
{label: "2014", value: 2014},
{label: "2015", value: 2015}
];
});Alternatively, you can specify options as an object with {value: label} format. Values are coerced into the expected type.
Template.registerHelper("yearOptions", function() {
return {
2013: "2013",
2014: "2014",
2015: "2015"
};
});You can also mix in optgroups. See the demo.
Accepts and requires just one attribute, name, which is the name of the schema key.
Outputs the user-friendly invalid reason message for the specified property, or an empty string if the property is valid. This value updates reactively whenever validation is performed. Refer to the SimpleSchema documentation for information on customizing the messages.
Accepts and requires just one attribute, name, which is the name of the schema key.
Returns true if the specified key is currently invalid. This value updates
reactively whenever validation is performed.
Just as quickForm renders a form in one line, afFormGroup renders a form
group, that is, everything related to a single field -- the label, the input,
and the error message -- in one line.
This component accepts the same attributes as afFieldInput.
Attributes that are prefixed with formgroup- become attributes on the div
element, which contains the label and the field. Attributes that are prefixed
with label- become attributes on the rendered label element while any
remaining attributes are forwarded to the afFieldInput component. You can
also set label=false to omit the label element or set label to a
string to use that text as the label text.
The afQuickField component is a helper component that decides whether a particular key should be rendered using afArrayField, afObjectField, or afFormGroup, and then forwards all your attributes to the chosen component.
- When you use the
afQuickFieldcomponent for a field that is anObject, it is rendered using theafObjectFieldcomponent unless you override thetypeor supplyoptions. - When you use the
afQuickFieldcomponent for a field that is anArray, it is rendered using theafArrayFieldcomponent unless you override thetypeor supplyoptions. - All other keys are rendered using the
afFormGroupcomponent.
Refer to the "Objects and Arrays" section for additional information.
{{> afQuickField name='firstField' autofocus=''}}
{{> afQuickField name='weirdColors' style="color: orange" label-style="color: green"}}
{{> afQuickField name="longString" rows=5}}
{{> afQuickField name="radioBoolean" type="boolean-radios" trueLabel="Yes" falseLabel="No"}}
{{> afQuickField name="selectBoolean" type="boolean-select" trueLabel="Yes" falseLabel="No"}}
{{> afQuickField name="optionsButNoSelect" options=numSelectOptions noselect="true"}}
{{> afQuickField name="firstOptionSelect" firstOption="(Select Something)" options=numSelectOptions}}
{{> afQuickField name="decimal" step="0.01"}}Use this helper with #if to dynamically show and hide sections of a form based on the current value of any non-array field on the form.
See the demo
Use with #each to loop through all the field names for the form's schema or for an object field.
The following is roughly equivalent to a quickForm:
{{#autoForm}}
{{#each afFieldNames}}
{{> afQuickField name=this.name options=afOptionsFromSchema}}
{{/each}}
{{/autoForm}}Or you can provide a name attribute for an object field:
{{#autoForm}}
{{#each afFieldNames name="profile"}}
{{> afQuickField name=this.name options=afOptionsFromSchema}}
{{/each}}
{{/autoForm}}You can optionally pass fields or omitFields attributes to afFieldNames.
Render an afQuickField for each field in the form schema or an object field.
{{#autoForm}}
{{! These do the same thing}}
{{#each afFieldNames name="profile"}}
{{> afQuickField name=this.name options=afOptionsFromSchema}}
{{/each}}
{{> afQuickFields name="profile"}}
{{/autoForm}}You can optionally pass fields or omitFields attributes to afQuickFields.
Fields with type Object or Array are treated specially.
When you use the afQuickField component for a field that is an Object, it is rendered using the afObjectField component unless you override the type or specify options. This happens by default when you use a quickForm for a schema that has a field of type Object.
The afObjectField component renders all of an object field's subfields together as one group. The group is labeled with the name of the object field. The actual visual representation of the group will vary based on which theme template you use. For the "bootstrap3" default template, the group appears in a panel with a heading.
When you use the afQuickField component for a field that is an Array, it is rendered using the afArrayField component unless you override the type or specify options. This happens by default when you use a quickForm for a schema that has a field of type Array.
The afArrayField component renders all of an array field's array items together as one group. The group is labeled with the name of the array field. The actual visual representation of the group will vary based on which theme template you use. For the "bootstrap3" default template, the group appears in a panel with a heading.
Additionally, buttons for adding and removing array items are automatically added to the UI. This is also done by the template, which means that you can easily make your own "afArrayField" template if you don't like the default appearance of the add/remove buttons.
An afArrayField (or an afQuickField for an array) supports the additional attributes minCount and maxCount. Normally, by default, you cannot remove items below the schema-defined minCount and you cannot add items above the schema-defined maxCount. However, sometimes you don't want a minimum or maximum count defined in the schema, but you do want to limit the number of items on a certain form. To do this, use the minCount and maxCount attributes. Note, however, that you may not override the minCount to be less than the schema-defined minCount, and you may not override the maxCount to be more than the schema-defined maxCount.
An afArrayField (or an afQuickField for an array) also supports the initialCount attribute. Use it to override the default initial count to be something other than 1, including 0. Note that minCount will still always take precedence. That is, if the minCount is 1 and you specify initialCount=0, the initial count will be 1.
To specify options for each item in the array you can set
'arrayFieldName.$': {
...
autoform: {
afFieldInput: {
options: function () {
//return options object
}
}
}
}
At the moment, the add and remove buttons disappear when you can't use them. This could be changed to make them disabled. You can do this yourself with a custom template, but if you have thoughts about how it should work out of the box, submit an issue to discuss.
This is a block helper that can be used to render specific content for each item in an array. It tracks the addition and removal of array item fields (or groups of fields) reactively for you. This allows you to customize the repeated array fields, removal buttons, etc. It's generally most useful within a custom afArrayField template. See the built-in afArrayField templates for example usage.
afEachArrayItem supports the same attributes as afArrayField.
These helpers must be used within an afEachArrayItem block and will return true or false depending on whether the current item/field in the array is the first or last visible item, respectively.
Depending on the type attribute you specify on your quickForm or autoForm, your form will have different behavior when rendering, validating, and submitting. The following form types are built in to the package, but you may also define your own.
Generates a document and inserts it on the client. You must provide a collection attribute referencing the Mongo.Collection instance. If the collection has an attached schema, it will be used for validation. If you provide a schema attribute, that schema will be used for validation, but the document must validate against the collection's schema, too.
Updates a document on the client. You must provide a collection attribute referencing the Mongo.Collection instance. If the collection has an attached schema, it will be used for validation. If you provide a schema attribute, that schema will be used for validation, but the document must validate against the collection's schema, too.
The form will generate and validate an update modifier. You must specify a doc attribute referencing the current document, which must have an _id property. Any properties present in doc will be used as the default values in the form fields.
Updates a document on the client by adding the form document to an array within the larger document. You must provide a collection attribute referencing the Mongo.Collection instance. If the collection has an attached schema, it will be modified to be scoped appropriately and that new schema will be used for validation. If you provide a schema attribute, that schema will be used for validation, but the document must validate against the collection's schema, too.
You can think of this as an insert form for subdocuments. It generates and validates a document instead of a modifier, pretending that the array item schema is the full schema. Then it performs an update operation that does a $push of that document into the array.
Use the scope attribute on your form to define the array field into which the resulting document should be pushed. For example, scope="employees" or scope="employees.0.addresses".
Will call the server method with the name you specify in the meteormethod attribute. Passes a single argument, doc, which is the document resulting from the form submission.
You may optionally specify a DDP Connection in the ddp attribute. If you do, the method will be called using the DDP connection provided.
The method is not called until doc is valid on the client.
You must call check() in the method or perform your own validation since a user could bypass the client side validation.
Will call the server method with the name you specify in the meteormethod attribute.
There are two possible ways your method can be called.
- By default, your method will be called with two arguments:
modifier: the modifier object generated from the form valuesdocumentId: the_idof the document being updated
- If you set
singleMethodArgument=trueas a form attribute, your method will be called with a single object argument with_idandmodifierproperties. You should do this if using the mdg:validated-method package.
You may optionally specify a DDP Connection in the ddp attribute. If you do, the method will be called using the DDP connection provided.
The method is not called until modifier is valid on the client.
You must call check() in the method or perform your own validation since a user could bypass the client side validation. Using the mdg:validated-method package is recommended.
Will call any onSubmit hooks you define, where you can do custom submission logic. If onSubmit does not return false or call this.event.preventDefault(), the browser will also submit the form. This means that you can use AutoForm to generate and validate a form but still have it POST normally to an HTTP endpoint.
Example:
AutoForm.hooks({
contactForm: {
onSubmit: function (insertDoc, updateDoc, currentDoc) {
if (customHandler(insertDoc)) {
this.done();
} else {
this.done(new Error("Submission failed"));
}
return false;
}
}
});The arguments passed to your onSubmit hook are as follows:
insertDoc: The form input values in a document, suitable for use with insert(). This object has been cleaned and validated, but auto values and default values have not been added to it.updateDoc: The form input values in a modifier, suitable for use with update(). This object has not been validated.currentDoc: The object that's currently bound to the form through thedocattribute
In addition to the normal this hook context, there is a this.done() method, which you must call when you are done with your custom client submission logic. This allows you to do asynchronous tasks if necessary. You may optionally pass one argument. If you pass an Error object, then any onError hooks will be called; otherwise, any onSuccess hooks will be called.
If you return false, no further submission will happen, and it is equivalent
to calling this.event.preventDefault() and this.event.stopPropagation(). If you return anything other than false, the browser will submit the form.
If you use autoValue or defaultValue options, be aware that insertDoc and
updateDoc will not yet have auto or default values added to them. If you're
passing them to insert or update on a Mongo.Collection with a schema, then
there's nothing to worry about. But if you're doing something else with the
object on the client, then you might want to call clean to add the auto and
default values:
AutoForm.hooks({
peopleForm: {
onSubmit: function (doc) {
PeopleSchema.clean(doc);
console.log("People doc with auto values", doc);
this.done();
return false;
}
}
});If you're sending the objects to the server in any way, it's always best to
wait to call clean until you're on the server so that the auto values can be
trusted.
All inputs will be disabled. Nothing happens when submitting.
All inputs will be read-only. Nothing happens when submitting.
For the full public API available on the AutoForm object, refer to the API documentation.
If you want to use an AutoForm for a form that does not relate to a collection (like a simple contact form that sends an e-mail), or for a form that relates to a collection that is schemaless (for example, Meteor.users()), you can do that.
- In client+server code, create a
SimpleSchemainstance to define the form's schema. - Use the
SimpleSchemainstance as theschemaattribute ofautoFormorquickForm. - Set up the form to be submitted properly. There are three ways to handle this:
- Define an
onSubmithook for the form. Put your logic in that function and have it returnfalseto prevent normal form submission. - Add normal form attributes like
actionand let the form do a normal browser POST after being validated. - Define a server method that does something with the form data. On your
autoFormorquickFormsettype="method"andmeteormethod="yourServerMethodName".
- Define an
When you use the third option, a server method, the form data will be gathered
into a single object when the user clicks the submit button. Then that object
will be cleaned and validated against the schema on the client and passed along
to your method on the server. You must
validate it again in your method on the server, using check() in combination
with myAutoFormSchema. This is why we create the SimpleSchema instance
in client+server code.
It's also generally best to call myAutoFormSchema.clean for the object again
in the server method. In particular, you will definitely want to do this if
the object's schema has auto or default values so that they can be added
securely and accurately on the server.
common.js:
Schema = {};
Schema.contact = new SimpleSchema({
name: {
type: String,
label: "Your name",
max: 50
},
email: {
type: String,
regEx: SimpleSchema.RegEx.Email,
label: "E-mail address"
},
message: {
type: String,
label: "Message",
max: 1000
}
});Note that we've created an object Schema in which to store all of our app's schemas.
html:
<template name="contactForm">
{{#autoForm schema=contactFormSchema id="contactForm" type="method" meteormethod="sendEmail"}}
<fieldset>
<legend>Contact Us</legend>
{{> afQuickField name="name"}}
{{> afQuickField name="email"}}
{{> afQuickField name="message" rows=10}}
<div>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-default">Reset</button>
</div>
</fieldset>
{{/autoForm}}
</template>client.js
Template.contactForm.helpers({
contactFormSchema: function() {
return Schema.contact;
}
});server.js
Meteor.methods({
sendEmail: function(doc) {
// Important server-side check for security and data integrity
check(doc, Schema.contact);
// Build the e-mail text
var text = "Name: " + doc.name + "\n\n"
+ "Email: " + doc.email + "\n\n\n\n"
+ doc.message;
this.unblock();
// Send the e-mail
Email.send({
to: "[email protected]",
from: doc.email,
subject: "Website Contact Form - Message From " + doc.name,
text: text
});
}
});Note the call to check(), which will throw an error if doc doesn't
match the schema. To reiterate, you must call check() in the method or perform your
own validation since a user could bypass the client side validation. You do
not have to do any of your own validation with collection inserts or updates,
but you do have to call check() on the server when submitting to a Meteor method.
To control when and how fields should be validated, use the validation
attribute on autoform or quickForm. Supported values are:
none: Do not validate any form fields at any time.submit: Validate all form fields only when the form is submitted.keyup: Validate each form field on every key press and when the user moves the cursor off it (throttled to run at most once every 300 milliseconds). Also validate all fields again when the form is submitted.blur: Validate each form field when the user moves the cursor off it (throttled to run at most once every 300 milliseconds). Also validate all fields again when the form is submitted.submitThenKeyup: (Default) At first acts like thesubmitoption, but after the user attempts to submit the form and it fails validation, subsequently acts likekeyup.submitThenBlur: At first acts like thesubmitoption, but after the user attempts to submit the form and it fails validation, subsequently acts likeblur.browser: Let the browser handle validation, if supported.
Notes:
- For all values except "browser", the
novalidate="novalidate"attribute is automatically added to the rendered form. - If you choose
keyupvalidation, there is sometimes a bug in Safari where the autofill mechanism will cause the text in the input to be selected after each keypress. This has the effect of making it near impossible to actually type anything because you keep "overwriting" your input. To fix this, simply add theautocomplete="off"attribute to your input fields.
In addition to telling your form to validate on certain events, sometimes you need to manually validate.
- To validate a particular field, call
AutoForm.validateField(formId, fieldName, skipEmpty). It returnstrueorfalsedepending on the validity of the field's current value, and it also causes reactive display of any errors for that field in the UI. - To validate a form, call
AutoForm.validateForm(formId). It returnstrueorfalsedepending on the validity of the current values in the entire form, and it also causes reactive display of any errors for that form in the UI.
After a successful submission, validation is reset, ensuring that any error
messages disappear and form input values are correct. However, you may need
to reset validation for other reasons, such as when you reuse an edit form to
edit a different document. To do this, call AutoForm.resetForm(formId).
When the user submits an autoForm, an object that contains all of the form
data is automatically generated. If the submission action is insert or a
method call, this is a normal document object. If the submission action is
update, this is a mongo modifier with $set and potentially $unset objects.
In most cases, the object will be perfect for your needs. However, you might
find that you want to modify the object in some way. For example, you might
want to add the current user's ID to a document before it is inserted. You can
use "before", "formToDoc", or "formToModifier" hooks to do this.
You can get the current values of all fields on a form at any time by passing the form id
to AutoForm.getFormValues. This method is not reactive. The form must be
currently rendered for this to work.
You can get the current value of a specific field on a specific form by passing the field name to AutoForm.getFieldValue. This method is reactive so it can be used in place of the built-in afFieldValueIs helper to show pieces of a form based on
custom criteria about the values of other fields on the form. If using outside of the autoForm, pass the formId as the second argument.
To add client-side hooks and callbacks for a form, use the AutoForm.hooks or AutoForm.addHooks method. The syntax for AutoForm.hooks is
AutoForm.hooks({
myFormId: hooksObject
});If you want to add the same hook for multiple forms or for all forms, use the
AutoForm.addHooks method instead:
// Pass an array of form IDs for multiple forms
AutoForm.addHooks(['form1', 'form2', 'form3', 'form4'], hooksObject);
// Or pass `null` to run the hook for all forms in the app (global hook)
AutoForm.addHooks(null, hooksObject);
// Pass `true` as optional third argument to replace all existing hooks of the same type
AutoForm.addHooks('form1', hooksObject, true);These calls should be anywhere in top-level client code and do not need to be within Meteor.startup. You should not put them in an autorun, template rendered function, or anywhere else where they will be called multiple times since that will cause the hooks to run multiple times for a single submission.
In all of the above examples, the hooks object should look like the following. This shows all possible hooks, but your object would have only the hooks that you need:
var hooksObject = {
before: {
// Replace `formType` with the form `type` attribute to which this hook applies
formType: function(doc) {
// Potentially alter the doc
doc.foo = 'bar';
// Then return it or pass it to this.result()
//return doc; (synchronous)
//return false; (synchronous, cancel)
//this.result(doc); (asynchronous)
//this.result(false); (asynchronous, cancel)
}
},
// The same as the callbacks you would normally provide when calling
// collection.insert, collection.update, or Meteor.call
after: {
// Replace `formType` with the form `type` attribute to which this hook applies
formType: function(error, result) {}
},
// Called when form does not have a `type` attribute
onSubmit: function(insertDoc, updateDoc, currentDoc) {
// You must call this.done()!
//this.done(); // submitted successfully, call onSuccess
//this.done(new Error('foo')); // failed to submit, call onError with the provided error
//this.done(null, "foo"); // submitted successfully, call onSuccess with `result` arg set to "foo"
},
// Called when any submit operation succeeds
onSuccess: function(formType, result) {},
// Called when any submit operation fails
onError: function(formType, error) {},
// Called every time an insert or typeless form
// is revalidated, which can be often if keyup
// validation is used.
formToDoc: function(doc) {
// alter doc
// return doc;
},
// Called every time an update or typeless form
// is revalidated, which can be often if keyup
// validation is used.
formToModifier: function(modifier) {
// alter modifier
// return modifier;
},
// Called whenever `doc` attribute reactively changes, before values
// are set in the form fields.
docToForm: function(doc, ss) {},
// Called at the beginning and end of submission, respectively.
// This is the place to disable/enable buttons or the form,
// show/hide a "Please wait" message, etc. If these hooks are
// not defined, then by default the submit button is disabled
// during submission.
beginSubmit: function() {},
endSubmit: function() {}
};The following properties and functions are available in all submission hooks when they are called. This does not include formToDoc, formToModifier, or docToForm.
this.addStickyValidationError(key, type, [value]): CallsAutoForm.addStickyValidationErrorfor the formthis.autoSaveChangedElement: The input element that was changed to cause this form submission (if the submission was due to autosave)this.collection: The collection attached to the form (fromcollectionattribute)this.currentDoc: The current document attached to the form (fromdocattribute)this.docId: The_idattribute of thedocattached to the form, if there is one, or for antype='insert'form, the_idof the newly inserted doc, if one has been inserted.this.event: The browser submit eventthis.formAttributes: The object containing all the form attributes from theautoFormorquickFormthis.formId: The form'sidattribute (useful in a global hook)this.insertDoc: The gathered current form values, as a normal objectthis.removeStickyValidationError(key): CallsAutoForm.removeStickyValidationErrorfor the formthis.resetForm(): Call this if you need to reset the formthis.ss: The SimpleSchema instance used for validating the formthis.ssIsOverride: This istrueifthis.ssis an override schema, meaning it's coming from aschemaattribute on theautoFormorquickForm, but there is also acollectionattribute pointing to a collection that has its own schema attached.this.template: TheautoFormtemplate instancethis.updateDoc: The gathered current form values, as a mongo modifier object suitable for passing to a collectionupdatecallthis.validationContext: The validation context used for the form. You can use this to check or add (non-sticky) invalid keys.
Notes:
- You can call
hooksoraddHooksmultiple times. The list of hooks is extended each time you call it, which means that multiple hooks of the same type can run for the same form. - Hooks will run in the order in which they are added, but all form-specific hooks run before all global hooks.
- The
beforehooks are called after the form is deemed valid but before the submission operation happens. (The submission operation depends on the form type.) These hooks are passed the document or modifier as gathered from the form fields. If necessary they can modify the document or modifier. These functions can perform asynchronous tasks if necessary. If asynchronicity is not needed, simply return the document or modifier, or returnfalseto cancel submission. If you don't return anything, then you must callthis.result()eventually and pass it either the document or modifier, orfalseto cancel submission. This is run only on the client. Therefore, you should not assume that this will always run since a devious user could skip it. - The
afterhooks are similar to the callbacks forinsertandupdateand method calls. They are passed two arguments:errorandresult - Refer to the next sections for details about the
formToDoc,formToModifier, anddocToFormhooks.
Specify formToDoc/formToModifier and docToForm hooks if you need form values in a different format in your form versus in the mongo document. They are mainly useful if you decide to override an input type.
Unlike document modifications made in "before hooks", modifications made in the
formToDoc hooks are made every time the form is validated, which could
happen a couple times per second on the client, depending on validation mode.
The other hooks are run only right before the corresponding submission actions,
as their names imply.
Here is an example where this feature is used to allow comma-delimited entry in a text field but store (and validate) the values as an array:
First specify type: [String] in the schema. The schema should reflect what you
are actually storing.
Add a type attribute to your afFieldInput component:
{{> afFieldInput name="tags" type="text"}}Then in client code, add the hooks:
AutoForm.hooks({
postsForm: {
docToForm: function(doc) {
if (_.isArray(doc.tags)) {
doc.tags = doc.tags.join(", ");
}
return doc;
},
formToDoc: function(doc) {
if (typeof doc.tags === "string") {
doc.tags = doc.tags.split(",");
}
return doc;
}
}
});Note that the update and method-update forms call formToModifier instead of formToDoc, and forms without a type attribute call both formToModifier and formToDoc. formToModifier is passed a Mongo modifier instead of a document.
If you are using the quickForm, afQuickField, afFormGroup, afObjectField, or afArrayField components, you may find that you want to set an attribute on something generated within those templates. In most cases, you can do this by setting attribute defaults in the corresponding schema definition. Add an autoform property to the schema defininition, and set it to an object specifying attribute defaults. For example, to ensure a field is always rendered as a textarea, you could do this:
summary: {
type: String,
optional: true,
max: 2000,
autoform: {
rows: 10
}
}You can also set autoform.omit to true in the schema definition for a field to prevent it from ever being included in quickForms, afObjectFields, afArrayFields, etc. This is useful for autoValue properties such as createdAt that you know you will not want on a form.
You can set the type for the field in the autoform object in the schema, too.
You can (and in most cases, should) target your attributes to a particular component by nesting them under the component name:
summary: {
type: String,
optional: true,
max: 2000,
autoform: {
afFieldInput: {
type: "textarea",
rows: 10,
class: "foo"
}
}
}Tip: Any attribute can instead be provided as a function that returns the attribute's value.
You can pass data structures using the data property. They will not be used as attributes, instead they will
be available in the field's context. For example:
summary: {
type: String,
autoform: {
afFieldInput: {
data: {
someArray: ['apple', 'orange', 'banana'],
someObj: {
complex: {
data: 'structure'
}
}
}
}
}
}You can use mongo dot notation to map an input to a subdocument. For example:
- If you use
{{> afFieldInput 'address.street'}}, whatever is entered in the field will be assigned todoc.address.street. - If you use
{{> afFieldInput 'addresses.1.street'}}, whatever is entered in the field will be assigned to thestreetproperty of the object at index 1 in thedoc.addressesarray.
By default, a schema field with type: Date will generate an input element with
type=date. You can override the type on each afFieldInput or afQuickField
helper if you want to use a datetime or datetime-local input instead. Here
are some tips for using each input type.
Consider using the moment and moment-timezone libraries to make this easy.
- Saving: The user-entered value must be of the format
YYYY-MM-DD, and it will be saved as aDateobject representing that exact datetime in the UTC time zone. (Chrome and some mobile browsers provide date pickers that set the input value to a string in this format automatically, but users of other browsers will have to manually enter the date in the correct format.) - Loading: If you are binding an object containing
Dateobjects to an update autoform and using them in an input withtype="date", the date that will be used as the value of the input is the date represented by theDateobject in the UTC time zone, with the time portion ignored. - Displaying: To make sure that the date format you use matches what the user
expects, you should construct your display strings based on the
Dateobject in the UTC time zone. Using themomentlibrary, you might do something like this:moment.utc(myDate).format("LL") - Min/Max: When specifying min or max values in your schema, use a
Dateobject that represents midnight on the morning of the minimum or maximum date in the UTC time zone.
Note: Using this type of input requires that the user do all the work to convert from the applicable time zone to the UTC time zone since the entered time is assumed to be UTC. It's generally better to use datetime-local.
- Saving: The user-entered value must be of the format
date string + "T" + time string + "Z", and it will be saved as aDateobject representing that exact datetime in the UTC time zone. - Loading: If you are binding an object containing
Dateobjects to an update autoform and using them in an input withtype="datetime", the date that will be used as the value of the input is the date and time represented by theDateobject in the UTC time zone. - Displaying: To make sure that the date format you use matches what the user
expects, you should construct your display strings based on the
Dateobject in the UTC time zone, or indicate on screen which time zone you are displaying. Using themomentlibrary, you might do something like this:moment.utc(myDate).format("LLLL") - Min/Max: When specifying min or max values in your schema, use a
Dateobject that represents the exact minimum or maximum date and time in the UTC time zone.
- Saving: If you use an input with
type="datetime-local", the datetime string that is entered will be assumed to be in the client's local timezone. To use a different specific timezone, add amoment-timezonepackage to your app and specify atimezoneIdattribute on theafFieldInputorafFormGrouphelper. Set this attribute to a timezone ID that moment-timezone understands, such as "America/Los_Angeles". For example, if you use an input withtype="datetime-local"in a form in which a user is setting up a meeting, you would need to previously determine the time zone in which the meeting will take place. When generating the autoform field, set thetimezoneIdattribute to the ID for this time zone. (Chrome and some mobile browsers provide datetime pickers that set the input value to a string in the expected format automatically, but users of other browsers will have to manually enter the datetime in the correct format, which isdate string + "T" + time string.) - Loading: If you are binding an object containing
Dateobjects to an update autoform and using them in an input withtype="datetime-local", be sure to set thetimezoneIdattribute on the component to the time zone ID that applies. This will ensure that the date and time you expect are shown in the input element. - Displaying: Before displaying the saved date, determine the equivalent
date and time in the corresponding time zone. The easiest way to do this is
using
moment(myDate).tz(timezoneId).format()from themomentandmoment-timezonepackages/libraries. - Min/Max: When specifying min or max values in your schema, use a
Dateobject that represents the exact minimum or maximum date and time in the corresponding time zone. This may mean returning the min or max value from a function based on a time zone ID you are storing elsewhere.
AutoForm has a robust and extendable template system. The following templates are built in:
bootstrap3: Default. UI elements will be generated using Bootstrap 3 structure and classes.bootstrap3-horizontal: Can be used withafFormGrouporafQuickFieldorquickFormonly. Generates markup and classes necessary to make the form appear with labels aligned horizontally with the fields. In additional to settingtemplate="bootstrap3-horizontal"on yourafQuickField, you must also define the column classes to use, for example,{{> afQuickField name="name" template="bootstrap3-horizontal" label-class="col-sm-3" input-col-class="col-sm-9"}}or{{> quickForm schema=Schemas.ContactForm id="contactForm" type="method" meteormethod="sendEmail" template="bootstrap3-horizontal" label-class="col-sm-3" input-col-class="col-sm-9" id-prefix="my-prefix"}}.plain: UI elements will be generated with no particular UI framework in mind. (You can of course add your own classes to customize.)plain-fieldset: Can be used withquickFormonly. Wraps the form in afieldset. Additionally allows alabelattribute that determines what the fieldset label should be.
The AutoForm components can be generated using a specific template by
providing a template name as the template option. In addition, you
can change the default template for all components or for a particular
component type at any time:
AutoForm.setDefaultTemplate('plain');
//OR
AutoForm.setDefaultTemplateForType('quickForm', 'plain-fieldset');These methods are reactive, meaning that as soon as you call them, you'll instantly see all visible forms change, if they're using the defaults.
Here's the list of possible types you can use for the first argument of
setDefaultTemplateForType:
- quickForm
- afFormGroup
- afObjectField
- afArrayField
- Any custom or built-in input type template name
To define a custom template that is recognized by the AutoForm
templates system, simply create a template with the name
afType + "_" + templateName. For example, if I want to define a template
named "nothing" that can be used by a quickForm to generate nothing:
<template name="quickForm_nothing">
</template>And then use it like this:
{{> quickForm schema=mySchema id="nothingForm" template="nothing"}}Or tell all quickForms to use it:
AutoForm.setDefaultTemplateForType('quickForm', 'nothing');Obviously a real example would be a bit more complex. Your template will have access to certain built-in helpers and properties in the data context, and you can use those to generate what you need in the format you need it. In practice, it is easiest to start by duplicating one of the built-in templates and then modify your copy as necessary.
If you create a good set of templates for a commonly used framework or a common purpose, consider releasing it as a separate add-on package. The goal is to keep the built-in templates minimal but to provide many others through separate packages.
The "plain", "bootstrap", and "bootstrap-horizontal" quickForm templates allow you to group fields into fieldsets in the schema if you want.
For example if you add this to several of the fields in your form schema:
{
autoform: {
group: 'Contact Information'
}
}Then all of those fields will be grouped into a fieldset with a legend that says "Contact Information". The fieldsets appear below any fields that do no list a group.
This only affects quickForms.
The fieldset has class "af-fieldGroup" and the legend has class "af-fieldGroup-heading" to help with styling.
The Telescope app makes use of this feature. Thanks to @SachaG for contributing it.
Every time AutoForm revalidates your form, it overwrites the list of invalid fields for that form. This means that adding your own errors to the form validation context (using the SimpleSchema API) will not always work because your custom errors will disappear upon first revalidation. To solve this, you can add sticky errors for a form. Sticky errors do not go away unless you reset the form, the form instance is destroyed, or you manually remove them.
AutoForm.addStickyValidationError(formId, key, type, [value])- `AutoForm.removeStickyValidationError(formId, key)
Making a custom input type (form widget) is easy.
- Create a template and any necessary helpers for it.
- Call
AutoForm.addInputTypeto give your new type a name and provide a few other necessary details.
AutoForm.addInputType accepts two arguments: name and options. The name argument defines the string that will need to be used as the value of the type attribute for afFieldInput. The options argument is an object with some of the following properties:
template: Required. The name of the template to use, which you've defined in a.htmlfile.valueIn: Optional. A function that adjusts the initial value of the field, which is then available in your template asthis.value. You could use this, for example, to change aDateobject to a string representing the date. You could also use a helper in your template to achieve the same result.valueOut: Required. A function that AutoForm calls when it wants to know what the current value stored in your widget is. In this function,thisis the jQuery object representing the element that has thedata-schema-keyattribute in your custom template. So, for example, in a simple case yourvalueOutfunction might just doreturn this.val().valueConverters: Optional. An object that defines converters for one or more schema types. Generally you will usevalueOutto return a value for the most common or logical schema type, and then define one or more converter functions here. The converters receive thevalueOutvalue as an argument and should then return either the same value or a type converted/adjusted variation of it. The possible converter keys are: "string", "stringArray", "number", "numberArray", "boolean", "booleanArray", "date", and "dateArray". Refer to the built-in type definitions for examples.contextAdjust: Optional. A function that adjusts the context object that your custom template receives. That is, this function accepts an object argument, potentially modifies it, and then returns it. That returned object then becomesthisin your custom template. If you need access to attributes of the parent autoForm in this function, useAutoForm.getCurrentDataForForm()to get them.
It's possible to use template helpers instead of valueIn and contextAdjust, but by keeping template helpers to a minimum, you make it easier for someone to override the theme template and still use your custom input type. For example, the bootstrap3 template overrides some of the default input types to add classes and adjust markup a bit, but it does not need to redefine template helpers to make context adjustments since valueIn and contextAdjust do that.
There is nothing overly special about the HTML template you define. Check out the properties of this within the template to get all of the information you need to render your control. Primarily you need to use this.value to set the control's value and the provided attributes in this.atts should be passed along to one or more of the elements you generate. In particular, you must make sure that the data-schema-key attribute in this.atts is added to one of the generated elements, the one that you want to be provided as this in your valueOut function.
For more examples, see the built-in input types here.
It depends. If you use quotation marks, then you are telling the autoform to
"look for an object in the window scope with this name". So if you define
your collections at the top level of your client files and without the var
keyword, then you can use this trick to avoid writing helpers.
If you don't use quotation marks, then you must define a helper function with that name and have it return the SimpleSchema or Mongo.Collection instance.
Probably the best technique for organizing your form schemas and making them
available as helpers is to add all SimpleSchema instances to a Schemas object
and register that object as a helper:
common.js:
Schemas = {};
Schemas.ContactForm = new SimpleSchema({
name: {
type: String,
label: "Your name",
max: 50
},
email: {
type: String,
regEx: SimpleSchema.RegEx.Email,
label: "E-mail address"
},
message: {
type: String,
label: "Message",
max: 1000
}
});
//... define all schemasclient.js:
Template.registerHelper("Schemas", Schemas);html:
<template name="contactForm">
{{#autoForm schema=Schemas.ContactForm id="contactForm" type="method" meteormethod="sendEmail"}}
<!-- etc. -->
{{/autoForm}}
</template>Generally you should start by using a quickForm for every form your app needs.
Then, if the generated form does not look correct, try the following in
this order:
- If there is something you can change or fix about the form's schema that
will cause the
quickFormto render correctly, change it. If the form is for a collection, you can also try using a different schema (a subset, perhaps with stricter rules) to render the form by supplying theschemaattribute in addition to thecollection. - If many of your forms need the same change, try writing and using a custom template for those forms if possible.
- Switch to using an
autoFormwithafQuickFields. You can then set attributes for both inputs and labels (usinglabel-prefix), omit labels if necessary (label=false), and specify field-specific template overrides. These features are typically enough to get the appearance you want. - If you really need something truly custom for just one field in just one
form, you can then switch to using the
afFieldInputcomponent directly.
Yes. Your code that flips between states should do the following in this order:
- Change the
typeattribute's value to "insert" or "update" as appropriate, probably by updating a reactive variable. - Change the
docattribute's value to the correct document for an update or tonull(or a document containing default values) for an insert, probably by updating a reactive variable. - Call
AutoForm.resetForm(formId). This will clear any existing validation errors for the form.
Beginning with AutoForm 5.0, a data-required attribute is now present on the form group div element for all built-in afFormGroup templates if the field is required. This allows you to use css like the following to display an asterisk after required fields: [data-required] label:after {content: '*'}
To specify options for any field, use the options attribute and provide an array of
objects, where each object has a label property and a value property. There are several
different ways you can do this.
Alternatively, you can specify options as an object with {value: label} format. Values are coerced into the expected type.
{
favoriteColor: {
type: String,
allowedValues: ['red', 'green', 'blue']
}
}{{> afQuickField name="favoriteColor" options="allowed"}}Fields generated by quickForm or afObjectField or afArrayField use allowedValues by default.
{
favoriteColor: {
type: String,
allowedValues: ['red', 'green', 'blue'],
autoform: {
options: [
{label: "Red", value: "red"},
{label: "Green", value: "green"},
{label: "Blue", value: "blue"}
]
}
}
}Alternative syntax:
{
favoriteColor: {
type: String,
allowedValues: ['red', 'green', 'blue'],
autoform: {
options: {
red: "Red",
green: "Green",
blue: "Blue"
}
}
}
}{{> afQuickField name="favoriteColor"}}Since autoform.options is in the schema, that will be used instead of allowedValues.
{
favoriteColor: {
type: String,
allowedValues: ['red', 'green', 'blue'],
autoform: {
options: function () {
return _.map(['red', 'green', 'blue'], function (c, i) {
return {label: "Color " + i + ": " + c, value: c};
});
}
}
}
}{{> afQuickField name="favoriteColor"}}autoform.options can be an array or a function that returns an array.
{
favoriteColor: {
type: String
}
}
Template.myFormTemplate.helpers({
colorOptions: function () {
return Colors.find().map(function (c) {
return {label: c.name, value: c._id};
});
}
});{{> afQuickField name="favoriteColor" options=colorOptions}}This example provides a reactive list of colors options read from the Colors collection. This assumes
that you've created and populated the Colors collection and published the necessary colors (documents)
to the client. Although using a UI helper is ideal because it is reactive, it can't be done with a quickForm
or afObjectField or afArrayField, unless you make a custom template.
Yes. Define your messages using one of the SimpleSchema methods, including
HTML elements such as <strong>. Then be sure to wrap your afFieldMessage
tags in triple stashes.
I would like to link to examples of public sites using this in production. If you have one, please add a link here. You can include a brief description of how you're using autoforms on the site, too. If the code is publicly available, link to that, too.
- While developing, be sure to call
AutoForm.debug()in your client code to enable extra logging. - If nothing happens when you click the submit button for your form and there are
no errors, make sure the button's type is
submit. - If your
beforehook is called but the form is never submitted, make sure you are returning thedocormodifierfrom the hook or eventually callingthis.result(doc)if you're doing something asynchronous.
Anyone is welcome to contribute. Fork, make your changes, and then submit a pull request.
Thanks to all those who have contributed code changes and all who have helped by submitting bug reports and feature ideas.
