I need to create a custom page with a controller action in ActiveAdmin. I need a form on this page that checks if the phone number exists and output a list of records with this telephone.
I have the following:
ActiveAdmin.register_page 'Phone' do
page_action :check, method: :post do
#resources = Telephone.where(number: params[:number]).
joins(:phoneable).map(&:phoneable)
end
# A form for #check action
end
But no idea what to do next. How to render the form properly (I would prefer to use AA DSL, rather than rendering a partial)? How to define routes?
Related
I have a nested resource in Active Admin which shows all SupportSessions (like meetings) for a particular SupportAllocation (which defines a teacher-pupil relationship):
ActiveAdmin.register SupportSession do
belongs_to :support_allocation
On my index page, I'd like a button at the top that the user can click (like they can click 'New Support Session'), which then executes a custom method which sends an email using ApplicationMailer. There is no 'page' where the button goes to - it just redirects back to the current index page with a message indicating success or otherwise.
I can get the 'Request Approvals' button to appear on the index page with this code:
# Adds a new button
action_item only: :index do
link_to 'Request approvals', send_for_approval #custom method
end
But obviously this raises an exception:
undefined local variable or method `send_for_approval'
Because I haven't defined this custom method anywhere.
I've created my mailer class but I'm not sure how to 'connect' it to my resource. I realise this will involve a new route of some sort, or use the existing 'put' method. I'd need to hand the current SupportAllocation ID to the method, so it knows which records/data to deal with when it sends email messages.
Any tips on how do I do create a button that runs this custom method + parameter? Where do I define this new custom method?
Thank you for your help.
You should code the action first, in your file:
member_action :send_for_approval, method: :patch do
# send your email here you can access to :resource which is your record
YourMailer.with(support_session_id: resource.id).your_email.deliver_now
# redirect to your admin index or show path
end
Then rails routes will give you the correct path to it so you can pass it to action_item, it will look something like that:
action_item only: :index do
link_to 'Request approvals', send_for_approval_admin_support_session_path, method: :patch
end
References:
https://activeadmin.info/8-custom-actions.html#member-actions
https://activeadmin.info/8-custom-actions.html#action-items
I am still new to rails and running into a rather weird problem (at least from my perspective):
There is a view with a form and a route and a method in a controller. Somehow the method gets called straight away instead of rendering the view, waiting for input and the passing that on to the method.
This is what it kinda looks like:
Controller
class Some::ThisController < ApplicationController
def method_a
variable_a = params[:variable_a].to_time
variable_b = #other stuff
#variable_c = # do stuff with the variable_a & variable_b
end
View (method_a.rb)
= form_tag this_method_a_path do
= text_field_tag :variable_a
= text_field_tag :variable_a
= submit_tag 'Apply'
Routes (some.rb)
The::Application.routes.draw do
namespace :some do
# leave all the unimportant stuff
match this/method_a => this#method_a, :as => :method_a
So what is my issue?
The view is not being rendered - I only get:
undefined method `to_time' for nil:NilClass
When I rename the method the view renders fine.
What do I want?
The view to render, so I can fill out the form and submit it and then have the method return whatever is in #variable_c.
I cant figure out what goes wrong. Maybe it is too late today...
You need two separate controller methods, one to render the view and one to accept the submission of the form. params[:variable_a] will be unavailable when you're in method_a because the form hasn't been submitted yet, it's just being rendered!
Try this:
class Some::ThisController < ApplicationController
def method_a
# nothing, just let Rails render the method_a view
end
# this will accept the submission of the form
def method_b
variable_a = params[:variable_a].to_time # this will now be available because the user has submitted the form
variable_b = #other stuff
#variable_c = # do stuff with the variable_a & variable_b
Add the new method to the routes:
The::Application.routes.draw do
namespace :some do
# leave all the unimportant stuff
get 'some/method_a' => 'some#method_a', :as => :method_a
post 'some/method_b' => 'some#method_b'. :as => :method_b
And now your view will be:
= form_tag method_b_path do
= text_field_tag :variable_a
= text_field_tag :variable_b
= submit_tag 'Apply'
When the user hits the submit button the params :variable_a and :variable_b will be POSTed to the method_b action in your controller and you will be able to call params[:variable_a] or b.
If you don't understand how this works maybe this will help:
User visits the path GET /some/method_a and your app receives the request at the method_a action in your controller and responds by rendering the method_a.html.erb view.
The form is rendered and the user fill out the form and clicks submit sending a new request to the method_b action in your controller. Along with this request the params from the text_fields are included and you can then use the for your computations.
Hope that's clear enough.
Well, this much depends on how you call your route.
Is it a get request? A url-defined "variable_a"? In this case, you are not defining it in you route, so it goes to null...
Or is it a post/patch request? In which case you would most probably have a form posting to a route and hence to a controller method... But you need first to render the form.
So, what you should have is:
A method in your controller to call your form view as 'get'
In your case, as simple as adding in your routes:
match "this/method_a", to: "controller#draw_form", via: 'get'
In your controller
def draw_form
render "method_a"
end
and then, when your form posts to the same url ("this/method_a" in your routes), there will be parameters to be processed by your method_a action in your controller.
My rails app has a single CustomerSelectionController, with two actions:
index: which shows a form where the user can enter customer information and
select: which just displays a static page.
class CustomerSelectionController < ApplicationController
def index
end
def select
end
end
I've created an entry in my routes.rb file:
resources :customer_selection
and the form in the index view looks like:
<h1>Customer Selection</h1>
<%= form_tag("customer_selection/select", :method => "get") do %>
<%= submit_tag("Select") %>
<% end %>
however when I click on the Select button in the browser, all I get is:
Unknown action
The action 'show' could not be found for CustomerSelectionController
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
Yes you have. That's what resources does. It defines the seven default RESTful routes: index, show, new, create, edit, update and destroy. When you route to /customer_selection/select, the route that matches is "/customer_action/:id", or the "show" route. Rails instantiates your controller and attempts to invoke the "show" action on it, passing in an ID of "select".
If you want to add a route in addition to those, you need to explicitly define it, and you should also explicitly state which routes you want if you don't want all seven:
resources :customer_selection, only: %w(index) do
collection { get :select }
# or
# get :select, on: :collection
end
Since you have so few routes, you can also just define them without using resources:
get "/customer_selection" => "customer_selection#index"
get "/customer_select/select"
Note that, in the second route, the "customer_select#select" is implied. In a route with only two segments, Rails will default to "/:controller/:action" if you don't specify a controller/action.
Is it possible to create a multistep form with ActiveAdmin?
If not, is it possible to just add another page that it redirects to after submitting the form (one that is not the default index, show or form pages)?
I've been fretting with this issue myself. I found that you can add your own pages using collection actions in your ActiveAdmin file. Say your model is called MyModel, you would add this to your ActiveAdmin my_model.rb file.
# GET /admin/my_model/page1
collection_action :page1, :method => :get do
render 'admin/page1'
end
# POST /admin/my_model/page1
collection_action :page1, :method => :post do
# Do your form processing
redirect_to test_admin_my_model_path
end
# GET /admin/my_model/page2
collection_action :page2, :method => :get do
render 'admin/page2'
end
You would then need to create a view at /app/views/admin/page1.html.erb and page2.html.erb
you'll probably want a member action if youre working on a single instance of a model
a form would need an action which operates on a single resource
http://activeadmin.info/docs/8-custom-actions.html#member_actions
I haven't had to do it within active_admin yet, but I would check out the railscast on multistep forms and combine it with active_admin's collection actions. Essentially, keep it model heavy but have a single custom action that handles the validation, progression, and creation of the model within the form.
I have this setup in my routes:
namespace :admin do
resources :posts
end
so in my admin/posts_controller.rb I have my new, create and edit actions.
I want to re-use my new and edit view page somehow, b/c the page has allot of custom javascript etc. for the form and I don't want to repeat myself.
How can I do this?
i.e. for the edit page, I have to pre-populate the form fields and for the new page it is to be empty.
For a new page, it should post to the 'create' action, and for the edit I am thinking it should post to a different 'update' action (which is a PUT request as per my rake routes)?
Rails is pretty clever, a form like
<% form_for post do |f| %>
<% end %>
will post to the create action if post.new_record? == true and to the update action otherwise.
So, you can put the form in a partial, and render it inside your new/edit views, which will probably have different headings and copy.
Alternatively you can just have a single view and perform your own logic based on post.new_record? - but I'd advise against this because you'll end up with an unnecessarily complex view.