Rails 4 - Toggle Boolean to false on click - ruby-on-rails

I am trying to use Rails 4 to build an app.
The app has 3 models - one for Project, one for Scope and one for Finalise.
Finalise belongs to scope. Scope belongs to Project. Project accepts nested attributes for Scope (and Finalise) and Scope accepts nested attributes for Finalise.
In my finalise model, I have a boolean attribute for :draft. If draft is true, then I want to include a link on the project show page to toggle the :draft to false when the draft is complete.
A SO member has given me some suggestions in the attached (which I have tried to follow, but have not served to toggle the boolean attribute).
In my projects controller, I have:
def new
#authorise #project
#project = Project.new
#project.scope = Scope.new
#project.scope.finalise = Finalise.new
end
In my scopes controller, I have;
def new
#scope = Scope.new
#scope.finalises.build
end
In my finalises controller, I have a method (which was suggested by another SO user):
def toggle_draft
#finalise = Finalise.find(params[:id])
#finalise.draft = true
#finalise.save
redirect_to project_path(#project)
end
The finalise route has:
resources :finalises do
patch '/toggle-draft', to: 'finalises#toggle_draft', as: 'toggle_draft'
end
The finalise partial show (which is incorporated in the project show) has:
<% if #project.scope.finalise.draft == true %>
<div class="finalise"> <%= link_to 'Finalise this draft',
finalise_toggle_draft_path(:id => #finalise.id), :remote => true, method: :patch %></div>
<% end %>
When I try this I get an error that says:
undefined method `id' for nil:NilClass
Gabriel (below) has suggested that I might need to link through the associations (so #project.scope.finalise.id). I have tried that formulation and #project.scope.finalise_id. Neither of these work either.
When I try it as:
<% if #project.scope.finalise.draft == true %>
<div class="finalise"> <%= link_to 'Finalise this draft',
finalise_toggle_draft_path(:id => #project.scope.finalise.id), :remote => true, method: :patch %></div>
<% end %>
I get an error that says:
No route matches {:action=>"toggle_draft", :controller=>"finalises", :id=>1} missing required keys: [:finalise_id]
When I try:
I get an error that says:
<% if #project.scope.finalise.draft == true %>
<div class="finalise"> <%= link_to 'Finalise this draft',
finalise_toggle_draft_path(:id => #project.scope.finalise.id), :action => 'toggle-draft', :remote => true, method: :patch %></div>
<% end %>
No route matches {:action=>"toggle_draft", :controller=>"finalises", :id=>1} missing required keys: [:finalise_id]
When I try:
I get this error:
<% if #project.scope.finalise.draft == true %>
<div class="finalise"> <%= link_to 'Finalise this draft',
controller: "finalises", action: "toggle_draft",
finalise_id: #finalise_id,
:remote => true,
method: :patch %>
</div>
<% end %>
No route matches {:action=>"toggle_draft", :controller=>"finalises", :id=>"53", :finalise_id=>nil, :method=>:patch}
Thank you

in projects_controller
def show
#project = Project.find(params[:id])
end
in the projects show view
<%= link_to 'Finalise draft', finalise_toggle_draft_path(#project.scope.finalise.id), method: :patch
You should be able to use the named route and pass the finalise id which you can get from #project. You'll have to be careful how you get the finalise model in the finalises#toggle_draft method, check how your params are named, probably finalise_id, and not id. It would be good to read up on nesting resources and how to pass variables between controllers.

This is what ended up working (using Margo's guidance):
finalises partial:
<div class="finalise">
<%= link_to 'Finalise draft', finalise_toggle_draft_path(projectid: #project.id, id: #project.scope.finalise.id, finalise_id: #project.scope.finalise.id), method: :patch %>
</div>
Finalises_controller
def toggle_draft
#finalise = Finalise.find(params[:id])
#finalise.draft = false
#finalise.finalised = Time.now
#finalise.save
redirect_to project_path(Project.find(params[:projectid]))

#finalise_idis not defined... You can gat the id through the relation #project.scope.finalise.id
<% if #project.scope.finalise.draft == true %>
<div class="finalise"> <%= link_to 'Finalise this draft', finalise_toggle_draft_path(id: #project.scope.finalise.id), method: :patch %></div>
<% end %>

Related

creating a form to route a method inside controller

I am trying to create a form that redirects to a method called accept_item on the form controller. My issue is that it's not going through the accept_item method when I submit the form.
form_controller.rb
def accept
#form = Form.find(params['id'])
end
def accept_item
redirect_to inventories_path
end
accept.html.erb
<%= form_tag accept_item_forms_url do %>
<% #form.items.each do |i| %>
<%= label_tag 'Item Name' %>
<p><%= i.name %></p>
<%= label_tag 'Quantity' %>
<p><%= text_field_tag 'quantity', i.quantity %></p>
<% end %>
<%= submit_tag 'Accept', class: 'btn btn-default btn-about pull-left', data: {confirm: 'Are you sure you want to accept?'} %>
<% end %>
routes.rb
resources :forms do
collection do
get :accept_item, :as => :accept_item
end
end
Error Message
No route matches [POST] "/forms/accept_item"
The form_tag helper uses the HTTP POST method by default. You defined your routes with get:
get :accept_item, :as => :accept_item
You should use post instead:
post :accept_item, :as => :accept_item
Also I don't think you need the as: :accept_item part, unless you're going to use accept_item_url instead of accept_item_forms_url.
just remove :as => :accept_item, and change method to post
post :accept_item
You will get /foo when using :as => 'foo'.

How to make a link_to that redirects and submits a form

I have a modal that will serve as a disclaimer in my app and I need the link at the bottom of the modal that says "agree & continue" to toggle a boolean and input the time that the boolean was toggled. I have created the button as a form with hidden links but I cant seem to see how to make it submit the form AND redirect to the path i specify. Here is my link_to code now.
<% if current_user.user_record.blank? %>
<%= form_for :user do |f| %>
<% f.hidden_field :disclosure_acceptance, :value => true %>
<% f.hidden_field :disclosure_date, :value => Time.now %>
<%= link_to("Agree & Continue", user_steps_path(current_user), class: "btn btn-primary") %>
<% end %>
<% end %>
First, create a new method in your user_records_controller or at whichever controller action the form is displayed at:
def new
#user_record = current_user.build_user_record
end
Put this in your view:
<% if current_user.user_record.blank? %>
<%= form_for #user_record do |f| %>
<%= f.hidden_field :disclosure_acceptance, :value => true %>
<%= f.hidden_field :disclosure_date, :value => Time.now %>
<%=f.submit "Agree & Continue", class: "btn btn-primary") %>
<% end %>
<% end %>
Make a create action for the user_record that looks like this:
def create
#user_record = current_user.build_user_record(permitted_params)
if #user_record.save
redirect_to user_steps_path(current_user)
else
render :new
end
end
private
def permitted_params
params.require(:user_record).permit(:disclosure_acceptance , :disclosure_date) #etc
end
UPDATE
If you directly want to jump to the 'create' action, you can make your configuration like this:
Add a custom action to your routes:
post 'rate/:article_id' => 'user_records#create' :as => :create_user_record
#or whichever controller/action you wish
You should update the route on your form:
= form_tag create_user_record_path, :method=>'post' do
#etc
In order to create a user_record from the controller, you need to change things a little bit:
def create
current_user.user_record.create(:user_id => current_user.id, :disclosure_acceptance => params[:disclosure_acceptance] , :disclosure_date => params[:disclosure_date])
if current_user.user_record.save
#etc
end

Routing a resource to controller

How do you route a resource to its controller? I am using the resource in an edit page for a different model, so my actions are being routed to its model controller first.
This edit page requests from
class Grandstreamers::ResellersController < ApplicationController
def new
end etc...
I am trying to route the requests to here instead:
Grandstreamers::CertificatesController < ApplicationController
def new
end
def update
end etc...
This is my form under views/grandstreamers/resellers/edit.html.erb
<%= form_for #untrained, :url => certificates_update_path(#untrained) do |f| %>
<p> Trained Users </p>
<%= select_tag "certificate[user_id]", options_for_select(#current_trained.collect{|x| [x.name, x.id]}), {:multiple => :multiple} %>
<%= f.submit "Un-Train", class: "btn btn-large btn-primary" %>
<% end %>
<%= form_for #trained, :url => certificates_create_path(#trained) do |f| %>
<p> Non-Trained Users </p>
<%= select_tag "certificate[user_id]", options_for_select(#non_trained.collect{|x| [x.name, x.id]}), {:multiple => :multiple} %>
<%= f.submit "Train", class: "btn btn-large btn-primary" %>
<% end %>
My route is:
resources :certificates
Note that the
:url => certificates_create_path
is not correct and not working. Is there a way to specify this route in my routes.rb file or in my form? Thank you
EDIT
This is my resellers_controller edit() which is routes to first.
#trained = Certificate.new(params[:certificate])
#Trying to get to certificates_controller update. Then update the :attend to "No"
##untrained = Certificate.new(params[:certificate])
##untrained = Certificate.find(params[:id])
##untrained = Certificate.find_by_user_id(params[:id])
#untrained is not defined, I am not sure how to get it to just go to my certificate controller. For #trained I can define it since its not made yet and does not give me errors when it cant find a correct value.
My certificates controller which uses create() but cannot get to update()
def create
#trained = Certificate.new(params[:certificate])
if #trained.save
#trained.update_attributes(attend: "Yes")
end
redirect_to grandstreamers_resellers_path
end
def update
#untrained = Certificate.find(params[:id])
#untrained.update_attributes(attend: "No")
redirect_to grandstreamers_resellers_path
end
Major Issue
The instance variable #trained and #untrained need to be defined somehow in reseller_controller. What can I define them as to load the edit page?
Part Solution
I defined this is in my resellers_controller and it loads the edit page now.
#untrained = User.find(params[:id])
Now I get this error:
No route matches [PUT] "/certificates.1"
I believe the problem is you need to let routing know the full name to the controller.
From the Rails routing guide:
scope module: 'Grandstreamers' do
resources :certificates
end
Use these paths when creating the form:
<%= form_for #untrained, :url => certificate_path(#untrained), :method => :put do |f| %>
<%= form_for #trained, :url => certificates_path, :method => :post do |f| %>
use this his routes.rb file
namespace :grandstreamers do
resources :certificates
end

getting error missing required keys: [:id] on a form_tag

I am rendering a form in a view in the instance that an user has not verified their email. This form also allows them to put in their email preferences.
Here is the form.
<%= form_tag update_email_and_sports_url, class: "form-signin", method: 'patch' do %>
<h2>Please confirm your email</h2>
<%= email_field_tag :email, nil, class: "input-block-level", placeholder: current_user.email %>
<h2>We'll email you when opportunities arise to play any of the sports you select below:</h2>
<%= check_box_tag :basketball, checked = false %>
<%= check_box_tag :volleyball, checked = false %>
<%= check_box_tag :soccer, checked = false %>
<%= check_box_tag :football, checked = false %>
<%= check_box_tag :hockey, checked = false %>
<%= check_box_tag :kickball, checked = false %>
<%= check_box_tag :softball, checked = false %>
<%= f.hidden_field_tag :user_id, :value => current_user.id %>
<div>
<%= submit_tag "Submit", class:"btn btn-large btn-success" %>
</div>
The view will not even render and I'm getting this error:
No route matches {:controller=>"users", :action=>"update_preferences"} missing required keys: [:id]
It's saying the error is in my form_tag line. Here is my route.
patch '/user/preferences/:id' => 'users#update_preferences', :as => 'update_email_and_sports'
And my controller
def update_preferences
current_user.email = params[:email]
preference = Preference.new(preference_params)
redirect_to root_url
end
def preference_params
params.require(:preference).permit(:user_id, :basketball, :football, :softball, :soccer, :kickball, :volleyball, :hockey)
end
Since you don't need id param to search for user in this action, you should remove it from your route to this action in routes.rb:
patch '/user/preferences' => 'users#update_preferences', :as => 'update_email_and_sports'
The error is raised because route with id included expects :id parameter, which you don't provide calling this route's helper.
Why is this happening?
The form_tag path (or in your case, url) needs to take an id (or an object) of the user you want to update... Something like:
<%= form_tag update_email_and_sports_url(current_user), class: "form-signin", method: 'patch' do %>
This will solve the error of missing required keys: [:id].
Potentially better idea...
However, if you're using current_user always for this update, you may consider dropping :id from your route... Then you'd leave the form_tag in your question as-is.

Ruby on Rails 3: Toggle Boolean with Forms

Users are able to send each other messages on my Ruby app. The table is as followed:
t.string "content"
t.integer "from_id"
t.integer "reply_to_id"
t.boolean "read", :default => false
I have recently added the boolean, :read, and I have the form linked to messages in their inbox. When they click "Mark Read" (the link provided via the form) I want it to toggle :read => true.
I am currently unable to accomplish this. This is my current setup.
_mark_read.html.erb
<%= form_for(:message, :mark_read => {read: true}) do |f| %>
<div><%= f.hidden_field :read %></div>
<%= f.submit "Mark Read", class: "btn-link" %>
<% end %>
Class MessagesController < ApplicationController
def mark_read
#message = current_user.messages.build
#message.toggle!(:read)
end
config/routes.rb
match '/mark_read' => 'messages#mark_read', :via => :post, :as => :mark_read
When I click "Mark Read", here is my debug info:
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: ✓
authenticity_token: ---
message: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
read: 'false'
commit: Mark Read
controller: static_pages
action: connect
I can see it is not even using my controller... and even so; I don't think I have it set up correctly.
Any advise as to how this should be set up would be kindly appreciated.
Believe it or not.. I didn't even need a model, nor controller :D As I said it is rendered within a paginated set. This paginated set is defined at #inbox_items identified as inbox_item.id.
Here is the form that works without any need of a model or controller. It is simply defined in config/routes.rb as:
match '/mark_read' => 'messages#mark_read', :via => :post
The form:
<ul class="share-actions" id="mark_read<%= inbox_item.id %>">
<%= form_for :mark_read do |f| %>
<%= f.check_box inbox_item.toggle!(:read) %>
<%= f.submit "Mark Read", class: "btn btn-link" %>
<% end %>
</ul>
Ammusing, huh? :D
More information in-case you are curious. The base Class of rendered #inbox_items, is Class Message. Messages define all of user's private posts. In the User class I begin to set up how #inbox_items will be defined:
Class User
def inbox
Message.from_users_inbox(self)
end
Then I build the rest:
Class Message
def self.from_users_inbox(user)
"SELECT best_friend_id FROM friendships WHERE new_friend_id = :user_id"
where("user_id = :user_id OR reply_to_id = :user_id", user_id: user.id)
end
MemberpagesController
def inbox
#message = current_user.messages.build
#inbox_items = current_user.inbox.paginate(page: params[:page])
end
feeds/_inbox.html.erb
<% if #inbox_items.any? %>
<%= render: 'feeds/inbox_item', collection: #inbox_items %>
<%= will_paginate #inbox_items %>
<% end %>
And finally:
feeds/_inbox_item.html.erb
<div id="<%= inbox_item.user.name %>_<%= inbox_item.id %>">
...
<ul class="share-actions" id="mark_read<%= inbox_item.id %>">
<%= form_for :mark_read do |f| %>
<%= f.check_box inbox_item.toggle!(:read) %>
<%= f.submit "Mark Read", class: "btn btn-link" %>
<% end %>
</ul>
...
And that did it! :)

Resources