I have this semantic nested form to destroy a nested object in Active Admin.
= semantic_form_for book.chapters.new, url: {controller: "admin/products", action: :remove_chapter} do |f|
= f.inputs do
li
label Select
= f.collection_select :chapter_product_id, book.chapter_products.order(:name), :id, :name, include_blank: 'Select Chapter'
= f.actions do
= f.action :submit, label: "Remove Chapter"
It works fine, but I feel it is wrong to use a semantic_for_for book.chapters.new since I am not actually creating anything.
I am just making a dropdown select based on a collection to remove an Object.
Just looking to see if there is a better way to do this than how I am doing it now.
I was thinking of just adding a delete button in a table_for, but I can't seem to properly direct to the member_action without getting a route error even though I use the exact same route as the controller in the semantic_form (and added method: :post).
Tried a whole bunch of options from this thread with no success -- https://github.com/activeadmin/activeadmin/issues/53
Thanks!
I just thought of it from a different simpler angle.
I linked to the nested resource's destroy directly, instead of trying to make a member_action in the mother object.
table_for product.chapters do
...
column(:delete) { |c| link_to 'Delete', admin_chapter_path(c), class: 'member_link', method: :delete, 'data-confirm': "Are you sure you want to delete this?" }
end
And since I wanted to be redirected back to the same page I came from I just hacked into the controller method on the nested Object's page and managed my redirects from there.
ActiveAdmin.register Chapter do
actions :index, :show, :destroy
controller do
def destroy
...
redirect_back fallback_location: admin_chapter_path, notice: "Message"
end
end
end
Related
I have an Approved column in a database which is false by default and might become true on "Approve" button click.
That's what this button look like at the moment:
<%= link_to('Approve It', #comment_path, method: :update) %>
But it raises an exception:
No route matches [POST] "/books/4/comments/6
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
redirect_to '/dashboard'
end
# config/routes.rb
resources :books do
resources :comments
end
How can I fix it?
link_to has to point to an existing route/action, with a proper method name. There is no :update HTTP method.
FYI: Approve action doesn't seem like it belongs to the #update method/action. You might want to extract it to a separate route like so:
resources :books do
resources :comments do
post :approve, on: :member
end
end
this is more idiomatic/common approach in Ruby because #update is usually preserved for more general object updates.
For this you will need to change :method argument value to :post and update your route/#comment_path.
Rails-ujs event handlers - this link might be useful for understanding how it works behind the scenes.
Controller Namespaces and Routing
Post / Update actions require forms
You're using a link_to. This is good for GET requests, but is no good for POST/PATCH/UPDATE requests. For that you'll have to use a form in HTML. Luckily Rails offers some short cut. You can use something like button_to:
<%= button_to "Approve", { controller: "comments", action: "update" }, remote: false, form: { "id" => #comment.id, "approved" => true } %>
This creates a form for you. Which will come with CSRF protection automatically. You can style the button however you like.
or you could use a link to:
<%= link_to comment_approved_path(#comment), method: :put %>
but then you would need to create a separate "approved" action in your controller, and a separate route to reach it.
(The above code has not been tested).
#html
<%= link_to "Approve It", book_comment_path(#comment), method: 'put' %>
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
#comment.save
redirect_to '/dashboard'
end
I'm creating rails product controller to hide the product without deleting user grid panel using Boolean values, but I'm trying so many times, it's not working. I'm just show my code..please help where i'm missing...
I'm using ruby 2.4.1, rails 5.3.2 please help me.
rails route is,
post 'products/hide', to: 'products#hide'
products controller function is,
def hide
#product = Product.find(params[:id])
#product.hidden = true
flash[:notice] = 'you have successfully hide your product'
redirect_to suppliers_index_path
end
view form is,
<%= link_to 'delete', product, method: :hide, data: { confirm:
"Are you sure hide this #{product.Product_name} ?" } %>
products table migration are
class AddHiddenToProducts < ActiveRecord::Migration[5.2]
def change
add_column :products, :hidden, :boolean, :default => false
end
end
please help to resolve this issue.
There's a handful of things wrong with your code.
The most important issue is that you aren't saving your changes.
Your controller code which reads #product.hidden = true only assigns the value to the ruby object in memory. It does not save that change to the database. I believe this is the code you want in your controller action.
def hide
#product = Product.find(params[:id])
#product.update_attribute(:hidden, true)
flash[:notice] = 'you have successfully hide your product'
redirect_to suppliers_index_path
end
Your link_to is attempting to use a non-standard HTTP method ("hide"). I can tell that you're interpreting this as the controller method you want to hit, but that's not what method: means here. The method: parameter in link_to allows you to specify which HTTP method you want to send along with the request. HTTP methods are a standard that function as "verbs" that tell the server what you want to do to the resource you're requesting. Since you're only updating one attribute on the object, the Mozilla Foundation would instruct you to use the PATCH method for your request here.
NOTE: We'll be updating the route to match this in the next point.
COMMENT: Whoever named the product's "name" column products.Product_name needs to have a code review meeting with someone. But anyway...
<%= link_to 'delete', product, method: :patch, data: { confirm:
"Are you sure hide this #{product.Product_name} ?" } %>
Your routes need to be mapped to respond to a PATCH method request. You could use the format you use in your example, but most of the time you want to use "resourceful" routes.
resources :products do
member do
patch :hide
end
end
I will add that you don't have to use the PATCH method at all. You could also use put :hide + method: :put or post :hide + method: :post. They just need to match.
The key method passed to link_to call doesn't define controller method, but HTTP method, so it should be :post actually. You specify controller method by setting correct path/http method. You can achieve your goal by:
<%= link_to 'delete', { controller: products, action: :hide, id: product }, method: :post, data: { confirm: "Your confirmation" } %>
But I would go a little bit further and provide correct named route, assuming you have resources :products somewhere in your routes:
resources :products do
member do
post :hide
end
end
and in your view:
<%= link_to 'delete', [:hide, product], method: :post, data: { confirm: "Confirmation text" } %>
I have a rails app!
I'd like to create a form for a product model, where users can choose a product category first and then can fill the form out.
This would be easy, but I'd like to show them different attributes based on the chosen category. Something like if they choose book category, then they will have fields like title, author, published_at, but if they choose shoes category then they can fill out the size, color and type fields.
I saw afew tuts about dynamic forms, but as far as I understand it, I don't need that since the form fields will be predefined and users won't be able to add extra fields.
What is the good approach in this case? Should I create more different models like (shoes,books, etc.) or something else?
Should I create more different models
No, I don't think that's necessary.
What you'd be best doing is using ajax to populate the form on category change. This would require some configuration, but will make it the most efficient and secure:
#config/routes.rb
resources :products do
put :new, on: :new #-> url.com/products/new
end
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
if request.get?
#product = Product.new
#categories = Category.all
elsif request.put?
#category = params[:product][:category_id]
#attributes = ...
end
respond_to do |format|
format.js
format.html
end
end
end
#app/views/products/new.html.erb
<%= form_for #product do |f| %>
<%= f.collection_select :category_id, #categories, :id, :name, {}, { data: { remote: true, url: new_product_path, method: :put }} %>
<div class="attributes"></div>
<%= f.submit %>
<% end %>
#app/views/products/new.js.erb
$attributes = $(...); // need a way to create form elements from #attributes
$("form#new_product .attributes").html( $attributes );
Something important to note is that Rails select & check elements allow you to use the data-remote attribute to send an ajax call to your controller on change.
Not much documentation about it, playing around with the above code should get it to work.
I this view is currently in the views/projects/show.html.erb file however I want it to use the website controller for deleting this file:
<%= link_to 'Delete', #website, :controller => 'website', :action => 'delete', method: :delete, data: {confirm: "Are you sure you want to delete this asset?"}%>
It returns the error 'Could not find action destroy in the ProjectsController'. Also i don't have #website defined in the projects controller so should I be using something else? Or am I still able to access it because it is defined in the websites controller.
#controllers/websites_controller.rb
class WebsitesController < ApplicationController
def new
#project = Project.find(params[:project_id])
#website = #project.assets.build(:type => 'Website', :project_id => Project.find(params[:project_id]), :asset_number => #project.assets.size + 1)
end
def create
#website = current_user.assets.build(website_params)
#website.update_attributes(:project_id => #project)
if #website.save
flash[:notice] = "Asset successfully added."
redirect_to(:controller => 'projects', :action => 'show', :id => #website.project_id)
else
render(:action => 'new')
end
end
def delete
#website = Asset.find(params[:id])
end
def destroy
asset = Asset.find(params[:id]).destroy
flash[:notice] = "The asset '#{asset.title}' has been destroyed Successfully."
redirect_to(:controller => 'projects', :action => 'index')
end
private
def website_params
params.require(:website).permit(:id, :project_id, :asset_number, :title, :type, :url, :page_rank, :rev_company ,:social_pages)
end
end
If you are using this link on the show page for projects then #website will not be available unless it is defined in the projects controller.
That said, if there is some relationship between the project and the website, you could use that as opposed to defining #website in your projects controller.
Also, as far as your link_to is concerned, I do not believe that you can specify controller and action in the link_to like that. Instead, you should use the path to #website. Which should make your link_to look something more like this:
<%= link_to "Delete", website_path(#website), method: :delete, data: {confirm: "Are you sure you want to delete this asset?" %>
However, the model that your websites_controller appears to handle is actually an Asset. Without seeing your routes it is hard to guess how you have set them up, but assuming that you do something like
map.resources :assets, :controller => 'websites'
in your routes. Then in your link_to instead of using website_path(#website) you would likely use asset_path(#website).
Generally speaking, it is rarely a good idea to defy rails convention by naming things inconsistently from your model in ruby. If your Asset model uses single table inheritance or you are implying something like single table inheritance and are using controllers to separate responsibilities, then this may perhaps be an exception, but you will still need to be careful to ensure you are mapping to the correct place in your routes.
You may want to read up on the rails guide for routing, as it is a very good resource and explains pretty well how destroy gets mapped, which in turn explains why the link_to for it looks the way that it does.
#website available in the show action is the the one defined in the projects controller because it is he one rendering the current html page.
Therefore the one you wish to delete is not available at the moment.
I am working on a rails 3.2.9 project with a Mongoid back end. I am trying to create posts and if the post is missing a title and content, the model should fail to save which it does properly. When an object is fixed and passes validation, I save it and now create a new post. If I try to save this one with the missing items, the error count just seems to append the previous one even though these are two different objects.
I am using the error_messages helper from dynamic_form to display my errors. Any Ideas?
Here is a sample error message:
152 errors prohibited this post from being saved
There were problems with the following fields:
Title can't be blank
Title can't be blank
Title can't be blank
Title can't be blank
Title can't be blank
The list continues on quite a way. Code is just basic form code:
= form_for #post,:as => :post, :url => post_path(:id=>#post.id), :method => :put do |f|
=f.hidden_field :is_question
#content
.title-page
%h1
New Post
= f.error_messages
In the model I have:
validates_presence_of :title
validates_presence_of :content
and my controller method:
def publish
#post = Post.first(conditions:{_id:params[:post_id]})
#post.assign_attributes(params[:post])
#post.published=true
if #post.save
redirect_to "/"
else
#video = Video.new
render action: "new"
end
end