logo Buffalo slack logo
Forms
Frontend

Forms

Buffalo uses the github.com/gobuffalo/tags package to make form building easier.

Plush includes two helpers from this package that produce Bootstrap v3 style forms. These helpers are form and form_for.

Both types of form helpers have the following features in common:

  • Automatically setting the CSRF authenticity token
  • Support for all HTTP methods (PUT, POST, DELETE, etc…)
  • Error Handling
  • Multipart form support
  • Customizable input types
  • Pass through HTML tag attributes

Basic Forms

The form helper can be used to generate HTML forms. Since this type of form isn’t attached to any particular “model” all information must be passed as options to the form and it’s methods.

// templates/talks/edit.html

<%= form({action: talkPath({id: 3}), method: "PUT"}) { %>
  <div class="row">
    <div class="col-md-12">
      <%= f.InputTag({name:"Title", value: talk.Title }) %>
    </div>

    <div class="col-md-6">
      <%= f.TextArea({value: talk.Abstract, hide_label: true }) %>
    </div>

    <div class="col-md-6">
      <%= f.SelectTag({name: "TalkFormatID", value: talk.TalkFormatID, options: talk_formats}) %>
      <%= f.SelectTag({name: "AudienceLevel", value: talk.AudienceLevel, options: audience_levels }) %>
    </div>

    <div class="col-md-12">
      <%= f.TextArea({name: "Description", value: talk.Description, rows: 10}) %>
    </div>
    <div class="col-md-12">
      <%= f.TextArea({notes:"Notes", value: talk.Notes, rows: 10 }) %>
    </div>

  </div>
<% } %>
// OUTPUT
<form action="/talks/3" method="POST">
  <input name="authenticity_token" type="hidden" value="e0c536b7a1a7d752066727b771f1e5d02220ceff5143f6c77b">
  <input name="_method" type="hidden" value="PUT">
  <div class="row">
    <div class="col-md-12">
      <div class="form-group">
        <input class=" form-control" name="Title" type="text" value="My Title">
      </div>
    </div>
    <div class="col-md-6">
      <div class="form-group">
        <textarea class=" form-control">some data here</textarea>
      </div>
    </div>

    <div class="col-md-6">
      <div class="form-group">
        <select class=" form-control" name="TalkFormatID">
          <option value="0" selected>Talk</option>
          <option value="1">Lightning Talk</option>
          <option value="2">Workshop</option>
          <option value="3">Other</option>
        </select>
      </div>
      <div class="form-group">
        <select class=" form-control" name="AudienceLevel">
          <option value="All" selected>All</option>
          <option value="Beginner">Beginner</option>
          <option value="Intermediate">Intermediate</option>
          <option value="Advanced">Advanced</option>
        </select>
      </div>
    </div>

    <div class="col-md-12">
      <div class="form-group">
        <textarea class=" form-control" name="Description" rows="10">some data here</textarea>
      </div>
    </div>

    <div class="col-md-12">
      <div class="form-group">
        <textarea class=" form-control" notes="Notes" rows="10">some data here</textarea>
      </div>
    </div>
  </div>
</form>

Model Forms

The form_for helper can be used to generate HTML forms for a specified model. This makes the code easier to write, and maintains a level of “consistency” across your application.

The form_for helper behaves in a similar matter to the form helper, with several key differences.

The first difference is that the form_for takes a “model” as a first argument. This “model” only needs to be a struct it does not have to be database backed.

The second difference is in the tag calls the models directly. These tags, such as InputTag, take the name of the attribute on the model you want to build a field for, then they take an optional set of options as the second argument.

