ActiveAdmin display default view content - ruby-on-rails

I am working with ActiveAdmin and need to make customizations to some views and have come across a couple of scenarios I feel I am doing wrong.
I am adding an additional table to a show view (comments on Posts). This requires me to rewrite the whole attributes table and then add my panel. Is there a way to customize views without losing the default content?
I would also like to add a table of associated items on the show view which doesn't need to be customized is there any way to include the default tale that would normally be on the index view with default actions and paging?

After digging in the source code of Active Admin, I've found a way to patch this
show do
default_main_content
panel "Your Added Stuff" do
# Add stuff here
end
end
Of course this is undocumented and maybe considered a hack, but unless any other solution exists, it works.
Note: To do this in the form action (new and edit):
form do |f|
f.inputs
# Other inputs here
f.actions
end

Instead of using default_main_content, you could also just loop through the columns on the model like so:
ActiveAdmin.register Ad do
show do
attributes_table do
default_attribute_table_rows.each do |field|
row field
end
# Custom bits here
end
end
end

A couple areas of the documentation might help you:
See Customize the Show Page, Customizing the Index Page, Customizing the Form, and Custom Pages. An example of customizing a show screen:
ActiveAdmin.register Ad do
show do |ad|
default_main_content
h3 ad.title
end
end
See Custom Action Items in the Custom Controller Actions section of the documentation. An example:
action_item :only => :show, :if => proc{ current_admin_user.super_admin? } do
"Only display this to super admins on the show screen"
end
NB default_main_content does not exist in the documentation anymore, yet it works fine.

Just figured that out myself:
For the default table index page you can do something like this
index do
h1 "Hello World"
p "get more content"
instance_eval(&default_table)
end

Related

Activeadmin - user option for hide and unhide column

Is there any solution in activeadmin for option where user can hide/unhide column?
Out of the box, no. You would need to define a data structure to hold the user's preferences then define your index something like:
index do
column :title unless current_user.hide_column?(:title)
...
end
The simplest way to hold the preferences would be a UserColumnPreference resource which itself could be managed through ActiveAdmin. More sophisticated solutions might involve using AJAX, subclassing ActiveAdmin::IndexAsTable, etc.
If you don't need to persist the preference then a simple JavaScript to manipulate the HTML table on the page will do, eg. Hiding columns in table JavaScript This is unrelated to ActiveAdmin.
I found a neat solution for this. Saving it as a session variable.
Complete solution:
This creates a button on the index page that says show or hide images depending on the state:
action_item :toggle_image_view, only: :index do
link_to (session['hide_images'] || false) ? t('views.admin.show_images') : t('views.admin.hide_images') , toggle_image_view_admin_person_poses_path
end
This toggles the session key and shows the index page again:
collection_action :toggle_image_view, method: :get do
session['hide_images'] = !(session['hide_images'] || false)
redirect_to collection_path
end
Here you see the column that will get displayed or not depending on the session variable. Default is set to display them.
index do
selectable_column
id_column
column :camera
unless (session['hide_images'] || false)
column :image
end
actions
end

Dividing a Rails edit view for the purposes of UI

So I have a 'Project' model which respective db table has many attributes.
Ideally, when I hit projects/1/edit, I'd like the user to:
See and be able to edit only a portion of the project attributes
See a navigation menu that allows her to navigate to another view that allows to see and edit the attributes left.
All this instead of showing all the attributes in one 'edit' view.
I'm leaning to a JS solution that hides and shows specific attributes when a menu is clicked. However, I'd ideally want that the urls reflect the section the user is located for example:
Instead of a url like projects/1/edit I'd like something like:
projects/1/edit/name_of_section_1 and projects/1/edit/name_of_section_2
In any case I wonder what would be the best practice when it comes to divide views like this.
UPDATE: Only a few precisions/revisions from Dan's solution below for future reference
I've used his second approach successfully revising his proposed code as follows:
Config/routes.rb
revised the controller name for both routes to 'projects'
resources :projects do
member do
get "settings/:panel" => "projects#edit", as: :settings
patch "settings/:panel/update" => "projects#update", as: :update_settings
end
end
Controllers/projects_controller.rb
There was some mistake in the proposed code for the edit action so I slightly changed it to make it work
def edit
panel = ['general', 'invitations', 'welcome_message', 'danger'].detect{|p| p == params[:panel] }
render template: "projects/#{panel}.html.erb" if panel
end
There's a couple of ways to do this and there all equally valid.
If you need all the fields filled in to create the record you'll either need to have tabbed views with javascript (bootstrap has a nice plugin for this)
To do this with custom routes you could have:
resources :projects do
member do
get "settings"
patch "update_settings"
get "settings2"
patch "update_settings2"
end
end
which would give you URLs like /projects/1/settings and projects/1/update_settings
If you have lots of panels you could do this more efficiently with something like this:
resources :projects do
member do
get "settings/:panel" => "settings#edit" as: :settings
patch "settings/:panel/update" => "settings#update" as: :update_settings
end
end
to allow for routes like /projects/1/settings/page1 and /projects/1/settings/page2
in your controller you could then have:
def edit
if panel = ['page1', 'page2'].detect{|p| p == params[:panel] }
render template: panel
end
end
this checks ifs its a valid panel you've made and then renders that specific template you've created in app/view/projects/page1.html.erb

ActiveAdmin and handling a subset of data

