Creating Form Widgets

There are plenty of forms that are commonly used on websites, forms for logging in, subscribing to a newsletter, requesting help, etc. Scrivito lets you implement forms as widgets, giving editors the freedom to place them wherever they are needed and to configure them for their purpose.

In this tutorial, we'll step through the process of creating a simple contact form widget. The information submitted via the form needs to be processed on the server, so we'll need a controller. For submitting the form, AJAX will be used. This allows the visitor to stay on the currently viewed page since the form processing results are displayed in the context of the widget.

If you want to render forms for editing CMS content (in place on a page or widget, in the Content Browser, or in properties views), use the scrivito_tag helper instead of Rails' form helper to have the input data saved to the CMS. 

Implementing the form widget

First, let's implement the widget itself. Add the file “app/models/contact_widget.rb” with the following content to your Scrivito Rails application:

Copy
class ContactWidget < Widget
  attribute :title, :string, default: 'Contact Us'
  attribute :confirmation_text, :html, default: 'Thank you for contacting us. We\'ll get in touch with you as soon as possible. Stay tuned!'
end

This is just a plain Scrivito widget with two attributes, title and confirmation_text. We'll render the title at the top of the form (like a headline communicating the purpose of the form), and the confirmation text after the form has been successfully submitted.

The form itself is not represented in the widget model because form data is user-generated content. We'll create a separate model, controller and view for the form in a minute.

Let's first implement the details dialog of the widget, “app/views/contact_widget/details.html.erb”, so that editors can provide the confirmation text:

Copy
<%= scrivito_medium_dialog do %>
  <%= scrivito_details_for 'Text displayed after the user submitted the form' do %>
    <%= scrivito_tag :div, widget, :confirmation_text %>
  <% end %>
<% end %>
The details dialog of the form widget should now look like this:

Creating a form model class and view

Rails' form helpers allow you to bind a form to a model, making it possible to validate and store the data entered into a form. So let's create the form model class in the “app/models/contact_form.rb” file:

Copy
class ContactForm
  include ActiveModel::Model

  attr_accessor :obj_id, :widget_id

  attr_accessor :email
  validates :email, presence: true

  attr_accessor :message
  validates :message, presence: true
end

This model defines four attributes:

  • obj_id: CMS object ID – the ID of the page containing the form widget
  • widget_id: ID of the form widget
  • email: Email address provided by the visitor
  • message: The message provided by the visitor

The object and widget IDs will be used internally to access the widget, while the email address and message are to be provided by the visitor. The user data attributes are validated to ensure that both form fields have been filled in.

To render the widget including a form based on the above model, provide the following “show” view, “app/views/contact_widget/show.html.erb”:

Copy
<%= scrivito_tag :h2, widget, :title %>

<%= render 'contact_forms/show', form: ContactForm.new(widget_id: widget.id, obj_id: @obj.id) %>

This template first renders the title using the scrivito_tag helper which enables editors to change it in place. Then the (not yet existing) “contact_forms/show” partial is rendered to display the form. To the partial a new ContactForm object is passed for validating and storing the user data. Note, that the  CMS object and widget IDs are provided by Scrivito.

Now, here is the partial, “app/views/contact_forms/_show.html.erb”:

Copy
<%= form_for form, url: contact_form_path, remote: true, html: { class: 'contact-form' } do |f| %>
  <div class="form-group">
    <%= f.label :email %>
    <% if form.errors[:email].present? %>
      <span class="help-block"><%= form.errors[:email].join(', ') %></span>
    <% end %>
    <%= f.text_field :email, class: 'form-control' %>
  </div>

  <div class="form-group">
    <%= f.label :message %>
    <% if form.errors[:message].present? %>
      <span class="help-block"><%= form.errors[:message].join(', ') %></span>
    <% end %>
    <%= f.text_area :message, class: 'form-control' %>
  </div>

  <%= f.hidden_field :obj_id %>
  <%= f.hidden_field :widget_id %>

  <%= f.submit 'Submit', class: 'btn btn-default' %>
<% end %>

