I am trying to create a button to change a model record attribute from false to true. I'm using a form_tag as follows:
=form_tag edit_goal_path(goal), method: :post do
=hidden_field_tag :purchased, value: true
=submit_tag "Purchase"
It's haml, but feel free to post suggestions with ERB. I'm getting the following error:
No route matches [POST] "/goals/4/edit"
Rails.root: /home/ben/rails_projects/hartwig
However, I already have the following route from resources:
PUT /goals/:id(.:format) goals#update
My controller looks as following:
def edit
#goal = Goal.find(params[:id])
end
def update
#goal = Goal.find(params[:id])
if #goal.update_attributes(goal_params)
redirect_to '/goals', notice: "Update successful!"
else
render '/'
end
end
def goal_params
params.require(:goal).permit(:item, :description, :picture, :purchased)
end
How do I get this to work? Or is there a better way to solve this?
Your question says:
I am trying to create a button to change a model record attribute from false to true
so why are you using a form for it? I think a better approach would be to create a link or button that will call an ajax method or a normal method with post route and update your attribute. You can achieve it by following these steps:
a. Create a route for your custom action where you'll update your attribute:
post 'purchase_update/:id' => "goal#update_purchase", as: update_purchase #post as you want to send your goal id
b. create your custom method inside your controller:
def update_purchase
#goal = Goal.find(params[:id])
#goal.update_attribute(:purchased, true)
respond_to do |format|
format.html {redirect_to your_path, notice: 'purchase updated'}
format.js {} #if you want to do something by ajax
end
end
c. Create your link that will call this method:
=link_to "Purchase", update_purchase_path(#goal), method: post
and if you want to do it by ajax then
=link_to "Purchase", update_purchase_path(#goal), method: post, remote: true
Another solution to your problem could be adding a new method to the Goal Controller:
in goals_controller.rb
def purchase
#goal.update_attribute(:purchased, true)
end
and also add on top (just add :purchase)
before_action :set_goal, only: [:show, :edit, :update, :destroy, :purchase]
in routes.rb change to
resources :goals do
member do
post 'purchase'
end
end
to add a new post routes to your goals
now you will have a purchase_goal_path that you can use in your view like this:
link_to 'Purchase', purchase_goal_path(#goal), method: :post
Related
When creating objects in ActiveAdmin I typically need to add multiple, and wish there was an option to add another object on the show page (which appears after submitting the new object).
I have been doing this model by model:
ActiveAdmin.register Color do
action_item :add, only: :show do
link_to "New", new_administration_color_path
end
end
Add this to admin/administration_color.rb
controller do
def create
create! do |format|
format.html { redirect_to admin_administration_color_path(resource, add_more: true) }
end
end
end
and some modified your code
action_item :add, only: :show do
link_to "New", new_administration_color_path if params['add_more'] == true
end
hoping someone can help. I've been searching through other "param is missing" questions, but can't seem to figure out what's wrong.
In my routes file I have this nested resource "actions"
resources :jobs do
resources :actions
end
The associated models. Ignore "action_reference". That's something else.
class Job < ActiveRecord::Base
has_many :actions
end
class Action < ActiveRecord::Base
belongs_to :job
belongs_to :action_reference
end
And I'm trying to create a new action by making a POST request using button_to
Here's the ActionsController
class ActionsController < ApplicationController
before_action :set_job
before_action :set_action, only: [:show, :edit, :update]
# GET /jobs/:job_id/actions/:id
def show
end
# GET /jobs/:job_id/actions/new
def new
#action = Action.new
end
# GET /jobs/:job_id/actions/:id/edit
def edit
end
# POST /jobs/:job_id/actions/
def create
#action = #job.actions.create(action_params)
if #action.save
flash[:success] = "Next step successfully added."
redirect_to jobs_path
else
flash[:danger] = #action.errors.full_messages.join(", ")
redirect_to new_job_action_path
end
end
# PATCH to /jobs/:job_id/actions/:id
def update
if #action.update(action_params)
flash[:success] = "Next step successfully updated."
redirect_to jobs_path
else
flash[:danger] = #action.errors.full_messages.join(", ")
redirect_to edit_job_action_path
end
end
private
def set_job
#job = Job.find(params[:job_id])
end
def set_action
#action = Action.find(params[:id])
end
def action_params
params.require(:action).permit(:action_reference_id, :job_id, :completed_at, :next_action_date)
end
end
And here's my button_to
<%= button_to answer[:text], post_action_jobs_path(#job), method: "post", params: { action: { action_reference_id: answer[:action_reference_id], job_id: #job_id , completed_at: answer[:action_completed_at], next_action_date: answer[:next_action_date] } }, type: "button", class: "btn btn btn-info btn-block" %>
I know the problem has something to do with the arguments I'm passing to the post_action_jobs_path in the view or the ones I'm passing to action_params in the controller, but I can't figure it out.
When I run this I get the error:
undefined method `permit' for "create":String Did you mean? print
I saw some thread a little while ago saying something about "action" being a reserved word in Rails, so you have to use something else, but if that's true I'm not sure how to go about that.
Any help would be greatly appreciated :)
Yes unfortunately this is due to "action" being an existing method inside rails controllers. It is used to get the name of the action that has been called. In this case "action" will equal the string "create".
One thing you could do would be to rename you Action model to JobAction and use params.require(:job_action)
Sadly I couldn't seem to find away around this, so I renamed my "actions" table and replaced every reference to "action" with a different word "step". Now it works!
I am implementing a simple voting system and by clicking on a button a +1 is added. For example, if a question has 5 votes, it will just increase. I have written the method already, but I am not sure how to execute it by clicking on a link_to. Do I need to reconfigure my routes?
questions_controller.rb
def self.ping
#question = Question.find(params[:id])
#question.increment!(:amplify)
render_to do |format|
if #question.save
format.html { redirect_to #question }
end
end
end
routes.rb
resources :questions
post '/ping' => 'questions#ping', as: 'ping'
Your routes will need to support an id:
post '/ping/:id' => 'questions#ping', as: 'ping'
Or better yet, if you want it to be scoped within the question:
resources :questions do
post '/ping' => 'questions#ping', as: ping
end
However, I don't think you want a class method ping in your questions_controller. I think you just want an instance method:
def ping
#question = Question.find(params[:id])
#question.increment!(:amplify)
if #question.save
render_to do |format|
format.html { redirect_to #question }
end
end
end
If that doesn't work, what errors do you see in the logs?
Further to CDub's answer, you'll likely benefit from a member route (2.10):
Routes
#config/routes.rb
resources :questions do
member do
post :ping
end
end
This should provide these routes:
http://yourapp.com/questions/:question_id/ping
View
This URL will only be accessible from POST, and would be best accessed using a link_to:
<%= link_to "+1", question_ping_path(question.id), method: :post %>
Controller
You don't need to declare class methods in a controller
#app/controllers/questions_controller.rb
def ping
#question = Question.find(params[:question_id])
#question.increment!(:amplify)
render_to do |format|
format.html { redirect_to #question }
end
end
.increment! saves the record :)
When I go through this with debugger, it does no hit the controller.
I get the following error:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
i.e. #user is nil. #user is nil even if I set it to find the first one via #user = #Users.first
I want to access the user via domain.com/id
Routes.rb
match ':id' => 'user#show'
Model
class User < ActiveRecord::Base
attr_accessible :userLink, :userName
end
Controller controllers/user_controller.rb
def Show
#user= User.find_by_id params[:id]
# Attempted this with User.first to see if param was broken
respond_to do |format|
format.html
format.json { render json: #user}
end
end
View file name views/user/show.erb
<script type="text/javascript">
<%= #user.id %>
</script>
You have a typo.
def Show in your controllers should be def show
You don't need to specify route like this
Just replace this
match ':id' => 'user#show'
with
resources :users, only: [:show]
This will generate default route for you for show method
use
match '/:id' => 'user#show'
but this is not a good approach as many of your routes will be disabled by that
like if you have www.yourdomain.com/profile will also goes to your show action.
also def Show here Show should be show
I would like to perform this action with the click of a link.
def index
#books = Book.all
end
def update_read_books
#books.each do |book|
book.update_attribute(:read, true)
end
end
How can I update mark all books as read?
Rails has an update_all method. See link.
def mark_as_read
Book.update_all(read: true)
redirect_to books_path
end
Setup up route to give you /books/mark_as_read
resources :books do
get :mark_as_read, on: :collection
end
Then in your view:
= link_to "Mark all as Read", mark_as_read_books_path
If you really want to be Restful, you can make your route a put/patch method. Don't forget to change your link to the method you choose.
If you want this to be an Ajax request, you can add a remote: true to the link:
= link_to "Mark all as Read", mark_as_read_books_path, remote: true
That will make it async. Then you need to handle that response in the controller.
def mark_as_read
Book.update_all(read: true)
respond_to do |format|
format.html { redirect_to books_path }
format.js
end
end
... and add a template inside /views/books/update_all.js.erb and add some jQuery to remove the notification. For example:
$('#notification_count').hide();
First of all define your method outside index method.
def index
#books = Book.all
end
def update_read_books
Book.update_all(read: true)
end
define route:
resources :books do
put :update_read_books, on: :collection
end
Then in your view:
= form_for update_read_books ,:remote => true do |f|
= f.submit "All Read"
Try with this. Hope it will help.