- Read Tutorial
- Watch Guide Video
Great job going through that section I know that was a lot of work. You have now extended your knowledge in regards of how to work with the view component inside of rails. In this deep dive, I want to isolate the main driver of how views are made possible in Rails. Rails uses a tool called ActionView. I want to take a much deeper look at what action view actually has and a number of methods that are available so you can use that in future applications.
I created an empty Rails application and I called it action view deep dive. I did not do any kind of creation, this is 100 percent the exact application you get if you do Rail's new. You don't have to clone or anything you can just type rails new and the app name. Then we can get started.
Right here we have an app, this guide is a deep dive into action view. I want to create some items that can leverage that. Let's talk about creating a controller, we're going to create one from scratch. Usually, you would use a controller generator, however, in this case, I want to take time and look at every stage of what we need to do in order to get views working in our application. The first one is to create a controller.
I'm going to create a class and this can be GuidesController and this has to inherit from ApplicationController. Remember the guides controller gets access to everything that is inside of the application controller.
class GuidesController < ApplicationController end
If I hit save, this is going to be guides_controller.rb
, now let's create some actions. I'm not even going to worry about crud actions, this is not specifically about data flow. The focus of this is on The View. I'm going to show that we can name these anything that we want. I could say
def book end
Now I could actually create something called guide or we could even call it something else like book. I want to show the importance of being able to have this and then be able to map this to a view directly.
I'm going to come down to views and we have to create a directory here. This is going to be called guides
and this is the second stage of the mapping and how action view is wired up. Right here we have guides controller, this is only going to work with the views inside of guides.
We have views/guides, that is the path and that is what the controller is going to look for. We named this guides and we have the file name here it's going to look in the view directory and try to find guides. If I right click and save this as book.html.erb
. The way the action view works is it takes this item here and it's going to map it to this HTML file.
Let's come and say <p>Hey, I'm in the book view</p>
and save.
We need to do is give this a route. In routes.rb, I'm going to say get 'guides/book'
Let's start up the terminal. Run rails db:create
and rails db:migrate
Opening this up in the browser, if I now go to guides/book, this works. It says "Hey, I'm in the book view."
Let's quickly review what this process is. We have our controller, this has an action name and the action name is wired up into a specific file. These have to be named the exact same, assuming you're following the standard conventions. Technically, you could override all the conventions but that would mean writing more code and that's rarely a good thing.
Next, guides is going to be mapped into the views directory and it's going to look for a guide's directory and that's where it's going to look for "book." If we were to move book, it is going to break. There is going to be a lot of crossover when we're talking about action view versus action controller. They're both modules inside of rails and there's crossover and that is perfectly fine.
Let's get into what gets shown on the page. We've shown multiple times how you can use embedded Ruby, with embedded Ruby we have two options. We have one option that has no equal sign and then we have one that does. Let's talk about the difference, if we had some type of collection, I'm going to create an array put it inside of the guides controller.
def books @books = ['Fountainhead', 'Deep Work', 'Rails Way'\ end
Now we have an array of strings. This is inside of books which means that we have access to put it in book.html.erb-guides. If I put this in both locations the difference is that books is going to be printed out here (<%= @books %>
) but it's not going to be printed out here (<% books %>
).
If I come to Google Chrome and hit refresh, you can see that we have ["Fountainhead", "Deep Work", "Rails Way"] but it's only been shown one time. If I add the equals, then it will be shown twice.
When is it important to do this? If you are relatively new to rails then it's definitely worth another look. Books printed out the value of books. What if we wanted to grab each item independently and iterate over them? That is where we would use this type (<% @books %>). I can say
<% @books.each do |book| %> <p><%= book %><?p> <% end %>
If I hit refresh now, it prints each one of the items out.
This is very important, I've been asked by students multiple times on what the difference is. The other thing I've been asked is how to fix this weird bug. That's not actually a bug it's actually rails doing exactly what it's supposed to. If you put the equals right here (<%= @book.each do |book| %>) at the block where it's starting to call each and where it sets the block variable. This is going to actually print out the object so it's going to print out whatever it's iterating over. This looks really weird and rails because rails works with much larger objects and so you end up with some weird content that's shown and so it's actually showing this one right here. If I remove that, then it gets fixed.
Let me show you another example. All of this still works, I'm usually asked: "Why would you use this?" There are many times where you want to print out the value of objects you're iterating over but not the initial starter collection. Also, the difference is that this only processes the Ruby code and It doesn't show it to the user.
If I wanted to do something like
<% puts "hey there"
and hit save, If I come back to the site and hit refresh, you don't see my "hey there."
puts goes into the terminal, it prints out "hey there." The way puts works, it is something that works with what's called standard out. Which means that it's something that will be rendered inside of the terminal, even if it's not rendered on the site.
I think that's one of the easier ways of understanding what's going on. Puts is running, this is working perfectly but it's simply hidden because it is only processing Ruby code.
I'm going to keep each one of these so you can grab the source code if you ever want to use it in the future.
That is the difference between using an equal and not using an equal.
I want to switch to the terminal and we're going to run a scaffold. I'd say rails g scaffold Blog title:string body:text
and rails db:migrate
I want to show you is how we can leverage partials in a different way. I know we've talked about partials quite a bit in the section but I still want to show you a few more items. Partials have so many different options that you can use.
I'm going to come in two views/blogs and we're going to refactor our index action like we've done before.
I'm going to edit this and render a partial.
We need to create a partial called _blog.html.erb
and we can paste
<tr> <td><%= blog.title %></td> <td><%= blog.body %></td> <td><%= link_to 'Show', blog %></td> <td><%= link_to 'Edit', edit_blog_path(blog) %></td> <td><%= link_to 'Destroy', blog, method: :delete, data: { confrim: 'Are you sure?' } %></td> </tr>
If I hit save, hit blogs and this all works
Let's create a few test blogs. Now we have some test data right here to work with.
If I change the file to this:
<div> <p><%= blog.title %></p> <p><%= blog.body %></p> <p><%= link_to 'Show', blog %></p> <p><%= link_to 'Edit', edit_blog_path(blog) %></p> <p><%= link_to 'Destroy', blog, method: :delete, data: { confrim: 'Are you sure?' } %></p> </div>
If I hit refresh, you can see that these are a little bit different.
One thing that is very cool with partials, something you won't use all the time but when you need to it becomes incredibly handy. Let's say that we have our call here and in the browser we want to have some way of showing when one blog is done and the other one is starting. We could technically come into the blog partial and add a tag and save. Now we have a horizontal line separating blogs.
Now I'm also going to come here and edit the index.html.erb
<p id="notice"><%= notice %> <h1>Blogs</h1> <div> <%= render @blogs %> </div> <br> <%= link_to 'New Blog', new_blog_path %>
There is a better way of doing this, (delete the tag in index.html.erb) the way you can do it is by actually implementing what's called a spacer template.
We have the ability to add
<div> <%= render @blogs, spacer_template: 'blog_ruler' %> </div>
Now we need to go create this blog ruler template _blog_ruler.html.erb
and we put our <hr>
tag in.
I'm going to hit refresh and nothing happened, so why exactly is nothing occurring? I have my blog ruler and I'm calling my spacer template of blog ruler, this is something that gave me a little bit of a hard time when I was initially learning it. This nice shorthand syntax of render blogs is really kind of picky and you will see many times where you'll have things like this happen where you'll implement something and it won't work with a partial. The reason is, we need to actually declare the partial.
We have to say <%= render partial: @blogs, spacer_template: 'blog_ruler' %>
then it allows you to take everything else in. It's just something that's built into rails, at some point I have to think they are going to refactor this and make it a little bit more consistent.
If you ever have an issue where you're calling something like we did there and either you get an error or it's not rendering properly there might be a chance that this is the issue.
Now with that saved, let's come back hit refresh. Now it's working, I want to point out why this is kind of important. Inside of our blog ruler we have our plain little hr tag, remember back when we had the hr tag here (_blog.html.erb) there was something a little bit funky going on. We had an hr tag here and we also had one here at the bottom. The reason for that was because we had an hr tag that simply said I am going to be printed out on the screen underneath every single blog post, even the last blog post.
Occasionally of you might want something like that but usually spacing items out that something that goes only in between components. That's what is nice about this, it doesn't put it at the very end. This is also considered a better practice in terms of having clean code. This is the syntax that you need in order to get it running.
Earlier in this section, we looked at the application.html.erb file for our layout. We kind of skipped over a few of these, we talked about how the stylesheet link tag is connected to a CSS file but also one thing we can do is wire this up to a CDN. Your application CSS file is in assets/stylesheets. We also have the ability to call outside stylesheets. Whenever you need to do that you can say <%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" %>
This is going to change our styles in the application. If I come to Google Chrome, notice what these fonts look like. If I hit refresh, now it's changed. Notice our hr tag actually looks a little bit cleaner and we're getting the font from Bootstrap right here. If we start navigating around everything looks a little bit different.
Even our buttons are different and the font for each one of these items has changed. By just calling styleesheet_link_tag, it was able to bring all of this in for us.
The last thing we're going to cover in this deep dive is caching. Caching is a way to be able to take a slow loading page, something where you have a bunch of HTML and CSS files, it takes a while to load. Rails gives you the ability to wrap those kinds of components up and then be able to cache them. A cache compiles the HTML/CSS into a very fast easy access file and that is what gets rendered in the browser for the user. This happens on the browser side.
Let's take an example, to see the difference on a site as small as this one is pretty difficult. Let's take a look at the blog index and show how we could do this. One way I have done this before is, I will just come and I'll take the div and I'll just give this type of process so I can say embedded Ruby and say cache do then wrap this up in a do block. And then here just say end.
If I hit save what is going to happen is that when we go to the browsers when we go to the index page and we hit refresh it's going to actually save this HTML code to the browser so that the next time they try to load it, it's going to load much faster.
We've barely even have any HTML so it loads seemingly almost instantly but by leveraging this I just wanted to really show you the format to do this and when you run into this in the future you'll know kind of what the syntax is.
If I switch to the browser and hit refresh the way this is that that first time when it just got hit then the cache was initiated and the cache stored only the HTML inside of this. That's one thing to be very cognizant of is you want to be kind of picky with where you're doing this you wouldn't want to say wrap the entire site in this type of a mechanism. You would end up with a number of issues, if you change the content and user had been accessing the cached version, they may actually see something different than what you pushed out. The cache expires after a certain point of time but you really want to be careful about how this is processed because I've done this before where I implemented caching and the client couldn't see the changes when I pushed it up because they had an older version of the HTML.
If we come back here and hit refresh the second time you can't really tell but it will load slightly faster. If you ever have a need where you have some very slow loading HTML and CSS components then you can slide them right into a cache block and then this will be stored and then will be easier to access.
One caveat, many times I've been told by a client "my site's loading really slow." If they are technical they may even say "I think we need to implement some kind of caching" and sometimes that is the case. Other times I have seen it happen where they would have things such as very slow database queries and things like that. This will not fix those. This will not fix a slow database Querrey.
This type of caching deals with HTML with CSS and with the assets that are specific to the browser, a query is something that occurs server side. It's important to understand the difference.
In this guide we've covered quite a bit, we have talked about the difference between embedded Ruby with an equal sign versus without. We've talked about the mapping that occurs between the controller, the directory, the file name and how those all have to match up. We've taken a deeper dive in two partials and we have also looked at how we can call stylesheet tags and then even got into the syntax for implementing caching.
Great job. That was a lot of content in this guide but also in this section. You should now have a little bit better idea of how views work in Rails.