0

So I am trying to create a list of tasks within a goal and in order to complete them, I am creating a form_for with a hidden field that will update the value for the each task record when clicking submit. I'm trying to add ajax to the form so that when a user hits submit, it will update the record for the task and it will render a partial replacing this form with a div saying "complete!".

goals_controller.rb

def show
  @goal = Goal.where(user_id: current_user, id: params[:id]).first
  @goal_tasks = @goal.tasks

  # This is where the AJAX request will happen
  respond_to do |format|
    format.html {}
    format.js {}
  end
end

show.html.erb for Goals

    <% @goal_tasks.each do |task| %>
        <div class="complete-button">
            <% if task.completed == false %>
                <%= form_for([@goal,task], remote: true) do |f| %>
                    <%= f.hidden_field :completed , :value => true %>
                    <%= f.submit "Complete" , :class => "submit-complete" %>
                <% end %>
            <% else %>
                <%= render partial: "goals/taskcompleted"  %>
            <% end %>
        <div>
    <% end %>

show.js.erb

$(".complete-button").html("<%= j(render partial: 'goals/taskcompleted') %>")

The behavior that is happening is that if there are multiple tasks, there are multiple forms with unique id="edit_task_unique_id" in the html but since I'm grabbing the .complete-button class in the show.js.erb. It is replace all of the forms with the complete div. I've tried to somehow grab the unique id's the form creates with each task but no luck. Any help or feedback is appreciated!

1 Answer 1

1

Show is a GET type, what you do is a PUT type. You could use update method or create a custom one. You don't even need form, since you don't expect the users to type anything. On the top of that I would use :completed_at field with datetime type. If it's nil then it's not completed, if it's not nil (there is a datetime) then it's completed.

routes.rb

resources :goals do
  resources :tasks do
    member do
      patch :complete
    end
  end
end

goals/show.html.erb

 ....
 <%= render @tasks %> # this is a rails convention. Basically it iterates thru the @tasks and renders each task based on _task.html.erb which represents a single task.
 ....

tasks/_task.html.erb

#You use task instead of @task as you do within a @tasks.each |task| do block.

<%= task.name %>
<%= task.content %>
<% if task.completed_at.nil? %>
  #here you only need to pass task.id to the path (rails convention again)
  <%= link_to "Complete Task", complete_goal_task_path(id: task.id), action: :complete, remote: true, method: :patch %>
<% end %>

goals controller

def show
  # I don't know why you wrote this:
  @goal = Goal.where(user_id: current_user, id: params[:id]).first
  #It should be just this since you are on the show page (You should define the current_user in the view link helper that takes the user on this page.)
  @goal = Goal.find(params[:id])
  @tasks = @goal.tasks
end

tasks controller

def complete
  @task = Task.find(params[:id])
  @task.update_attribute(:completed_at, Time.zone.now)
  respond_to do |format|
    format.html
    format.js
  end
end

tasks/complete.js.erb

//You remove the completed task from the DOM (I guess you wanna do that.)
$("#task_<%= @task.id %>").fadeOut(400, function(){
  $(this).remove();
});
//For flash checkout a stackoverflow question. Something like "how to use flash in js.erb"

schema.rb

create_table "tasks", force: :cascade do |t|
  t.integer  "goal_id",  null: false
  t.string   "name"
  t.text     "content",      null: false
  t.datetime "deadline",     null: false
  t.datetime "completed_at"
  t.datetime "created_at",   null: false
  t.datetime "updated_at",   null: false
end
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for your help! I just have a few questions and I'm learning a ton from your feedback. So I wanted to ask what is the difference between using a boolean and the datetime (nil?) option?
With datetime you can also use the datetime (task.completed_at ) to show the user when he finished the task.
Your answer has gotten me much further than what I've been trying to do. It does everything that I wanted it to do. Thank you for you help! I've learned so much
Could you quickly explain what the member do patch :complete end does?
You need a route for every action and this creates a custom one. If you hit rake routes you see how it looks like. Basically it's the same like the update method. If you don't need the update method for anything else then you could use that one for completing the task, but I guess you wanna reserve that for changing some fields. This explains it pretty well: guides.rubyonrails.org/routing.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.