-
Notifications
You must be signed in to change notification settings - Fork 0
13 Posts Edit
In this unit, we will cover the following topics:
- Partials
- Private controller methods
In order to edit existing posts, we'll need an edit
method in PostsController
. Like show
, it doesn't need to do anything apart from assigning the existing record to an instance variable.
app/controllers/posts_controller.rb
def edit
@post = Post.find params[:id]
end
As for the view, at this point it doesn't need to be any different from new.html.erb
. We may want to have some language or other elements unique to each page at some point, so rather than just rendering :show
from the edit
action, we'll extract the existing form into a partial, and render that partial in both new.html.erb
and edit.html.erb
Create a new partial: app/views/posts/_form.html.erb
Paste the contents of new.html.erb
into the new partial.
app/views/posts/_form.html.erb
<%= form_for @post do |f| %>
<fieldset>
<%= f.label :title %>
<%= f.text_field :title %>
</fieldset>
<% if f.object.link? -%>
<fieldset>
<%= f.label :link %>
<%= f.text_field :link %>
</fieldset>
<% end -%>
<% if f.object.text? -%>
<fieldset>
<%= f.label :body %>
<%= f.text_area :body %>
</fieldset>
<% end -%>
<%= f.hidden_field :post_type %>
<%= f.submit class: 'btn btn-default' %>
<% end %>
Replace the contents of new.html.erb
with a single render
expression.
app/views/posts/new.html.erb
<%= render 'form' %>
Now create edit.html.erb
with the same render
expression.
app/views/posts/edit.html.erb
<%= render 'form' %>
Let's give ourselves a way to reach the edit page now.
On posts/index
, let's add a list of "action" links for each post. For the moment, the only "action" will be edit
.
app/views/posts/index.html.erb
<li>
<div class="title"><a href="<%= post_url(post) %>"><%= post.title %></a></div>
<div class="tagline" title="<%= post.created_at %>">submitted <%= time_ago_in_words post.created_at %> ago</div>
<ul class="actions">
<li><%= link_to 'edit', edit_post_path(post) %></li>
</ul>
</li>
Now let's do something similar on pages/show
. We'll place it below the body
.
app/views/posts/show.html.erb
<article class="post">
<div class="title"><a href="<%= @post.link %>" target="_blank"><%= @post.title %></a></div>
<div class="tagline" title="<%= @post.created_at %>">submitted <%= time_ago_in_words @post.created_at %> ago</div>
<section class="body"><%= @post.body %></section>
<ul class="actions">
<li><%= link_to 'edit', edit_post_path(@post) %></li>
</ul>
</article>
This will look pretty awful if we don't add some styles, so throw this at the bottom of the stylesheet:
app/assets/stylesheets/style.scss
ul.posts, article {
ul.actions {
padding: 1px 0;
list-style-type: none;
li {
display: inline-block;
padding-right: 4px;
font-size: x-small;
line-height: 1.6em;
white-space: nowrap;
a {
color: #888;
font-weight: bold;
padding: 0 1px;
text-decoration: none;
}
}
}
}
Now that we have our edit
links, let's try them out. Be sure to try from both posts/index
and posts/show
. Notice that our FormHelper
changes the text on the submit button.
If you go as far as submitting the edit form, you'll see a jarring reminder that we have no update
method in our controller. Let's add that now.
Implement PostsController#update
.
To start, we'll find the existing post. Unlike in create
, where we used a local variable post
until we actually needed an instance variable, we're just going to use an instance variable from the get-go.
app/controllers/posts_controller.rb
def update
@post = Post.find params[:id]
end
Next we'll attempt to update the post from params
. Once again, we'll explicitly permit each attribute.
app/controllers/posts_controller.rb
@post.update params.require(:post).permit(:title, :link, :body, :post_type)
Speaking of which, in the interest of not repeating ourselves let's extract that chain of methods on params
into a private method. At the bottom of the controller, add the following:
app/controllers/posts_controller.rb
private
def post_params
params.require(:post).permit(:title, :link, :body, :post_type)
end
Now we can just call post_params
in both create
and update
.
app/controllers/posts_controller.rb
12 def create
13 post = Post.new post_params
# ...
31 def update
32 @post = Post.find params[:id]
33 @post.update post_params
As we did in create
, we're going to want to handle update failures differently from successful updates. So let's throw that @post.update
into an if
condition and proceed with redirects, renders, and flash messages as before. The only real differences: We may want to change the specific language in our flash message, and we want to render :edit
upon failure, not :new
.
app/controllers/posts_controller.rb
def update
@post = Post.find params[:id]
if @post.update post_params
redirect_to posts_path, flash: { notice: 'Your post was updated successfully.' }
else
flash.now[:error] = @post.errors.full_messages
render :edit
end
end
When you can successfully update posts in various ways, can trigger all of the appropriate flash messages, and you make sure you didn't break create
, it's time to commit.
We load a post record based on params[:id]
in several of our controller actions. Let's add another private method to do this.
def find_post
@post = Post.find params[:id]
end
Rather than directly calling this method from within each appropriate controller action, we can add a before_action
(formerly before_filter
in Rails 4+) to call the method before specific actions.
before_action :find_post, only: [:show, :edit, :update]
We can then remove @post = Post.find params[:id]
from the individual controller actions.
$ git add .
$ git commit -m "Edit and update posts."