We use ActiveAdmin and have a notion of an event at like http://domain.com/admin/events/3/edit. I'd like to be able to edit the finance data aspect of this (something like http://domain.com/admin/events/3/finance/edit or http://domain.com/admin/events/3/edit/finance). How would you organize it? I was thinking of trying to do a custom controller like this:
ActiveAdmin.register Event, as: 'Finance' do
permit_params %i(venue_id name event_type_id)
before_create do |event|
event.created_by = current_user
end
show do |event|
panel 'Details' do
attributes_table_for event do
row :id
end
end
end
end
but this doesn't seem to work. What would be the most basic pattern to have a controller which has a portion of our data and can work well within ActiveAdmin?
You appear to be asking about nesting a resource inside another, see 'Belongs To' on this page

What is the "Rails Way" to load templated pages

I am just starting to pick up Ruby on Rails. I rather like it, but I ran into a roadblock. I know of two ways to solve a problem but I am curious as to what the "Rails Way" to do this is.
The Setup
I have an index page that lists project descriptions. When a user clicks on a project it brings them to the show function of the projects controller. Projects in the list are loaded from a MySQL database.
What I Want To Do
I want to be able to load project specific information on each "Project Page." This information consists of documentation, code examples, etc. Each page will have the same general template.
Way 1
Store the HTML and text in the MySQL database in a "Sections" table and load all the sections related to that project. Display each section going down the page.
Way 2 (The way I would rather do it)
Have a separate view for each project with the full documentation. Load a specific view based on the project that is loaded in the show function
Comments
Is there some third way in Rails that I am not used to thinking of since I come from using CodeIgniter?
I am completely up for adapting to what the "Rails Way" is, but I am just not sure what the proper convention for this kind of thing is.
Or is this problem a case of, it does not matter how you do it at all?
Thanks in advance.
There are many ways to do this, one of the easiest ways is to override default template.(render :action doesn’t run any code in the target action only template)
This is Way2 in your question. Example:
def show
#project = Project.find(params[:id])
if #project.has_template?
render :action => "show_#{#project.template_name}" and return
end
render :action => "show"
end
In this example directory app/views/projects should have templates for each project
with names like "show.html.erb" for default one and "show_myspecialproject.html.erb" for project with template_name "myspecialproject" , etc....
Template_name is a method that tell you if project has such or should use default, you can put any logic in this method, it can be additional column id table or just template_name can be equal to project name, or it can just check if file exist in current directory.
You can also use partial templates if you want to use show.html.erb because you have code duplications and keep your templates DRY.
http://rails.rubyonrails.org/classes/ActionView/Partials.html
This way
controller action is default
def show
#project = Project.find(params[:id])
end
in show.html.erb
<h1><%= #project.name %></h1>
<%= render #project.tamplate_name %>
In this example all partial templates should start with "_" ,
Ex. : "_myspecialproject.html.erb"
If each project has the same general template, I don't know what the value is of having different views.
Here's a suggestion to try on for size.
You can store the documentation, code examples, etc. in a sections table, or each type in it's own table. If you have them in a sections table, include a type field, and use different classes for each type. Let's say Documentation and CodeExample are two such classes.
Define a has_one in your Project class for each type with a :conditions option.
class Project < ActiveRecord::Base
has_one :documentation, :conditions => { :type => "Documentation" }
has_one :code_example, :conditions => { :type => "CodeExample" }
end
class Documentation < ActiveRecord::Base
set_table_name "sections"
belongs_to :project
end
If you want to organize your project show page to include the sections, you can call partials for each section type.
If you want your project show page to have a link or tab (you can implement tabs on the page using links or CSS formatted buttons) go to a show action in a controller for the section type. Using nested routes for this might be an idea you want to use.
resources :projects do
resource :documentation
resource :code_example
end
These are some ideas. You might take what you want from my ideas, if you don't want to do it all this way.

DRYing up Rails Views with Nested Resources

What is your solution to the problem if you have a model that is both not-nested and nested, such as products:
a "Product" can belong_to say an "Event", and a Product can also just be independent.
This means I can have routes like this:
map.resources :products # /products
map.resources :events do |event|
event.resources :products # /events/1/products
end
How do you handle that in your views properly?
Note: this is for an admin panel. I want to be able to have a "Create Event" page, with a side panel for creating tickets (Product), forms, and checking who's rsvp'd. So you'd click on the "Event Tickets" side panel button, and it'd take you to /events/my-new-event/tickets. But there's also a root "Products" tab for the admin panel, which could list tickets and other random products. The 'tickets' and 'products' views look 90% the same, but the tickets will have some info about the event it belongs to.
It seems like I'd have to have views like this:
products/index.haml
products/show.haml
events/products/index.haml
events/products/show.haml
But that doesn't seem DRY. Or I could have conditionals checking to see if the product had an Event (#product.event.nil?), but then the views would be hard to understand.
How do you deal with these situations?
Thanks so much.
I recommend you to make separate admin controller with it's own views to administrate everything you want. And your customer's logic stayed in products contoller.
I don't have good and clean solution for this problem. Usualy if views doesn't differ to much, I use single view and add some code like #product.event.nil?. You can always add some variable, or helper that will make this method shorter, on example has_event? - then your view will look cleaner. And use it in code like this:
<% if has_event? %>
some html
<% end %>
or for single line:
<%= link_to 'Something special', foo_path if has_event? %>
On the other side, you can create few partials that are the same for both views, put them in some folder, on example /shared/products/... and render them from your views like this:
<%= render :partial => '/shared/products/info' %>
and so on.
But if they don't differ too much, I really would use if version.
The views will be handled by the ProductsController. You can alter the logic in your controller depending on the nesting of the resource.
# app/controller/products_controller.rb
# ...some code...
def index
#event = Event.find_by_id(params[:event_id]) if params[:event_id]
#products = #event ? #event.products : Product.all
end
The view will be handled by the usual product view
# app/views/products/index.html.haml
- unless #products.blank?
- #products.each do |product|
%p= product.some_attribute

Resources