Tutorial: Using Materialize with Ruby on Rails & Simple Form

Materialize is a CSS framework built around Google’s Material Design principles. In general, I would describe material design as being very focused with a kind of minimalist aesthetic. Components feel sharp and generally stand out in a subtle yet noticeable way. User interaction such as clicks are accompanied by small yet engaging and visually appealing effects. You can read more about Material Design at Google’s website. Here I'll walk you through a tutorial on how to use materialize with ruby on rails with a focus on simple form.

Set Up

Conveniently, there is a gem that bundles materialize with rails. In your gem file include:

gem ‘materialize-sass’

This gem is located at https://github.com/mkhairi/materialize-sass

Remember that since we are using the sass css precompiler, we will also need to include:

gem ‘sass-rails’ 

Your rails application will most likely have an application.css file. Replace this file with the sass equivalent, application.scss. Use import directives to include other sass stylesheets into this application file. The materialize import directive needs to come first. At the top of your application.scss write:

@import “materialize”;

You can then include other pages such as ‘home’ or ‘main’ by writing:

@import “home”;
@import “main”;

Finally, in your application.js you need to require jQuery and materialize-sprockets by writing:

//= require jquery
//= require materialize-sprockets


Using Materialize

Materialize uses some conventions that bootstrap users will find familiar. It comes with a 12 column grid layout that uses small, medium and large columns for different device sizes. It also comes with a responsive container class that you can use to wrap your body content.

Materialize Icons

In the head of your document use an external stylesheet linking to the google material icons.

   = stylesheet_link_tag “https://fonts.googleapis.com/icon?family=Material+Icons"

You can then specify icons by name inside an "i" tag with class ‘material-icons’. The following would produce the materialize menu icon

<i class = “material-icons”>menu</i>

Materialize Navbar

You can use the following haml to create a mobile responsive navbar that includes a drop down menu

%nav.red

 .nav-wrapper


   %div.brand-logo

     // Replace this with an image

     My Logo


   %a{href: "#", class: "button-collapse", data: {activates: "example"}}

     %i.material-icons menu


   // Normal NavBar that goes across the top

   %ul.right.hide-on-med-and-down


     %li

       =link_to "Link 1"

     %li

       = link_to "Link 2"

     %li

       = link_to "Link 3"


     // Drop Down Menu

     %li

       %a{href: "#", class: "dropdown-button", data: {activates: "myDropdown"}}

         = "Drop Down Menu"

         %i.material-icons.right arrow_drop_down

       %ul{id: "myDropdown", class: "dropdown-content"}

         %li

           = link_to "Drop Down Link 1"

         %li

           = link_to "Drop Down Link 2"

         %li

           = link_to "Drop Down Link 3"


   // Mobile Sidebar

   %ul#example.side-nav


     %li

       = link_to "Link 1"

     %li

       = link_to "Link 2"

     %li

       = link_to "Link 3"


       // Drow Down Menu on the mobile sidebar

       %li

         %a{href: "#", class: "dropdown-button", data: {activates: "myDropDownSide"}}

           = "Admin Menu"

           %i.material-icons.right.fix-line-height arrow_drop_down

         %ul{id: "myDropDownSide", class: "dropdown-content dropdown-content-sidebar-fix"}

           %li

             = link_to "Drop Down Link 1"

           %li

             = link_to "Drop Down Link 2"

           %li

             = link_to "Drop Down Link 3”


 

Materialize and Rails Simple Form

Materialize form elements require certain wrapper elements and classes to work. These are not the same as what simple form defines for your inputs by default, meaning it can take some extra work to get materialize and simple form working nicely together. 

Simple form defines the wrapper and other elements included with the input in the simple_form.rb initializer file located in config. 

You can remove all wrapper elements and all companion elements inserted by simple form by using f.input_field instead of f.input. So for example, to ignore the simple form wrappers and make our own simple string input that works with materialize, we could write…

