Multistep form with ActiveAdmin? - ruby-on-rails

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.

Related

ActiveAdmin - custom page actions

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?

how to add a `preview` action to resources?

I am looking for the right approach to include a preview action between new and create actions.
Let's assume I have the following:
resources :users
By default, when the form is submitted:
if new, call create action;
if edit, call update action.
In this way, I can use the same form (partial) for new and create, which is great!
How can I configure the resources to include a preview between actions. I mean, forcing new to call preview and then preview to call create.
I could add a new route/action and point the form for that action, however the same form cannot be used for new and edit.
There is a way to configure the resources to do that?
Have a look at this railscast: multibutton form, it shows a form with both a 'preview' and 'submit' button, maybe that's something you might want to do.
By the way, couldn't you use the same form by passing locals to the partial? For example:
<%= form_for #profile, url: dynamic_path do |f| %>
...
<% end %>
<%= render 'form', dynamic_path: profile_preview_path %>
you have several ways to do this:
you change the url of the form to your preview action (for which you have to add a route).
you use your create action for preview and create:
i.e. you add a parameter (like ':go_to_preview') to the form submit request. if you find it in the controller you render preview.
when the user wants to confirm the preview, you submit the data again (without that parameter) and this time create the record.
there are also 2 more dynamic possibilities:
you create the preview in real-time - if that is possible (like here on SO) - and use just the create action,
a variation of the first option: when the user submits the form, you send an ajax post request to a preview action, render a partial and include it on the page, then while your user still has the form he just filled, the user decides if she wants to modify or submit definitely.
I would suggest adding a DateTime column "finished_at", "published_on", etc... whatever is appropriate for your domain.
Using blog posts as an example:
scope :published, where("published_on IS NOT NULL")
scope :draft, where(:published_on => nil)
Use the scopes and new field where appropriate to limit the follow up actions.
This approach gives you more than you asked for :
a way to limit processing based on "state"
Data on creation times versus publishing times

How does rails decide to render a form with a method of PUT or POST?

Rails generates a form partial that can be used on both the page rendered by a get action and a page rendered by a new action. If it is the former the form's method is set to PUT, if the latter the form's action is set to POST.
How does rails decide which method to use?
if the object passed to the form is persisted?, the form builder knows that you are updating an object and will therefore render a PUT action. If it is not persisted, then it knows you are creating a new object and it will use POST.
<%= form_for #user do |f| %>
<%= f.button %>
<% end %>
If #user is a new record, POST is used and the button label becomes Create User, otherwise PUT is used and the label becomes Update User. There's not much more to it.
Forms editing existing resources use PUT, forms creating a new resource use POST. As per REST standards described here.
From the rails form_for helper code:
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
and persisted? for ActiveRecord is declared as:
!(new_record? || destroyed?)

Should my edit and new actions re-use the same view? (editing a post)

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.

One model and Many edit views

I have a model I named User, and I want use two different Views to edit it: the usual edit view and another view I called edit_profile.
I had no problem in creating routing, controller and views: I added edit_profile and update_profile views, and I added on routes.rb the line:
map.resources :users ,:member => {:edit_profile => :get, :update_profile => :put}
The problem is: when I submit the form in edit_profile and some error occur in some input fields, rails reload the edit_path page instead of edit_profile_path page !
This is the form on edit_profile.html.erb
form_for(:user, #user, :url => {:action => :update_profile}, :html => { :method => :put} ) do |f|
f.text_field :description
f.text_area :description
f.error_message_on :description
....
....
f.submit 'Update profile'
After clicking Update profile, if input errors occur I want to show edit_profile view instead of edit view
Where is the problem ?
Do You have some ideas ?
many thanks
Adding extra actions to a RESTful controller is often a code smell, an indication that there's a better way to model what you're trying to do. In this case, profile is really a sub-resource of user:
map.resources :users, :has_one => :profile
making your profile routes like
GET /users/1/profile # show
GET /users/1/profile/edit #edit
PUT /users/1/profile # update
DELETE /users/1/profile #destroy
You will have a separate ProfilesController for these actions... much cleaner.
How you model the data is up to you, (you don't have to have a one-to-one correlation between your models and your controllers!), but in this case I'd probably use ActiveRecord's aggregations to model the relationship between User and Profile. Think of it as an embedded has_one: http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html
Note that adding additional actions to RESTful controllers isn't always "wrong" ... its up to you to determine when its appropriate to split off the new actions into a separate resource. In this case, however, I think it's very clear-cut!
Your controller's action (the edit action, I assume) will need to know whether it has been reached via the normal edit page or the edit_profile page. You can use a hidden field named, perhaps, profile to post a breadcrumb that will tell it that. By doing this, you can redirect conditionally based on the existence of a profile param.
A cleaner way is to create a new action called edit_profile and extract the editing code to a common method that is called from both edit and edit_profile let the public methods handle any redirects.
Take a look in your user_controller file's update method. That's where submitting the edit form takes you. You'll see there that if the record can't be updated, it redirects back to the edit method.
One way to do what you want is to make your edit_profile form point to a new method, perhaps called update_profile, which is the same as the edit method but redirects to edit_profile when the record can't be saved.
Perhaps a better and DRY-er way to do it would be to pass a parameter from the edit_profile form that you can detect in your existing update method to differentiate between update attempts coming from edit / edit_profile.
Good luck!

Resources