// models/talk.go
type Talk struct {
  ID            int          `json:"id" db:"id"`
  CreatedAt     time.Time    `json:"created_at" db:"created_at"`
  UpdatedAt     time.Time    `json:"updated_at" db:"updated_at"`
  UserID        int          `json:"user_id" db:"user_id"`
  Title         string       `json:"title" db:"title"`
  Description   nulls.String `json:"description" db:"description"`
  Notes         nulls.String `json:"notes" db:"notes"`
  ParentID      nulls.Int    `json:"parent_id" db:"parent_id"`
  Abstract      string       `json:"abstract" db:"abstract"`
  AudienceLevel string       `json:"audience_level" db:"audience_level"`
  IsPublic      nulls.Bool   `json:"is_public" db:"is_public"`
  TalkFormatID  int          `json:"talk_format_id" db:"talk_format_id"`
}
<%= form_for( talk, {action: talkPath({id: 3}), method: "PUT"}) { %>
  <div class="row">
    <div class="col-md-12">
      <%= f.InputTag("Title") %>
    </div>
    <div class="col-md-6">
      <%= f.TextArea("Abstract", {hide_label: true}) %>
    </div>


    <div class="col-md-6">
      <%= f.SelectTag("TalkFormatID", {options: talk_formats}) %>
      <%= f.SelectTag("AudienceLevel", , {options: audience_levels}) %>
    </div>

    <div class="col-md-12">
      <%= f.TextArea("Description", {rows: 10}) %>
    </div>

    <div class="col-md-12">
      <%= f.TextArea("Notes", {rows: 10}) %>
    </div>
  </div>
<% } %>
// OUTPUT
<form action="/talks/3" id="talk-form" method="POST">
  <input name="authenticity_token" type="hidden" value="cd998be98a99b452481c43fd3e4715e4e85333a45b982ac999">
  <input name="_method" type="hidden" value="PUT">
  <div class="row">
    <div class="col-md-12">
      <div class="form-group">
        <label>Title</label>
        <input class="form-control" id="talk-Title" name="Title" type="text" value="My Title">
      </div>
    </div>
    <div class="col-md-6">
      <div class="form-group">
        <textarea class="form-control" id="talk-Abstract" name="Abstract">some data here</textarea>
      </div>
    </div>

    <div class="col-md-6">
      <div class="form-group">
      <label>TalkFormatID</label>
        <select class="form-control" id="talk-TalkFormatID" name="TalkFormatID">
          <option value="0" selected>Talk</option>
          <option value="1">Lightning Talk</option>
          <option value="2">Workshop</option>
          <option value="3">Other</option>
        </select>
      </div>
      <div class="form-group">
        <label>AudienceLevel</label>
        <select class=" form-control" id="talk-AudienceLevel" name="AudienceLevel">
          <option value="All" selected>All</option>
          <option value="Beginner">Beginner</option>
          <option value="Intermediate">Intermediate</option>
          <option value="Advanced">Advanced</option>
        </select>
      </div>
    </div>

    <div class="col-md-12">
      <div class="form-group">
        <label>Description</label>
        <textarea class=" form-control" id="talk-Description" name="Description" rows="10">some data here</textarea>
      </div>
    </div>

    <div class="col-md-12">
      <div class="form-group">
        <label>Notes</label>
        <textarea class=" form-control" id="talk-Notes" name="Notes" rows="10">some data here</textarea>
      </div>
    </div>
  </div>
</form>

Select Tags

To build your <select> tags inside forms Tags provide 3 convenient ways to add your <select> options: form.SelectOptions, map[string]interface{} or []string, all of them by passing an options field into the form.SelectTag options like:

<%= f.SelectTag("TalkFormatID", {options: talkFormats}) %>

or

<%= f.SelectTag("TalkFormatID", {options: ["one", "two"]}) %>

Which will use the same value for the value attribute and the body of the option, or:

<%= f.SelectTag("TalkFormatID", {options: {"one": 1, "two": 2}}) %>

Which allows us to define the options map inside the view.

Selectable Interface

Another alternative for the select options is to pass a list of structs that meet the form.Selectable interface.

Which consist of two functions:

//Selectable allows any struct to become an option in the select tag.
type Selectable interface {
  SelectValue() interface{}
  SelectLabel() string
}

By implementing this interface tags will call SelectValue and SelectLabel to get the option Value and Label from implementer.

Selected

