Creating a Page Type with Widgets

The idea behind widgets

Websites are usually composed of much more than just a simple headline and a body text. Maybe you wish to display the latest news, a contact form, images, a gallery or a video. Also, the editors should not be forced to handcraft the required dynamic functionality or even use a programming language to have complex content structures displayed on the website.

Adding functionality or pieces of information to a page, moving these pieces from one column to another, for example, should be as easy as possible. To achieve this, Scrivito allows the developer to define and customize ready-to-use widgets  that can be inserted into pages, customized, filled with content, duplicated, moved around, and more.

How to create a page type prepared for widgets

Every web page is represented by a CMS object. For a web page to contain widgets, the CMS object that represents it needs to be equipped with a field of the widgetlist type, similar to a field of the string or date type. Since every CMS object is based on an object class, we require an object class for our page that includes such a widgetlist type field.

The Scrivito SDK comes with a page generator that creates the model and the controller for the page, plus the index view as well as the page properties and thumbnail views for in-place editing:

Copy
$ rails g scrivito:page MyPage
      create  app/models/my_page.rb
      create  app/controllers/my_page_controller.rb
      create  app/views/my_page/index.html.erb
      create  app/views/my_page/details.html.erb
      create  app/views/my_page/thumbnail.html.erb

Edit the model file to define the attributes of the page object class:

Copy
# app/models/my_page.rb
class MyPage < Obj
  attribute :title, :string
  attribute :body, :widgetlist
  attribute :child_order, :referencelist
end

Note that a page may have several widgetlist fields, but for simplicity we will start with just one.

Now, new CMS objects based on the MyPage object class can be created. You might want to try this out in place on your Scrivito-based website.

Embedding widgets into a view

As with all other CMS content, widgets can be rendered inside a template by means of the scrivito_tag helper. Pass to this helper the name of the widget field to be rendered, and the CMS does the rest.

The page generator used above creates a template in app/views/my_page/index.html.erb. To have it render links pointing to all the children of a page, and display its headline, and its widgets, insert the following:

Copy
<%= scrivito_tag_list :ul, @obj, :toclist do |list, child| %>
  <%= list.tag :li do %>
    <%= link_to child.display_title, scrivito_path(child) %>
  <% end %>
<% end %>

<%= scrivito_tag :h1, @obj, :title %>
<%= scrivito_tag :div, @obj, :body %>

Now you are ready to add widgets to this page. Scrivito comes with a basic set of widgets, so you can experiment a bit with them. You might also add a couple of pre-packaged widgets to your app and simply use them.

Read on if you would like to know more about widgets and how to define custom widgets inside your application.

Widget file structure

Widgets live where the other models and views live, too (see also Providing Page and Widget Views).

* /app/models
  * paragraph_widget.rb

* /app/views
  * paragraph_widget

    # this template is invoked when a widget is to be rendered
    * show.html.erb
    # this template defines the appearance of your widget in the widget browser
    * thumbnail.html.erb
    # this template defines the property view of your widget
    * details.html.erb

Let's create a paragraph widget consisting of a headline and text:

$ rails g scrivito:widget ParagraphWidget
Copy
# app/models/paragraph_widget.rb
class ParagraphWidget < Widget
  attribute :headline, :string
  attribute :text, :string
end

A widget doesn't require a controller because it is never delivered individually. However, you do require at least a view for rendering it. The generator call above already created it.

The description_for_editor methods allow you to customize how a widget or a widget type is displayed to the user. It comes in two variants, the Widget.description_for_editor class method and the instance method of the same name. The value returned by the instance method is displayed on the widget handle, while the class method is used in several places where no widget instance is available, e.g. when adding new widgets or when communicating type restrictions to the user.

It is a good idea to change the class method to return a more descriptive name which is used as a fallback if the instance method has not been defined. The class method defaults to the class name, ParagraphWidget in our case.

