[ToDoList] Materialize IV: Other Views

With the last section being fairly intense, you'll be pleased to know (I hope) that the remaining todo views aren't nearly as complicated and have a bit more freedom with regards to creativity when building them.
We'll spend some time sprucing up the index and show views to conform to our fancy new standard, and try to have a bit of fun with it.

  1. I Am The Table
  2. Show With the Flow

 


 

Starting with the index view, we'll naturally want to keep it fairly basic - showing the name and possibly the description, as well as the same [C]RUD actions we had already in our current index view.
There are numerous options available to us to present these features:

  • We could go mega-fancy and do a Kanban-esque board of cards, with their actions contained somewhere within.
  • We could go ultra-contemporary and still use cards, but simply list each item.
  • We could go sleek with all of the show views displaying under tabs.
  • Or we can just stick with a fairly basic table option.
In my personal opinion, I'd like a To-Do List that is just that - a list of the items. To maximise the number we can show on a page, I'm going to go with the table option!

There are a couple of funky options that Materialize gives us in terms of tables, including responsive tables that will flip the axes if the browser window falls below a certain width (i.e. mobile browsers) as well as functionality to highlight the selected row.
To fulfill my vision, I am going to leave the axes alone but add the highlight functionality, and then replace the action links with tooltipped icons. For absolute ease of use, I'm also going to add a button to create a new To-Do list item at the top of this page.

So, let's get started! There shouldn't be a huge change to the general structure of our original index view - just adding in the containers and the buttons! Heading into /app/views/todos/index.html.erb:

<div class="container">
  <div class="row valign-wrapper">
    <div class="col s8">
      <h3 class="header">Your To-Do List</h3>
    </div>
    <div class="col s4">
      <%= link_to 'New To-Do Item', new_todo_path, class: 'btn waves-effect waves-light right' %>
    </div>
  </div>
  <div class="row">
    <table class="highlight">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Description</th>
          <th colspan="3">Actions</th>
        </tr>
      </thead>

      <tbody>
        <% @todos.each do |todo| %>
          <tr>
            <td><%= todo.id.to_s %></td>
            <td><%= link_to todo.name, todo_path(todo) %></td>
            <td><%= truncate(todo.description, length: 100) %></td>
            <td>
              <%= link_to todo_path(todo),
                          :class => 'tooltipped',
                          :"data-tooltip" => 'Show To-Do Item',
                          :"data-position" => 'bottom' do %>
                <i class="material-icons">visibility</i>
              <% end %>
            </td>
            <td>
              <%= link_to edit_todo_path(todo),
                          :class => 'tooltipped',
                          :"data-tooltip" => 'Edit To-Do Item',
                          :"data-position" => 'bottom' do %>
                <i class="material-icons">edit</i>
              <% end %>
            </td>
            <td>
              <%=
                link_to todo_path(todo),
                        method: :delete,
                        data: { confirm: 'Are you sure?' },
                        :class => 'tooltipped',
                        :"data-tooltip" => 'Delete To-Do Item',
                        :"data-position" => 'bottom' do %>
                <i class="material-icons">delete</i>
              <% end %>
            </td>
          </tr>
        <% end %>
      </tbody>
    </table>
  </div>
</div>
So, unpacking what we've done:
  • We've aligned the button to create a new item with the heading using a valign-wrapper class on the row div that contains them.
  • The highlight class was added to the <table> element to give it the functionality we want.
  • We've replaced the text previously showing the action links with icons, but not buttons, in order to fit them nicely in the table.
  • Our trusty Rails helper truncate has been dusted off to limit the description to 100 characters.
When we head to our index URL, we can see how beautiful this now looks!

Materialize index view

Oh, but were it so simple...
If we reduce the width of our browser window to mobile size, we can see how horrific this will look to poor mobile users:

Materialize index view mobile before

Not only is the button for new items truncated and squashing the page heading, but the table itself is awfully squished and unseemly. Believe me, no amount of playing with column widths is saving this (especially if we want the top button and the heading vertically aligned!)

However, we've been doing this guide for over 20 sections now - I'm sure we can come up with a solution. And we can.

Perusing the Materialize site, we come across the functionality for floating action buttons which, as we've used mobile devices before, we know will add a persistent button in the bottom corner of our view - this is a prime solution to our mobile-user problem, especially given that Materialize already provides helpers to hide elements in certain browser window widths!

So let's add one of these floating action buttons to the small views to replace our create button at the top, and use the helpers and column widths to help us hide the ones that aren't needed - we'll replace our first row div block with:

  <div class="row valign-wrapper">
    <div class="col s12 m8">
      <h3 class="header">Your To-Do List</h3>
    </div>
    <div class="col m4 hide-on-small-only">
      <%= link_to 'New To-Do Item', new_todo_path, class: 'btn waves-effect waves-light right' %>
    </div>
    <div class="fixed-action-btn hide-on-med-and-up">
      <%= link_to new_todo_path,
                  class: 'btn-floating btn-large waves-effect waves-light tooltipped',
                  :"data-tooltip" => 'New To-Do Item',
                  :"data-position" => 'left 'do %>
        <i class="material-icons">add</i>
      <% end %>
    </div>
  </div>
