Remember this handy diagram?
routes
In Rails we can easily generate a set of files that enable CRUD operations. What is CRUD you ask?
$ rails g scaffold Destination city:string country:string description:text
Let's open up our travel projects and add to them.
To get our CRUD goodies we use a command called scaffold. Let's scaffold a resource called "Destination" with 3 attributes: city, country and description.
$ rake db:migrate
Then run the rake command to migrate the data columns.
Where do we browse this page? /destinations of course!
Notice that the scaffold command generated many more views this time. We have a set of files that allow us to create, read, update and delete (CRUD) records in our database.
It also created a model (which we'll get to later in the course) and some assets. Let's open up the scaffolds.css.scss file and delete it's contents so they don't interfere with the styles we'll be writing.
Let's go back to layouts/application.html.erb and add a link to our destinations index to the navbar.
Remember how to do that using erb?
<ul class="nav navbar-nav">
<li class="active">
<%= link_to 'About', about_path %>
</li>
<li>
<%= link_to 'Destinations', destinations_path %>
</li>
...
What are a few cities/ countries you'd like to visit? Make sure to add several cities in the same country and add a few countries with multiple words like "New Zealand."
Anytime we see a file with an underscore preceding its name this is referred to as a "partial." It's essentially just a snippet of code that can be used repeatedly in our site. The "_form" file, for example, is used in both the edit and new pages to generate a form.
<%= render 'form' %>
_form is referenced or 'rendered' in
new.html.erb & edit.html.erb
Let's fancy up this Rails generated form we've been chatting about.
<div class="field form-group">
<%= f.label :city %><br>
<%= f.text_field :city, class: "form-control" %>
</div>
<div class="field form-group">
<%= f.label :country %><br>
<%= f.text_field :country, class: "form-control" %>
</div>
<div class="field form-group">
<%= f.label :description %><br>
<%= f.text_area :description, class: "form-control", rows: "5" %>
</div>
<div class="actions form-group">
<%= f.submit class: "btn btn-primary" %>
</div>
app/destinations/_form.html.erb
<h1>My Destination Wish List</h1>
<div class="row">
<% @destinations.each do |destination| %>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<h2><%= destination.city %></h2>
<h3<%= destination.country %></h3>
<p><%= destination.description %></p>
<div class="btn-group">
<%= link_to 'Show', destination, class: "btn btn-default" %>
<%= link_to 'Edit', edit_destination_path(destination), class: "btn btn-default" %>
<%= link_to 'Destroy', destination, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-default" %>
</div>
</div>
</div>
</div>
<% end %>
</div>
<%= link_to 'New Destination', new_destination_path, class: "btn btn-primary" %>
Let's give a facelift to our destinations index page. We'll update the h1, remove the table, add a 3 column grid, panels, and button groups.
app/views/destinations/index.html.erb
We can even make our show page a little nicer.
app/views/destinations/show.html.erb
<h1><%= @destination.city %>, <%= @destination.country %></h1>
<p> <%= @destination.description %></p>
<div class="btn-group">
<%= link_to 'Edit', edit_destination_path(@destination), class: "btn btn-default" %>
<%= link_to 'Back', destinations_path, class: "btn btn-default" %>
</div>
http://getbootstrap.com/components/#alerts
Let's style up our "notice" text with a Bootstrap alert "success" style. Note that this text only appears when we do something awesome like create a new destination.
It will say:
"Destination was successfully created."
Click back to the destinations index page and notice that the green box is still appearing. How would we get it to ONLY appear when there's text inside?
<% if notice != nil %>
<p id="notice" class="alert alert-success" role="alert">
<%= notice %>
</p>
<% end %>
This is really simple Ruby, right? Well, embedded Ruby. If notice isn't nil, or empty then display the paragraph tag. This logic can be added to the top of both the index and show pages.
<p id="notice"><%= notice %></p>
Before:
After:
Remember our good friend the query string? Instead of using it to change the page color we can use it to filter our destinations by country; a much more useful application.
def index
# @destinations = Destination.all
@destinations = Destination.where(country: params[:country])
end
If you have France in your database try navigating to:
/destinations?country=France
app/controllers/destinations_controller.rb
We've lost the ability to see ALL our countries ... and isn't that the point of the index page?
1. How can we write some logic to solve this?
Secondly we're not able to use lowercase variables in our query string. We want "France" and "france" to both work.
2. How can we make the filter work with words that begin with either an uppercase or lowercase letter?
Finally, we need to make sure that "New_Zealand", "new_zealand", "New_zealand" and "new_Zealand" filter correctly.
3. How can we ensure the filter works when our countries have multiple words?
def index
if params[:country] == nil
@destinations = Destination.all
else
@destinations = Destination.where(country: params[:country].titleize)
end
end
app/controllers/destinations_controller.rb
We have to write some logic to check if the country parameter is nil (or empty). If so, display all records. If not, display the specific country passed through the query string, and titleize it (meaning capitalize each letter in the string if there are multiple words).
<%= link_to "All", destinations_path %>
# /destinations
<%= link_to "New Zealand", controller: "destinations", country: "new_zealand" %>
# /destinations?country=new_zealand
<%= link_to "France", controller: "destinations", country: "france" %>
# /destinations?country=france
Let's put some links at the top of our destinations index page that set the query string for us. This way we don't have to keep typing it into the address bar.
Notice that countries consisting of 2 words can be combined with an underscore. Spaces in URLs are generally bad practice so let's try to steer clear.
<ul class="nav nav-pills" role="tablist">
<li><%= link_to "All", destinations_path %></li>
<li><%= link_to "New Zealand", controller: "destinations", country: "new_zealand" %></li>
<li><%= link_to "France", controller: "destinations", country: "france" %></li>
</ul>
Let's add some nav classes to our links so they sit nicely spaced out at the top of our page.
What if there were a dozen or so countries in your database? Manually typing out each link sounds like a pain in the butt.
How can we dynamically pull country names so that whenever we create a new country the link will magically appear in our link list?
Hint: Push countries into an array in the controller...
Extra Credit: Alphabetize the list of countries.
<ul class="nav nav-pills" role="tablist">
<li><%= link_to "All", destinations_path %></li>
<% @my_countries.each do |country| %>
<li><%= link_to "#{country}", controller: "destinations", country: "#{country}" %></li>
<% end %>
</ul>
@destinations = Destination.all
@my_countries = []
@destinations.each do |destination|
@my_countries.push(destination.country)
end
@my_countries = @my_countries.uniq
# Alphabetical
# @my_countries = @my_countries.uniq.sort_by{|country| country.downcase|}
app/controllers/destinations_controller.rb
app/views/destinations/index.html.erb
There's another way to filter our records. We could set up a new action in our destinations controller with a variable that selects only records that meet a certain criteria.
def show_france
@france = Destination.where(country:"France")
end
app/controllers/destinations_controller.rb
<div class="row">
<% @france.each do |oohlala| %>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<h2><%= oohlala.city %></h2>
<p><%= oohlala.description %></p>
</div>
</div>
</div>
<% end %>
</div>
Then create a corresponding view and route to match.
app/views/show_france.html.erb
config/routes.rb
get "france" => "destinations#show_france"
Let's install a gem that helps us store images in our database. Instructions are here:
Get Paperclip working so you're able to add, update, remove and show images associated with each destination. Then add some logic so images only display if they exist in the database. (read: do not display broken images)
Add Bootstrap and Paperclip to your Blog app
Scaffold blog, blog_post and comments.
Rip out those CRUD tables and play with using 3 or 4 column grids to display content. Add styles like we did in class today and get creative!