That's quite a bit of code, so let's step through it. In the first line, the form_for helper call serves to define the contact form using the form variable that contains the ContactForm object passed to the partial. The url points to a controller we have not yet created, and html adds a CSS class to the form itself. 

In addition, we use the remote option that causes the form to be submitted in the background via AJAX, meaning that the user stays on the currently viewed page. If you haven't used this option before, take a look at the excellent Rails guide for an overview.

The next lines generate the form fields for the email and message the user should enter. Along with the form fields, any errors that occurred during validation are displayed.

Copy
  <%= f.hidden_field :obj_id %>
  <%= f.hidden_field :widget_id %>

These two lines add hidden fields for the CMS object and widget IDs to the form. They are needed to access the widget later on.

Finally, a submit button is rendered.

Handling submitted input

After submitting the form, its contents should be validated. If the validation fails, the errors that occurred should be displayed. If it succeeds, the request should be forwarded to someone able to respond to it. To achieve this, we require a new controller. So, let's add the file “app/controllers/contact_forms_controller.rb” to the application and provide it with the following contents.

Copy
class ContactFormsController < ApplicationController
  def create
    @form = ContactForm.new(form_params)

    if @form.valid?
      @widget = Obj.find(@form.obj_id).widgets[@form.widget_id]
      process_contact_form
      render 'confirmation', layout: false
    else
      render 'show', layout: false, status: :bad_request
    end
  end

  private

  def form_params
    params.require(:contact_form).permit(:obj_id, :widget_id, :email, :message)
  end

  def process_contact_form
    Rails.logger.info("Contact Form Submission from #{@form.email}: #{@form.message}")
  end
end

The process_contact_form method is where you can react to the incoming form data, send it to a user as an email message, forward it to your CRM system, or do whatever your needs require. For simplicity's sake, we're just writing a log entry.

The form_params method uses strong parameters to ensure that only the expected data is passed to the ContactForm.

The create method should seem familiar to you. It first creates a ContactForm using the form_params method, validates it and, depending on the outcome of the validation, either processes the form and renders a confirmation text, or rerenders the form with the errors included.

Please note, our calls to render include the layout: false flag. This instructs Rails not to render the application layout but just the template. 

We also require a route for our controller to make it accessible:

Copy
  resource :contact_form, only: [:create]

We still need the views that render the form and, after it has been submitted, the confirmation message. For the form, we can reuse the partial we previously defined for rendering the form data plus any validation errors. Put the call to the partial into “app/views/contact_forms/show.html.erb”:

Copy
<%= render 'show', form: @form %>

To render the form widget's confirmation text to be shown to the user after successfully submitting the form, place a call to the scrivito_tag helper into the app/views/contact_forms/confirmation.html.erb file.

Copy
<%= scrivito_tag :div, @widget, :confirmation_text %>

Wiring it up

If you now submit the form, you should see the request arriving at your server, however, the view will not be changed. To include the responses returned by the server, we'll add a JavaScript snippet to the application.js file. Alternatively, you can put it in its own file.

Copy
$(function() {
  $(document).on('ajax:success', '.contact-form', function(event, html) {
    $(this).replaceWith(html);
  });

  $(document).on('ajax:error', '.contact-form', function(event, xhr) {
    $(this).replaceWith(xhr.responseText);
  });
});

This code sets up two handlers that listen to the ajax:success and ajax:error events that Rails' JS integration triggers after the form has been submitted, either successfully or causing a validation error. For further information on these events, there is a compact overview of “data-remote” custom events at GitHub.

Both handlers replace the form with the response delivered by the server. In the case of successful submission, the confirmation text is rendered, while failure to validate the form data causes the form plus the validation error messages to be displayed.

Note that we are using event delegation here. This allows us to register just a single event for each of the two cases. They're able to handle all of the contact forms on a page, even the ones that are dynamically added to the page later on.

That's all the code required to set up a basic contact form!

What's next?

If you provide your application with such a form, note that you can add further configuration options to the widget. For example, if the incoming form data should be sent to an email address, you could make this address configurable in the widget's details view.