So we've hidden our original new button for small views, and made the heading use all 12 columns in this case. We've also added the floating action button and hidden it for everything that isn't a small view, which we've decorated with a charming plus icon.

As a final thought, let's also add the same hide helper for the description table header:

          <th class="hide-on-small-only">Description</th>
as well as the table cell:
            <td class="hide-on-small-only"><%= truncate(todo.description, length: 100) %></td>
and picture the rejoicing of mobile users everywhere as we gaze upon the beautiful new mobile interface we have constructed!

Materialize index view mobile after

 


 

With our index complete, there remains only one action view remaining to beautify - our show view. Again, I'm going to stay with the "simpler is better" mantra and, out of the many options that Materialize gives us for this sort of thing, am going to opt for displaying the Todo values in a collection (though will obviously go with a little flair and add some icons).

Aside from the values themselves, we will need a similar set of button links as the edit view so that we can navigate effectively to other actions.
Given this, we may as well copy the HTML for our edit view and use it as a basis to build from - all we need to do differently is:

  • Change the page heading to reflect the view's purpose.
  • Substitute the form's render element for the collection containing the Todo object values.
  • Swap the show view button for one that links to the edit view.
Everything else (namely the general layout & column widths) will remain the same!

The page heading is simple, as we can simply preserve the value from the existing show view and pop it into the <h3> block from our copied edit view HTML.

We can largely apply this same logic to the Todo object value collections as well, copying the HTML from the avatar content collection example, removing the bits we don't want and subtituting in the values for our object as with our current show view.

And all we need to do to the link buttons is change the show button's link helper, icon and tooltip to get it to point to the correct view. We'll even change the colour so that there is some consistency of this amongst our action buttons!

So, with all this in mind, we'll set about making the view as glamourous as the others - /app/views/todos/show.html.erb:

<% content_for(:html_title) { @todo.name } %>
<div class="container">
  <div class="row">
    <h3 class="header">To-Do item: <%= @todo.id.to_s %></h3>
  </div>
  <div class="row">
    <div class="col s12 m8">
      <ul class="collection">
        <li class="collection-item avatar">
          <i class="material-icons circle">title</i>
          <span class="title"><strong>Name</strong></span>
          <p><%= @todo.name.to_s %></p>
        </li>
        <li class="collection-item avatar">
          <i class="material-icons circle">subject</i>
          <span class="title"><strong>Description</strong></span>
          <p><%= @todo.description.to_s %></p>
        </li>
      </ul>
    </div>
  </div>
  <div class="row">
    <div class="col s4 m2 xl1 offset-m1 offset-xl2 center">
      <%= link_to edit_todo_path(@todo),
                  class: 'btn-floating btn-large waves-effect orange tooltipped center',
                  :"data-tooltip" => 'Edit To-Do Item',
                  :"data-position" => 'bottom' do %>
        <i class="material-icons">create</i>
      <% end %>
    </div>
    <div class="col s4 m2 center">
      <%= link_to todo_path(@todo),
                  method: :delete,
                  data: { confirm: 'Are you sure?' },
                  class: 'btn-floating btn-large waves-effect red tooltipped center',
                  :"data-tooltip" => 'Delete To-Do Item',
                  :"data-position" => 'bottom' do %>
        <i class="material-icons">delete</i>
      <% end %>
    </div>
    <div class="col s4 m2 xl1 center">
      <%= link_to todos_path,
                  class: 'btn-floating btn-large waves-effect green tooltipped center',
                  :"data-tooltip" => 'Back to To-Do List',
                  :"data-position" => 'bottom' do %>
        <i class="material-icons">subdirectory_arrow_left</i>
      <% end %>
    </div>
  </div>
</div>
Loading a To-Do item's show view in our browser will reveal the unrivalled gorgeousness we have just applied to our view:

Materialize show view

But, of course, there's a slight problem - and I bet you know what it is!
Switching to a mobile view width, we can see that the action buttons aren't visible if the description is too large - we have to actively scroll down to reach them:

Materialize show view mobile before

Now, obviously, this isn't the end of the world. However it also isn't ideal, and little details like these will put users (especially new ones) at a disadvantage.
It didn't matter so much with the edit view, because that page has a general flow to it where one will naturally scroll to the bottom while working one's way through the form. But on a show view, where people might only be accessing the view to check something and then edit / delete it, we need to be a bit more accommodating.

Luckily, we've already explored the best means to circumvent this issue already in this section - our friendly neighbourhood floating action button!
Our usage of it above largely stripped out its secondary functionality, which is to spawn even more floating action buttons, and this is what we will use to get around our issue. All we need to do is append an unordered list (<ul>) of button links in the floating-action-btn div!