Tags will add the selected attribute to the option that has the same value than the one it receives on the value option of the form.SelectTag, so you don’t have to look for the option that has equal value than the selected one manually, p.e:

<%= f.SelectTag("TalkFormatID", {options: {"one": 1, "two": 2}, value: 2}) %>

Produces:

<div class="form-group">
  <label>TalkFormatID</label>
  <select class="form-control" id="talk-TalkFormatID" name="TalkFormatID">
    <option value="1">one</option>
    <option value="2" selected>two</option>
  </select>
</div>

And similarly with the form.SelectOptions slice:

<%= f.SelectTag("TalkFormatID", {options: talkFormats, value: 2}) %>

Checkbox Tags

Tags provide a convenient way to build an HTML <input> element with type="checkbox":

<%= f.CheckboxTag("IsPublic") %>

That produces:

<div class="form-group">
  <label>
    <input class="" id="talk-IsPublic" name="IsPublic" type="checkbox" value="true" checked="">
    IsPublic
  </label>
</div>

You can easily change the label content with

<%= f.CheckboxTag("IsPublic", {label: "Is the talk public?"}) %>

That produces:

<div class="form-group">
  <label>
    <input class="" id="post-IsPublic" name="IsPublic" type="checkbox" value="true" checked="">
     Is the Talk public?
  </label>
</div>

Non-Checked Checkbox Values

By default when a checkbox is not “checked” no value will be sent to the server. Often, it is useful to send a value indicating a non-checked checkbox. This can be set by passing in a unchecked value.

<%= f.CheckboxTag("IsPublic", {unchecked: false}) %>
<div class="form-group">
  <label>
    <input id="widget-IsPublic" name="IsPublic" type="checkbox" value="true">
    <input name="IsPublic" type="hidden" value="false"> IsPublic
  </label>
</div>

When the form is submitted the hidden tag will be posted and the server will see the false value.

Error Handling

Both form and form_for helpers have support for handling errors from the github.com/gobuffalo/validate package.

In an action simply set a value of type *validate.Errors on the context as errors and the form helpers will pick it up and add error messages to the appropriate form tags.

// actions/widgets.go
func (v WidgetsResource) Create(c buffalo.Context) error {
  tx := c.Value("tx").(*pop.Connection)
  widget := &models.Widget{}
  if err := c.Bind(widget); err != nil {
    return err
  }
  // Validate the data from the html form
  verrs, err := tx.ValidateAndCreate(widget)
  if err != nil {
    return errors.WithStack(err)
  }
  if verrs.HasAny() {
    c.Set("widget", widget)
    // Make the errors available inside the html template
    c.Set("errors", verrs)
    return c.Render(422, r.HTML("widgets/new.html"))
  }
  c.Flash().Add("success", "Widget was created successfully")
  return c.Redirect(302, "/widgets/%s", widget.ID)
}
// templates/widgets/new.html
<%= form_for(widget, {action: widgetsPath(), method: "POST"}) { %>
  <%= f.InputTag("Name") %>
  <button class="btn btn-success" role="submit">Save</button>
  <a href="<%= widgetsPath() %>" class="btn btn-warning" data-confirm="Are you sure?">Cancel</a>
<% } %>
// OUTPUT
<form action="/widgets" id="widget-form" method="POST">
  <input name="authenticity_token" type="hidden" value="AI0pb5YFBw2xU/EfcS6FaEOwTLWaGv58Y+w0ArfJoknfqu7l/j6tRLWybbcm+YZqXbBmi7f80l3Sf0WfnR7COA==">
  <div class="form-group has-error">
    <label>Widget</label>
    <input class=" form-control" id="widget-Widget" name="Widget" type="text" value="">
    <span class="help-block">Widget can not be blank.</span>
  </div>
  <button class="btn btn-success" role="submit">Save</button>
  <a href="/widgets" class="btn btn-warning" data-confirm="Are you sure?">Cancel</a>
</form>

Using Non-Bootstrap Form Helpers

