Autoform Tutorial

Autoform for Meteor Blasting our way to easy forms

While typing out a form is relatively easy, it can become difficult and messy once you consider validation, displaying errors and wiring it up to the back end. It would be nice if we could auto generate a form for a given data structure, and luckily we can, thanks to Autoform.

In my other tutorials, I've been working on an app called Foosboom, but for this tutorial I'm going to just create a new project because I feel it doesn't really fit into Foosboom - or at least Foosboom won't make full use of the features available in this package.

Let's create a new project and add some starting packages:

meteor create autoform-tutorial
cd autoform-tutorial
meteor add mizzao:bootstrap-3
meteor add msavin:mongol
meteor add aldeed:autoform

"mizzao:bootstrap-3" gives us Twitter's bootstrap styling and msavin:mongol gives us an easy way to see what's in the database.

Autoform depends on a package called simple-schema, which allows you to create schemas for your forms. However there is another package called collection2, which builds on top of simple-schema and allows you to attach your schema directly on your collections. This seems like a better option and appears to be what the package author prefers. So let's install collection2 as well:

meteor add aldeed:collection2

Simple Form

Insert

With our packages in place we can now start building. Firstly, remove the 3 files that are automatically generated, and add the following 2 files (and obviously the folders they are in):

both/collections/posts.js
client/main.html
Our project's starting point

For this example I'm going to use a collection called Posts, and to begin with it's going to be quite simple. After looking at how to create schemas I came up with the following very simple schema:

If you've ever worked with MySQL, you'll know what a schema is. It basically is a way of describing what your data is... so it's data about data (metadata). So here I've specified that our data "table" has two columns: "title" and "content". Mongo, being noSQL, wouldn't call it a table or column, but rather "document" and "field" (see a comparison of MySQL vs MongoDB terminology here).

Our "title" is of the type String. You can choose from the standard Javascript types like String, Number, Boolean, Object, or use any constructor you like (such as Date or something you've created). It has the label "Title", which Autoform puts as the label of the input field. Finally, we don't want it to have any more than 200 characters.

We have more schema options for when it comes to describing our data (such as defaultData for specifying our ... well ... default data, and optional for letting the form know this field isn't required), but we will keep it simple for now.

Now apparently, Autoform should be able to utilize this schema information and create a form for us. Let's put it to the test.

Autoform provides you with two options for your templates: {{>quickform}} and {{#autoform}}{{/autoform}}. {{>quickform }} is when you want the Autoform package to just look at your schema and convert it straight into a form. The greater-than symbol (>) shows that it's a template - so we are rendering another template created by Autoform and passing some variables to it.

If we want to customize our form in any way, we can use the second option, {{#autoform }} ... {{/autoform}}, which gives you more control over things (you build the form manually).

Since our schema is stupidly simple, we'll use {{>quickform }} to start with. Edit your main.html file to look like this:

And voila, a form!

Now let's test it out. Hit control-m to bring up Mongol. Now add some posts and watch them appear in Mongol. Note that if you make the title more than 200 characters, it automatically truncates it to 200 characters in length because in our schema we set max: 200.

We can see our posts appearing in Mongol

Update

With insert working fine, it's time to try out updating our posts. Since our app is becoming a little more complex, we'll add some routes - we'll use iron router for this:

meteor add iron:router
And add the following files ("post.html" is the one we care about - it's where we put our update form):

Sorry for the code dump. Now we have a small app running. On the home page we can add posts and see them appear below:

Our shiny new home route

And if we click on a post in the list, it'll take us to an edit page ('post.html').

In router.js, on line 9 above, you can see we pass the post as data to the route, and hence it becomes this inside the template, explaining why we go doc=this in our quickForm code (line 5 on "post.html").

Autoform

Okay let's say we want our content field to be a textarea. For this we need to open up some more functionality by using the {{#autoForm}} helper. Here's how I did it:

We now have a textarea

As you can see, how form now takes a bit more code, but we are able to customise it a bit more. I was able to add the parameter rows=6 to the afQuickField template, which obviously told it to render a textarea instead of a normal input field.

Let's do the same thing to the update form:

Our edit form upgraded

Adding complexity

Select list

So far Autoform has been pretty cool. But apps in the real world are never this straight forward ...

Let's change the schema to add a new attribute: category. And in the form we want to be able to select our category from a list of them.

Now how will we render a list of potential categories in a select element? Turns out Autoform has us covered: we can specify options in the afQuickField helper like this (don't copy this - I'm just showing an example):

{{> afQuickField name="category" options=categories}}
Template.posts.helpers({
  categories: function () {
    return [
      {label: "Business", value: "business"},
      {label: "Health", value: "health"},
      {label: "Finance", value: "finance"}
    ];
  }
});

We are also able to specify the potential categories in the schema itself, which is what we'll do (as well as adding a new afQuickField in our template html):

Our select element works nicely

User accounts

Okay that was a little too easy. What happens when we throw user accounts in the mix?

meteor add accounts-password
meteor add ian:accounts-ui-bootstrap-3
meteor remove insecure
Now we can sign in

Well, I've signed in and added my allow() rules (and removed the insecure package, which was allowing us to do anything with the database from the client). Post's now require a userId, and hence any attempt to create a post is failing. I'll need to get the form to post the current users ID somehow.

After poring over the docs I managed to find this:

"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"

So we need to create a "before" hook for our form. This basically allows us to modify the data just before it gets whisked away up to the server. Here is how I did it:

Now we can create posts again. But we can't edit the previous posts, as they don't have a userId set in them. You can delete these old ones using Mongol (ctrl-m to activate).

Converting data before it goes into the form

Another use case and challenger for Autoform is converting a data type before it goes into a form. For example, if we store our tags in an array, and want to edit them as a comma separated list in a string.

So say our tags in the database looked like this:

post: {
  title: "hi",
  content: "stuff",
  tags: ["yolo", "living life to full", "IAMHONEYBADGER"]
}

And in our form we want it to be a text field like this:

"yolo, living life to full, IAMHONEYBADGER"

We can do this in Autoform with formToDoc, formToModifier, and docToForm. Here's what I came up with:

Our tags are represented as a string in our form, but are really an array

While this works for once off cases, if we want to reproduce the comma delimited behaviour in other collections it would be better to create a "custom input type". When we add our inputs ({{> afQuickField name='tags' type='text'}}) you'll notice we supply a type parameter. This tells Autoform to use a specific type of input, which comes with it's own template and logic. The author of the package (Eric Dobbertin) has created a number of custom input types that come with Autoform, which you see here.

So we could create a custom input type called "commaDelimited" and then reference it in our template like this: {{> afQuickField name='tags' type='commaDelimited'}}. Unfortunately this is beyond the scope of this tutorial, but given the amount of examples provided by Eric, people should be able to work it out.

I'm going to end it here - as this should be enough to get people started. You can checkout this example page if you want to see the full range of capabilities of Autoform. Also Sabrina Gelbart from the comments has nicely provided the code for this tuturial (something I probably should have done - oops) - just note that the main collection is called "Items" instead of "Posts".

There are also a number of plugins that extend Autoform to make it work with third party libraries like select2, summernote, and ionic. So don't go hacking until you check what's already been done.

The end.

comments powered by Disqus

Prefer video?

I've just started a video site! I hope to make it similar to railscasts.com, but for Meteor.

Nebula

Prefer book format?

I wrote a book that incorporates and expands on these tutorials. I also use Javascript instead of Coffeescript. Plus it's free to read online!

Meteor ebook