So let's get to it! Below the final row div in our view, let's add the HTML for the full-power floating action buttons (making sure we hide the div on large (desktop) view windows only, so it's there for tablet users as well):

  <div class="fixed-action-btn hide-on-large-only">
    <a class="btn-floating btn-large waves-effect">
      <i class="material-icons">more_vert</i>
    </a>
    <ul>
      <li>
        <%= link_to edit_todo_path(@todo),
                    class: 'btn-floating btn-large waves-effect orange tooltipped center',
                    :"data-tooltip" => 'Edit To-Do Item',
                    :"data-position" => (tooltip_pos rescue 'bottom') do %>
          <i class="material-icons">create</i>
        <% end %>
      </li>
      <li>
        <%= link_to todo_path(@todo),
                    method: :delete,
                    data: { confirm: 'Are you sure?' },
                    class: 'btn-floating btn-large waves-effect red tooltipped center',
                    :"data-tooltip" => 'Delete To-Do Item',
                    :"data-position" => 'left' do %>
          <i class="material-icons">delete</i>
        <% end %>
      </li>
      <li>
        <%= link_to todos_path,
                    class: 'btn-floating btn-large waves-effect green tooltipped center',
                    :"data-tooltip" => 'Back to To-Do List',
                    :"data-position" => 'left' do %>
          <i class="material-icons">subdirectory_arrow_left</i>
        <% end %>
      </li>
    </ul>
  </div>
And, so we don't double up on buttons, let's make sure the row div containing our original buttons is only set to show on large window views:
<div class="row hide-on-med-and-down">
Now if we head back to our browser and simulate a mobile view of our show view, we can see our lovely new button! And when you click / tap it, true joy ensues:

Materialize show view mobile after

Well now, this is looking dangerously professional. At least, it is from the front-end. From the back end we can see, purely in the show view we've just been working in, that we have a lot of repeating code - the buttons are the chief culprits of this egregious crime, and we're going to lock them up for it.
"Where?" you ask?
"Why," I respond, again with infuriating levels of childlike excitement, "in some partials, of course!"

As we've already kept a consistent colour and icon theme with our buttons, this will work absolutely brilliantly. The only thing we need to routinely change in their code is the data-position attribute for the tooltip.
Well, wouldn't you know, Rails lets us pass local variables to our partials, which we can use to pass whichever value we need to the partial!

Let's use the Edit To-Do Item (edit) button as our guinea pig.
First, we'll copy the code for the button and put it in a partial in our todos view directory, and substitute in a variable for the value of data-position - /app/views/todos/_button_edit.html.erb:

<%= link_to edit_todo_path(@todo),
            class: 'btn-floating btn-large waves-effect orange tooltipped center',
            :"data-tooltip" => 'Edit To-Do Item',
            :"data-position" => (tooltip_pos rescue 'bottom') do %>
  <i class="material-icons">create</i>
<% end %>
Notice the rescue where we have called the tooltip_pos variable which means that, should we fail to pass the variable to the partial and it error out, instead if will default to displaying tooltips at the bottom of the button!

Now let's head back to to our show view and swap the list item for the mobile edit button (in the floating-action-btn list) for:

      <li><%= render 'button_edit', tooltip_pos: 'left' %></li>
Inspecting this in our browser's mobile view we can see that the button is present and correct, with its tooltip in the right place!
(You might need to hold the button for it to appear)

Materialize show view mobile partial edit button

We can also do the same for the other edit button for larger views (the one we added in the row below the collection) and, because we set the default value of our tooltip_pos variable to 'bottom' with that handy rescue, we don't even need to specify the position!

      <%= render 'button_edit' %>
Again, inspecting this in our browser window (regular, not mobile) shows the button and tooltip exactly where they were before:

Materialize show view desktop partial edit button

With this method of fighting bloated code, we can now go forth and do the same for each instance of a button that we're likely to use more than once:

  • Back to To-Do Item (show view) button in the edit view.
  • Delete To-Do Item (destroy) buttons in the show & edit views.
  • Back to To-Do List (index view) buttons in the show & edit views.
  • New To-Do Item (new view) button in the index view (though only the mobile-friendly one!).
(I would skip the Cancel button in the new view, as we're only likely to use this once).
When you have partialised these buttons, you may relax and bask in the glory of your exquisitely handsome app, knowing that all the beauty in the front-end is also reflect under the hood!

 


 

With all this excitement, we can't forget our other (non-CRUD) views, which we will be addressing next in our final Materialize-specific section.
For now, though, celebrate with a short desk dance and a push to GitHub!

$ git add -A
$ git commit -m "Show and index views beautified, buttons refactored"
$ git push origin materialize

 
 

Comments

Popular posts from this blog

New Rails Apps with Docker Compose

[ToDoList] Basic Pages

[ToDoList] Docker Compose