Search code examples
ruby-on-railsruby

Ruby on rails edit article comment


Objective is to edit an article comment.

However I can only figure out how to show a new comment field using <%= render 'form', comment: @comment %> or the comment text that is already there using <%= render 'comment', comment: @comment %> within edit.html.erb

Also tried changing <%= form_with model: [@article, @article.comments.build] do |form| %> to <%= form_with model: @article.comment do |form| %> but results in NoMethodError in Comments#edit

views/comments/edit.html.erb

<h1>Edit comment</h1>
<%= render 'comment', comment: @comment %>

comments/_form.html.erb

<%= form_with model: [@article, @article.comments.build] do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_area :commenter %>
  </p>
  <p>
    <%= form.label :body %>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.label :status %>
    <%= form.select :status, ['public', 'private', 'archived'], selected: 'public' %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

comments/_comment.html.erb

<p>
    <strong>Commenter:</strong>
    <%= comment.commenter %>
  </p>
  <p>
    <strong>Comment:</strong>
    <%= comment.body %>
  </p>
<%= link_to "Edit comment", edit_article_comment_path(comment.article, comment) %>
<p>
  <%= link_to "Destroy comment", [comment.article, comment], data: {
    turbo_method: :delete,
    turbo_confirm: "Are you sure?"
  } %>
</p>

comments_controller.rb

class CommentsController < ApplicationController

  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

  def create
    @article = Article.find(params[:article_id])
    @comments = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  def destroy
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
    @comment.destroy
    redirect_to article_path(@article), status: :see_other
  end

  def edit
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
  end

  def update
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
    # redirect_to article_path(@comment)

    if @comment.update(comment_params)
      redirect_to @comment
    else
      render :edit, status: :unprocessable_entity
    end
  end

  private
  def comment_params
    params.require(:comment).permit(:commenter, :body)
  end
end

articles_controller.rb

class ArticlesController < ApplicationController

  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to root_path, status: :see_other
  end

  private
  def article_params
    params.require(:article).permit(:title, :body, :status)
  end

end

views/articles/show.html.erb

<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li> <%= link_to "Edit", edit_article_path(@article) %></li>
  <li>
    <%= link_to "Destroy", article_path(@article),
                data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
  </li>
</ul>

<h2>Comments</h2>
<%= render @article.comments %>

<h2>Add a comment</h2>
<%= render 'comments/form' %>

new comment/_form.html.erb

<%= form_with model: @comment do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_area :commenter %>
  </p>
  <p>
    <%= form.label :body %>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.label :status %>
    <%= form.select :status, ['public', 'private', 'archived'], selected: 'public' %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

new articles_controller.rb

class ArticlesController < ApplicationController

  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
    @comment = @article.comments.new
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to root_path, status: :see_other
  end

  private
  def article_params
    params.require(:article).permit(:title, :body, :status)
  end

end

With the inclusion of @comment = @article.comments.new results in ActionController::UrlGenerationError in Articles#show No route matches {:action=>"edit", :article_id=>"11", :controller=>"comments", :id=>nil}, missing required keys: [:id] on 'show.html.erb' for an article. Without that and with the changes to form_with I am not able to create a comment - no errors are shown.


Solution

  • I found this post beacuse I had the same problem, and thanks to @Chiperific's response I found a solution for this. I have a similar situation, with object Shopping that has many Items objects.

    These are my new and edit views for Items

    <h2>Aggiungi articoli</h2>
    <div id="divform">
     <%= render 'items/form' %>
    </div>
    
    <h2>Modifica Articolo</h2>
    <div id="divform">
     <%= render 'items/form' %>
    </div>
    

    This is my _form.html.erb for Items

    <%= form_with model: [ @shopping, @item ], remote: true do |form| %>
      <div class="form-group">
        <%= form.label :name %><br>
        <%= form.text_field :name, class: 'form-control' %>
      </div>
      <div class="form-group">
        <%= form.label :quantity %><br>
        <%= form.number_field :quantity, class: 'form-control' %>
      </div>
      <div class="form-group">
        <%= form.label :unit_price %><br>
        <%= form.number_field :unit_price, step: :any, class: 'form-control' %>
      </div>
      <div class="form-group">
        <%= form.submit class: 'btn btn-primary' %>
      </div>
    <% end %>
    

    These are my actions in the Items_controller

    def new
      @shopping = Shopping.find(params[:shopping_id])
      @item = @shopping.items.new
    end
    
    def create
      @shopping = Shopping.find(params[:shopping_id])
      @item = @shopping.items.create(item_params)
      @item.update(item_params)
      redirect_to edit_shopping_path(@shopping)
    end
    
    def edit
      @item = Item.find(params[:id])
      @shopping = Shopping.find(@item.shopping_id)
    end
    
    def update
      @item = Item.find(params[:id])
      @shopping = Shopping.find(params[:shopping_id])
      if @item.update(item_params)
        redirect_to edit_shopping_path(@shopping)
      else
        render :edit, status: :unprocessable_entity
      end
    end
    

    With this link I click to edit an Item object:

    <%= link_to "Modifica", edit_shopping_item_path(item.shopping_id, item), class: 'btn btn-success btn-xs' %>
    

    And in my form I can create an Item object for the New action, and if I click on the link to edit an Item, I see the form fields populated with the item properties' values, and if I submit the form, the Item object is updated. Thanks @Chiperific for your explanation