The default form helpers, form and form_for, generate forms that are compatible with Bootstrap 3. If this is not for you, you can easily use the non-Bootstrap versions of these helpers.

Requires Plush version v3.6.8 or greater

// actions/render.go
func init() {
  r = render.New(render.Options{
    // ...
    // Add template helpers here:
    Helpers: render.Helpers{
      "form":     plush.FormHelper,
      "form_for": plush.FormForHelper,
    },
    // ...
  })
}
// templates/widgets/new.html
<%= form_for(widget, {action: widgetsPath(), method: "POST"}) { %>
  <%= f.InputTag("Name") %>
  <%= f.InputTag("Body") %>
  <button class="btn btn-success" role="submit">Save</button>
  <a href="<%= widgetsPath() %>" class="btn btn-warning" data-confirm="Are you sure?">Cancel</a>
<% } %>
// OUTPUT
<form action="/widgets" id="widget-form" method="POST">
  <input name="authenticity_token" type="hidden" value="jN3nYOhCTqxZvmYnO9v1maso2VMs8fslj3rmKg1TS281W6JKpMd6Uezqp1dd3VBu2su41nKRBkd5AWDyCM4BzQ==">
  <input id="widget-Name" name="Name" type="text" value="">
  <input id="widget-Body" name="Body" type="text" value="">
  <button class="btn btn-success" role="submit">Save</button>
  <a href="/widgets" class="btn btn-warning" data-confirm="Are you sure?">Cancel</a>
</form>

FAQs

How Do I Map a Form to a Model/Struct?

See the Request Binding page for more information on request binding.

Can I Change the Name of the f Variable in My Template?

By default the form value inside the block is given the name f, however this can be changed when creating the form and passing the var option.

<%= form({var: "xyz"}) { %>
  <%= xyz.InputTag({name: "Foo"}) %>
<% } %>

How Do I Create a Multipart Form?

<%= form({multipart: true}) { %>
<% } %>
<form enctype="multipart/form-data" method="POST">
</form>

Can I Just Use My Own Form (Without the Use of the Form Helper)?

Yes! You most definitely can create and use your own form! The forms provided from having Buffalo generate your resources are simply a placeholder to get you up and running quickly! It is important to note, however, that asking Buffalo to generate your resources, using the supplied generators, will also generate the resource’s CRUD related routes. This is important to note since the route associated with the UPDATE action makes use of the PUT method and is not a valid value for an HTML form method according to the HTML Standard. That being said, you need to ensure that you structure your form (for editing a resource) to use the POST method to tunnel the HTTP method, while using a hidden input to indicate your intention to make use of the PUT method server side. An example of this would look like the follow:

<form method="POST" ...>
  <input type="hidden" name="_method" value="PUT" />
...

Can I use CSRF token if I disable SSL?

The CSRF token is a secret value that is handled securely to remain valid during cookie-based sessions.

In development environment you can run smoothly under http.

If you disable SSL (https) and post a form in production environment, you will get the message CSRF token invalid.

More details: PR #1851

How Do I Handle CSRF Tokens If I Use My Own Form?

If you do decide to use your own forms you are going to need a way to provide the form with the authenticity token. There are two ways to solve this issue.

The first way is to use the authenticity_token directly in form, since it is already in the context.

<form method="POST" ...>
  <input name="authenticity_token" type="hidden" value="<%= authenticity_token %>">
</form>

Another way is to write a helper to generate that line of code for you.

"csrf": func(ctx plush.HelperContext) (template.HTML, error) {
  tok, ok := ctx.Value("authenticity_token").(string)
  if !ok {
    return "", fmt.Errorf("expected CSRF token got %T", ctx.Value("authenticity_token"))
  }
  t := tags.New("input", tags.Options{
    "value": tok,
    "type":  "hidden",
    "name":  "authenticity_token",
  })
  return t.HTML(), nil
},

Now that you have defined a helper to use in your templates you can use your helper inside your form with <%= csrf() %>. So your custom form should end up looking like this:

<form method="POST" ...>
  <%= csrf() %>
</form>