Copy
class ParagraphWidget < Widget
  attribute :headline, :string
  attribute :text, :string

  def self.description_for_editor
    'Paragraph'
  end
end

The widget view: show.html.erb

This view is invoked for rendering the widget. This occurs whenever the widget is embedded into a CMS page, but also on other occasions, for example when a new widget is created in place (via AJAX). Thus, do not expect variables from other contexts, e.g. a particular controller instance variable, to be always present when the view is rendered.

Instead, the show.html.erb view should rely on the widget variable that contains the widget to be rendered. This instance variable is made available to the show.html.erb template of the widget:

Copy
# the widget to be rendered
widget

# the page that displays the widget
@obj

# the name of the widget field that contains the widget
widget.container_field_name

# the Obj that contains the widget (typically, but not necessarily, identical to @obj)
widget.container

As with any other view, a widget may be structured using partials. It also has access to the regular helpers part of the application.

If you plan to reuse your widget in other applications, avoid dependencies from functionality specific to a single application. Generally, it is a good idea to avoid custom helpers, @objwidget.container, and widget.container_field_name when writing widgets that are supposed to be reusable.

Note that the erb file suffix is meant to be an example here. Widget templates may be defined using the regular Rails/ActionView mechanisms, i.e. other template engines such as haml may be used as well.

To have a paragraph widget render its contents, the following template could be used. It outputs the HTML elements for the headline and text attributes of the widget.

Copy
# app/views/my_widget/show.html.erb
<%= scrivito_tag :h3, widget, :headline, data: {newlines: false} %>
<%= scrivito_tag :p, widget, :text %>

the data-newlines="false" parameter is used here to make the headline editable as a single line of text, i.e. without accepting line breaks.

Choosing a widget: thumbnail.html.erb

As editors insert a widget into a page, they are presented with a widget selection dialog. Use a thumbnail.html.erb file for your widget to define its look in the selection dialog. There is a scrivito_thumbnail helper available that makes things easy. Scrivito includes a set of icons you can use, and you don't need to care about styles:

Copy
# app/views/paragraph_widget/thumbnail.html.erb
<%= scrivito_thumbnail ParagraphWidget.description_for_editor, :text do %>
  A headline and text
<% end %>

Of course, you could also use your own image file instead of the icon or your own text instead of description_for_editor.

Editing a widget: details.html.erb

The property view of a widget can be invoked by clicking the Widget properties menu item in the widget menu. This opens a dialog where the details.html.erb of your widget is rendered. You can use it to let the editor change values not directly represented on the web page, e.g. alignment or style options. In the specific example given here, the property view of the widget remains empty because both the headline and the text can be edited in place.

The details dialog can be rendered in different sizes, according to the amount of content you want to display. The default size is medium. It can also be set to large or small by using scrivito_large_dialog or scrivito_small_dialog, respectively, as the top-level helper for rendering the details view.

As an example, let's assume that there is a Headline widget consisting of the headline text and an anchor:

Copy
# app/models/headline_widget.rb
class HeadlineWidget < Widget
  attribute :headline, :string
  attribute :anchor, :string
end

To make the anchor attribute of this widget editable using a smaller dialog, apply scrivito_small_dialog to the rendering helpers in the widget's details view, app/views/headline_widget/details.html.erb:

Copy
<%= scrivito_small_dialog do %>
  <%= scrivito_details_for 'Anchor' do %>
    <%= scrivito_tag(:div, widget, :anchor) %>
  <% end %>
<% end %>

By the way, the dialog size can also be set for page details views.

See Page and Widget Views Explained for an overview of helpers such as scrivito_details_for.

Try it out!

Start your server, turn on editing mode, and create a new page using the MyPage object class. Notice the small icon hovering at the very spot where your widget field is displayed?

Click the “Add Widget” item from a widget menu and choose Paragraph Widget. This will add a widget instance to the my_widgets field of the page. The widget is automatically rendered using the show.html.erb template and appears on the page.