= simple_form_for @myModel do |f|
    .input-field
      =f.input_field :my_field
      %label{for: “my_field"} My Label Text

However this becomes somewhat tedious to do every time we need to write a form element. A better approach is to create our own wrapper in the simple form initializer. In the simple_form.rb we can write:

config.wrappers :materialize_form, class: 'input-field my-class‘, error_class: 'has_error' do |b|

    b.use :html5

    b.use :placeholder

    b.use :input

    b.use :label

    b.use :error, wrap_with: { tag: 'p' , class: 'error-text'}

    # b.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }

  end

The above creates a custom wrapper called materialize_form_class. By using this wrapper every input by default gets a label and both are contained within a div with two classes, input-field and my-class. Input-field is used by materialize and my-class is a custom class that we could use to control layout and other properties. This wrapper also tells simple form to append the class ‘has_error’ to the wrapper div when the input contains errrors. Furthermore, when an error is present, within the wrapper div a ‘p’ tag with class error-text gets added after the label. With this we can control how we want to display error messages.

We can tell simple form to use this wrapper by writing:

= simple_form_for @myModel, wrapper: :materialize_form_class

Now all elements within this form will have the :materialize_form_class wrapper.

We can tell simple form to use our wrapper by default by adding the following line into the simple_form.rb file.

config.default_wrapper = :materialize_form

We can also declare our wrapper for individual elements within a form.

=simple_form_for @myModel do |f|

   = f.input :my_field, wrapper: :materialize_form_class

When using our :materialize_form_class wrapper we can change the label or wrapper html by using

=f.input :my_field, label: “New label", wrapper_html: {class: “newclass”}

This adds a class of “newclass” to our wrapper div and changes the label to display “New label”.

Materialize CSS comes with button classes that we can use to give our buttons a nice effect when clicked. We can tell simple form to use a default button class. In simple_form.rb write:

config.button_class = “btn waves-effect waves-light”

We can use this in simple form by writing:

=f.button :button

This will create a button with type submit.

We can also use simple form to write our own custom input components. Let’s write a custom input component for the materialize switch. The markup for this switch is:

<div class="switch">
   <label>
      “Off”
      <input type="checkbox">
      <span class="lever"></span>
      “On”
   </label>
</div>

We will create a custom input called my_switch which will output the above markup automatically.

Start by adding a new folder to your app directory called inputs if it does not already exist. In this folder create a new file called my_switch_input.rb and start by writing the following:

class MySwitchInput < SimpleForm::Inputs::Base

   def input

   end

end

Within the input method we can start building up our template

def input

   template.content_tag(:div, class: 'switch') do

      template.content_tag(:label)

   end

end

This would give us a label 

class MySwitchInput < SimpleForm::Inputs::Base

   def input

      template.content_tag(:div, class: 'switch') do

          template.concat label_tag

       end

    end

    def label_tag

        template.content_tag(:label) do

           template.concat "Off"

           template.concat @builder.input_field(attribute_name, {type: :checkbox})

           template.concat span_tag

           template.concat "On"

       end

    end

    def span_tag

        template.content_tag(:span, class: 'lever') do

        end

    end

end

Within the outermost div tag with class switch we call our method label_tag to nest a label tag. Within this tag we nest some text, a simple form input field with type ‘checkbox’ and a span tag defined in our span_tag method.

The @builder allows us to declare simple form elements like a text_field or input_field within our custom input. Here I used input_field to make sure there is no additional wrapper markup added around our checkbox input element.

We can now use this element in our forms as follows

=f.input :my_field, as: :my_switch

Keep in mind that this will give the input whatever markup is defined in the default wrapper class.

Remember that we can get the input by itself with no wrapper by using:

=f.input_field :my_field, as: :my_switch

We can even tell simple form what wrapper to use for our new custom input by including the following in our simple_form.rb initializer:

config.wrapper_mappings = { :my_switch => :my_wrapper}

Now our :my_switch component will use the wrapper :my_wrapper by default. Pretty convenient!

Posted in: Development





Download the Web Application Startup Guide






Share This Article:

comments powered by Disqus
TEST