Learn to apply some styling to the existing list of products
We'll cover the following
Our customer has one more request. (Customers always seem to have one more request, don’t they?) The listing of all the products is ugly. Can we pretty it up a bit? And, while we’re in there, can we also display the product image along with the image URL?
We’re faced with a dilemma here. As developers, we’re trained to respond to these kinds of requests with a sharp intake of breath, a knowing shake of the head, and a murmured, “You want what?” At the same time, we also like to show off a bit. In the end, the fact that it’s fun to make these kinds of changes using Rails wins out, and we fire up our trusty editor.
Before we get too far, though, it would be nice if we had a consistent set of test data to work with. We could use our scaffold-generated interface and type data in from the browser. However, if we did this, future developers working on our codebase would have to do the same. And if we were working as part of a team on this project, each member of the team would have to enter his or her own data. It would be nice if we could load the data into our table in a more controlled way. It turns out that we can. Rails have the ability to import seed data.
To start, we simply modify the file in the DB directory named seeds.rb
.
We then add the code to populate the products table. This uses the create!()
method of the Product model. The following is an extract from that file. Rather than type the file by hand, you might want to download the file from the sample code available online. While you’re there, copy the images into the app/assets/images
directory in your application. Be warned: this seeds.rb
script removes existing data from the products table before loading the new data. You might not want to run it if you’ve just spent several hours typing your own data into your application!
Product.delete_all#...Product.create!(title: 'Build Chatbot Interactions',description:%{<p><em>Responsive, Intuitive Interfaces with Ruby</em>The next step in the evolution of user interfaces is here.Chatbots let your users interact with your service in theirown natural language. Use free and open source tools alongwith Ruby to build creative, useful, and unexpected interactionsfor users. Take advantage of the Lita framework’s step-by-stepimplementation strategy to simplify bot development and testing.From novices to experts, chatbots are an area in which everyonecan participate. Exercise your creativity by creating chatbotskills for communicating, information, and fun.</p>},image_url: 'dpchat.jpg',price: 20.00) #...
(Note that this code uses %{…}. This is an alternative syntax for double-quoted string literals, convenient for use with long strings. Note also that because it uses the Rails create!()
method, it’ll raise an exception if records can’t be inserted because of validation errors.)
To populate your products table with test data, run the following command:
depot> bin/rails db:seed
Now let’s get the product listing tidied up. This has three pieces: defining a set of style rules, connecting these rules to the page by defining an HTML class attribute on the page, and changing the HTML to make styling the page easier.
We need somewhere to put our style definitions. Rails have a convention for this, and the generate scaffold command that we previously issued has already laid all of the necessary groundwork. As such, we can proceed to fill in the currently empty products.scss
style sheet in the app/assets/stylesheets
directory:
// Place all the styles related to the Products controller here.// They will automatically be included in application.css.// You can use Sass (SCSS) here: https://sass-lang.com/.products {margin: 0;padding: 0.5em;a {padding: 0.354em 0.5em;border-radius: 0.354em;}table {border-collapse: collapse;}td {padding: 0.5em;margin: 0;}tr.list_line_odd {background-color: #effeef;}td.image {// Hide this on mobile devicesdisplay: none;// Assume anything bigger than 30em// is a non-mobile device and can// fit the image.@media (min-width: 30em) {display: block;img {height: 11.3em;}}}td.description {h1 {font-size: 1.4em;}}td.actions {ul {padding: 0;list-style: none;li {padding: 0.5em 0.5em;}}}tfoot {td {padding: 0.5em 0;}}}
If you choose to download this file, make sure that the timestamp on the file is updated. If the timestamp isn’t updated, Rails won’t pick up the changes until the server is restarted. You can update the timestamp by going into your favorite editor and saving the file. On macOS and Linux, you can use the touch command
.
Look closely at this style sheet and you’ll see that CSS rules are nested, in that the rule for li
is defined inside the rule for ul
, which is itself inside the rule for td.actions
. This tends to make rules less repetitive and therefore easier to read, write, understand, and maintain.
At this point, you’re familiar with files ending with .erb
being preprocessed for embedded Ruby expressions and statements. If you note that this file ends with .scss
, you might guess that the file is preprocessed as Sassy CSS before being served as CSS. And you’d be right!
Again, like ERB, SCSS doesn’t interfere with writing correct CSS. What SCSS does is provide additional syntax that makes your style sheets easier for the author and easier to maintain. All of this is converted for you by SCSS to standard CSS that your browser understands.
Finally, we need to define the products
class used by this style sheet. If you look at the .html.erb
files we’ve created so far, you won’t find any reference to style sheets. You won’t even find the HTML
application.html.erb
, is a Rails layout and lives in the layouts
directory:
<!DOCTYPE html><html><head><title>Depot</title><%= csrf_meta_tags %><%= csp_meta_tag %><%= stylesheet_link_tag 'application', media: 'all','data-turbolinks-track': 'reload' %></head><body><main class='<%= controller.controller_name %>'><%= yield %></main></body></html>
You’ll note we’ve wrapped the content in a main tag
that has a CSS class of the current controller. This means that when we are rendering a product listing, it will have the CSS class products
, and when we render an order, it’ll have the class orders. This allows us to target our CSS, because Rails includes all CSS in app/assets/stylesheets
on every page, no matter what. If we want to apply a style to, say, a table header only on order pages, we can write .orders th { «css» }
and—even though that CSS will be included on non-orders pages —it won’t apply, because the top-level element won’t have the class orders
.
Now that we have all the style sheets in place, we’ll use a table-based template, editing the index.html.erb
file in app/views/products
and replacing the scaffold-generated view:
<% if notice %><aside id="notice"><%= notice %></aside><% end %><h1>Products</h1><table><tfoot><tr><td colspan="3"><%= link_to 'New Product', new_product_path %></td></tr></tfoot><tbody><% @products.each do |product| %><tr class="<%= cycle('list_line_odd', 'list_line_even') %>"><td class="image"><%= image_tag(product.image_url, class: 'list_image') %></td><td class="description"><h1><%= product.title %></h1><p><%= truncate(strip_tags(product.description),length: 80) %></p></td><td class="actions"><ul><li><%= link_to 'Show', product %></li><li><%= link_to 'Edit', edit_product_path(product) %></li><li><%= link_to 'Destroy',product,method: :delete,data: { confirm: 'Are you sure?' } %></li></ul></td></tr><% end %></tbody></table>
This template uses a number of built-in Rails features:
-
The rows in the listing have alternating background colors. The Rails helper method called does this by setting the CSS class of each row to either
list_line_even
orlist_line_odd
, automatically toggling between the two style names on successive lines. -
The
truncate()
helper is used to display the first eighty characters of the description. But before we calltruncate()
, we callstrip_tags()
to remove the HTML tags from the description. -
Look at the
link_to 'Destroy'
line. See how it has the parameterdata: { confirm: 'Are you sure?' }
. If you click this link, Rails arranges for your browser to pop up a dialog box asking for confirmation before following the link and deleting the product.
We loaded some test data into the database, we rewrote the index.html.erb
file that displays the listing of products, we filled in the products.scss
style sheet, and that style sheet was loaded into our page by the application.html.erb layout file. Now let’s bring up a browser and point to http://localhost:3000/products
. The resulting product listing might look something like the following screenshot.
The above link
http://localhost:3000/products
is valid if you run this application to your local machine. Our widget will automatically redirect you to this link after pressing theRUN
button.
So we proudly show our customer her new product listing and she’s pleased. Now it’s time to create the storefront.
A running app
See things in action by running the following application
<% if notice %> <aside id="notice"><%= notice %></aside> <% end %> <h1>Products</h1> <table> <tfoot> <tr> <td colspan="3"> <%= link_to 'New Product', new_product_path %> </td> </tr> </tfoot> <tbody> <% @products.each do |product| %> <tr class="<%= cycle('list_line_odd', 'list_line_even') %>"> <td class="image"> <%= image_tag(product.image_url, class: 'list_image') %> </td> <td class="description"> <h1><%= product.title %></h1> <p> <%= truncate(strip_tags(product.description), length: 80) %> </p> </td> <td class="actions"> <ul> <li><%= link_to 'Show', product %></li> <li><%= link_to 'Edit', edit_product_path(product) %></li> <li> <%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %> </li> </ul> </td> </tr> <% end %> </tbody> </table>
Get hands-on with 1400+ tech skills courses.