ActiveAdmin: want to build new association from controller instead of view partial - ruby-on-rails

I know that I shouldn't be building a new associated image for my gallery in the Gallery view partial. However, I can't figure out to do this in ActiveAdmin's generated controller, which can apparently be customized using the "Controller" method in the resource file. How can I do this using the Controller method instead of the view partial?
Here is my Gallery resource file:
ActiveAdmin.register Gallery do
controller.authorize_resource
scope_to :current_admin_user
before_filter :block_access
controller do
def block_access
if params && params['q'] && params['q']['admin_user_id_eq']
params['q']['admin_user_id_eq'] = nil
end
end
end
form :partial => "form"
end
Here is my Gallery view partial:
<% new_image = #gallery.images.build %>
<%= semantic_form_for [:admin, #gallery] do |g| %>
<%= g.inputs "Details" do %>
<%= g.input :title %>
<%= g.input :images, :as => :check_boxes, :label_method => Proc.new { |image| image_tag(image.thumb_path, :alt => "") + content_tag("h3", image.title) } %>
<% end %>
<%= g.inputs :for => [:images, new_image], :name => "New Image" do |image| %>
<% if image.object.new_record? %>
<%= image.input :title %>
<%= image.input :asset, :as => :file %>
<% end %>
<% end %>
<%= g.buttons %>
<% end %>
UPDATE #1: Adding the following methods inside the "controller do" block (based on the suggestion of Thomas Watson) worked for creating/editing Galleries.
def new
#gallery = Gallery.new
#new_image = #gallery.images.build
new!
end
def edit
#gallery = Gallery.find(params[:id])
#new_image = #gallery.images.build
edit!
end
def update
#gallery = Gallery.find(params[:id])
#new_image = #gallery.images.build
update!
end

Active Admin depends on Inherited Resources for its action magic. You can open up any controller action and add stuff to it. You can even create your own instance variable and Active Admin will automatically use that instead of creating its own.
In your case you would do something like this if you where to build it on the new action:
ActiveAdmin.register Gallery do
controller do
def new
#gallery = Gallery.new
#new_image = #gallery.images.build
# call `new!` to ensure that the rest of the action continues as normal
new!
end
end
end

Related

Rails double the records on update_attributes

My update function double the records for nested items in model on submit.
The one, which is NOT in the fields_for works as expecting, but every record in fields_for is doubling.
What am I missing? Any help will be highly appreciated
def edit
#printer = Printer.find(params[:id])
end
def update
#printer = Printer.find(params[:id])
if #printer.update_attributes(printer_params)
redirect_to #printer
else
render 'edit'
end
end
def printer_params
params.require(:printer).permit(:model, colors_attributes: [:color], materials_attributes: [:material], printer_photos_attributes: [:image_url] )
end
edit.html.erb
<%= form_for #printer, html: { multipart: true }, :url => url_for(:controller => 'printers', :action => 'update') do |p|%>
<%= p.text_field :model %>
<%= p.fields_for :colors do |color|%>
<%= color.text_field :color %>
<% end %>
<%= p.submit "Edit" %>
<% end %>
You are missing :id in printer_params. Without :id each your update for nested params is considered to be a new record. It should be as following for your colors_attributes:
def printer_params
params.require(:printer).permit(:model, colors_attributes: [:id, :color], materials_attributes: [:material], printer_photos_attributes: [:image_url] )
end
I guess, you should also correct your other nested attributes in this method.

Routing issue after update

So I'm still a Rails noob so I may be completely going at this wrong but I have two controllers. A Question Controller and an Answer Controller. I am trying to build a grading function that allows an admin user to assign points to essay questions. I am using the /answer/:id to be where the :id is the id of the question and then rendering a partial to iterate through all of the answers for that id. Clear as mud I'm sure...
My problem: Within the partial where the user's answer is displayed, I have a form that allows the admin to fill out the number of points for that answer and submit. Ideally, I'd like it to move to the next page (using will_paginate), but at a minimum, I'd like to stay on the same page. I am able to get the form working but it keeps going to /answers/:id but where :id is the id of the individual answer, so not what I'm hoping.
answers_controller.rb
class AnswersController < ApplicationController
def index
#user = current_user
#questions = Question.all
end
def show
#user = current_user
#question = Question.find(params[:id])
#answers = Answer.where("question_id = ?", #question.id).paginate(:page => params[:page], :per_page => 1)
#answer = Answer.where("question_id =? AND user_id = ?", #question.id, #user.id)
end
def edit
#answer = Answer.find(params[:id])
end
def update
#answer = Answer.find(params[:id])
if #answer.update_attributes(grade_params)
flash[:success] = "Answer Graded"
else
flash[:warning] = "Not Graded"
end
end
private
def grade_params
params.require(:answer).permit(:points_earned)
end
end
_essay_grades.html.erb (partial that is being rendered on the show page that contains the form)
<% #answers.each do |answer| %>
<p>User: <%= answer.user_id %></p>
<%= answer.answer %><br>
<%= #question.value %>
<br>
<%= form_for(answer) do |f| %>
<%#= f.radio_button :points_earned, #question.value %><br>
<%#= f.radio_button :points_earned, 0 %> <br>
<%= f.text_field :points_earned %> Points<br>
<br>
<%= f.submit "Award Points" %>
<% end %>
<% end %>
<br>
<br>
<%= will_paginate #answers, renderer: BootstrapPagination::Rails %>
routes.rb
Rails.application.routes.draw do
resources :admins, :answers, :static_pages, :questions
devise_for :users, :controllers => { registrations: 'registrations' },
:path => '', :path_names =>
{ :sign_in => "login", :sign_up => "register" }
root "static_pages#index"
end
I'm sure there's a simple solution here (or maybe it's changing how I have things set up...). Any help is greatly appreciated!
AFTER FEEDBACK:
Added the grades model and set up a through relationship with questions.
answer_controller.rb
class AnswersController < ApplicationController
def show
#user = current_user
#question = Question.find(params[:id])
#answers = Answer.where("question_id = ?", #question.id).paginate(:page => params[:page], :per_page => 1)
#answer = Answer.where("question_id =? AND user_id = ?", #question.id, #user.id)
end
def update
#user = current_user
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
#grade = #question.grades.new(grade_params)
if #grade.save
flash[:success] = "Answer Graded"
redirect_to #question
end
end
private
def grade_params
params.require(:grade).permit(:user_id, :answer_id, :points_earned, :graded_by, :comment)
end
end
_answer.html.erb
<%= answer.user_id %>
<%= form_tag [#question, answer], method: :put do %>
<%= hidden_field_tag :graded_by, current_user.id %>
<%= hidden_field_tag :answer_id, answer.id %>
<%= number_field_tag :points_earned %>
<%= submit_tag "Submit Grade" %>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :questions do
resources :answers, only: [:update]
end
resources :admins, :static_pages
questions/show.html.erb
...
<h3>Show answers</h3>
<%= render #answers, locals: {question: #question} %>
<%= will_paginate #answers, renderer: BootstrapPagination::Rails %>
You will have to use the following in your form so the update does not load a new page, but still submits your update. Use Chrome / Firefox developer tools to view requests / responses.
<%= form_for(answer), :remote => true do |f| %>
Then, alter the update action in the answers controller to load the 'next unrated answer':
def update
rated_answer = Answer.find(params[:id])
if rated_answer.update_attributes(grade_params)
flash[:success] = "Answer Graded"
else
flash[:warning] = "Not Graded"
end
#answer = get_next_unrated_answer(rated_answer.question_id)
end
private
def get_next_unrated_answer(question_id)
# I am making a couple of assumptions on your model here, but get an answer that has not been rated yet for this question
next_answer = Answer.where("question_id = ? and points_earned = ?", question.id, nil)
#returned automatically
end
Then you will have to create app/views/answers/update.js.erb to load the new answer to your page with the following line:
$('#main_div').html('<%= escape_javascript(render partial: 'whatever_partial_you_have_created_to_display_the_next_unrated_answer') %>');
Just go and create a new partial that displays your answer and form correctly for the next unrated answer. Or ideally load your initial 'show.html.erb' with the relevant partials and reuse them.
This is the simple way to do it, but if I were you I would probably rename these new functions to not use 'update' or 'show' but rather call it something like 'rate' and even 'rate_show' so you can use update and show in its original form (for updating and answer or showing an answer) if required later in your project.
From what I understood of your question, I think you'd be best looking into nested routes:
#config/routes.rb
resources :questions do
resources :answers, only: [:update]
end
#app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
def show
#question = Question.find params[:id]
end
end
#app/views/questions/show.html.erb
<%= #question.title %>
<%= render #question.answers, locals: {question: #question} %>
#app/views/questions/_answer.html.erb
<%= answer.title %>
<%= form_tag [question, answer], method: :put do %>
<%= text_field_tag :grade %>
<%= submit_tag %>
<% end %>
The above will give you what you have already (just to clarify your "clearly mad" remark is not the case at all!).
-
The following is where the nested resources come in.
At the moment, it seems you're having a problem associating an answer to a question:
where :id is the id of the individual answer, so not what I'm hoping
A remedy for this is as follows:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def update
#question = Question.find params[:question_id]
#answer = #question.answers.find params[:id]
#grade = #answer.grades.new grade_params
redirect_to #question if #grade.save
end
private
def grade_params
params.permit(:points_earned) #-> will probably have to refactor this
end
end
This will create a new grade (which you should have in an associated model), for that specific answer. Because the answer has been associated to a question, it will allow you to use the nested routes to load both.
In terms of your setup, I'd personally add a Grade model, so that you can have multiple grades per answer. This is against your current schema, but works well to ensure you have the functionality necessary to facilitate multiple grades:
#app/models/grade.rb
class Grade < ActiveRecord::Base
belongs_to :answer
belongs_to :user
end
#app/models/answer.rb
class Answer < ActiveRecord::Base
has_many :grades
end

Nested form attributes

I have one form working to save in the database but I want to save some fields that are going to be saved in a different record.
<%= form_for #complaint, url: {action: 'create'}, :html => {:multipart => true} do |f| %>
<%= f.text_field :complaint_info %>
<%= f.fields_for :witness do |witnesses_form| %>
<%= witnesses_form.text_field :name %>
<% end %>
<% end %>
In my controller:
def new
#complaint = Complaint.new
end
def create
#complaint = current_user.complaints.build(complaint_params)
if #complaint.save
redirect_to dashboard_complaint_path(#complaint)
else
render 'new'
end
end
private
def complaint_params
params.require(:complaint).permit(:complaint_info, witnesses_attributes: [:name])
end
on the model:
class Complaint < ActiveRecord::Base
belongs_to :user
has_many :witnesses
accepts_nested_attributes_for :witnesses
end
.
class Witness < ActiveRecord::Base
belongs_to :complaint
end
But I'm getting this error:
Unpermitted parameter: witness
Everything seems to be as it suppose to be, what am I missing here?
EDIT:
was able to save the record by adding:
#complaint.witnesses.build
to the create action in the controller but it is still not letting me save the :name there
<ActiveRecord::Relation [#<Witness id: 1, name: nil, phone: nil, complaint_id: 8, created_at: "2015-06-08 20:05:06", updated_at: "2015-06-08
EDIT 2:
Was able to fix it by moving the #complaint.witnesses.build from the create action to the new action and it fixed it, now I can create the record and lets me save the text_fields in it.
Can you try with changing your controller and views codes as following
In controller
def new
#complaint = Complaint.new
#witnesses = #complaint.witnesses.build
end
def edit
#witnesses = #complaint.witnesses
end
In views
<%= f.fields_for :witnesses, #witnesses do |witnesses_form| %>
<%= witnesses_form.text_field :name %>
<% end %>
I was able to fix it by adding #complaint.witnesses.build to the newaction instead of the create action.
So my controller now looks like this:
def new
#complaint = Complaint.new
#complaint.witnesses.build
end
def create
#complaint = current_user.complaints.build(complaint_params)
if #complaint.save
redirect_to dashboard_complaint_path(#complaint)
else
render 'new'
end
end
private
def complaint_params
params.require(:complaint).permit(:complaint_info, witnesses_attributes: [:name])
end

Rails accessing custom method instance variable

What I am trying to do is add a simple search to a 'show' view in my rails app. I've watched the rails cast about simple search and it's given me some good direction but I can't seem to get it to work with my situation. I just have a list of FAQs and want to have a search filter for the 'question(s)'.
Here's my views/guests/show.html.erb
<%= form_tag faq_search_guests_path, method: :get, remote: true, :id => "faq_search_form" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<div id="faq-search-test"></div>
<% #faqs.each do |f| %>
<h4>Question</h4>
<%= f.question %>
<h4>Answer</h4>
<%= f.answer %>
<% end %>
Here's the controller/guests_controller.rb method that originally sets #faqs
def show
#guest = Guest.find(params[:id])
#hotel = Hotel.find(params[:hotel_id])
#faqs = Faq.where(hotel_id: #hotel.id)
#template = Template.where( hotel: #hotel.id )
#visit = Visit.where("hotel_id = ? AND guest_id = ?", #hotel.id, #guest.id).last
#visit = Visit.find(params[:visit_id]) if params[:visit_id] && (#visit.hotel_id == #hotel.id)
unless current_user.hotels.include?(#hotel)
render :file => "public/401.html", :status => :unauthorized
end
end
Here's my controllers/guests_controller.rb custom method my form is calling
def faq_search
#faq_result = Faq.search(params[:search])
render :nothing => true
end
Here's my routes.rb for that
resources :guests do
get 'faq_search', :on => :collection
end
resources :faqs
Here's my models/faq.rb
def self.search(search)
if search
where('question LIKE ?', '%#{search}%')
else
scoped
end
end
So from here, I am having trouble displaying #faq_result in show.html.erb. I guess I have no idea how to access an instance variable from a custom controller method from a view.
Any help would be much appreiciated :)!

View not recognising model attributes? Rails

I am trying to make it so that this form here will display certain fields based on the type of the website. In this case, I want it to display the form for when project.type == Website.
However I keep getting
undefined method `type' for #<Project::ActiveRecord_Relation:0x007ffe1cb543a8>
I am sure i can call .type normally because it works in the console.
Here are my files:
#views/assets/_new_asset.html.erb
<%= simple_form_for #asset do |f| %>
<% if #project.type == 'Website' %>
<%= f.input :name %>
<%= f.input :url %>
<%= f.button :submit %>
<% end %>
<% end %>
Here is my assets/controller
#controller/assets_controller.rb
class AssetsController < ApplicationController
def new
#asset = Asset.new
project = Asset.where(:project_id)
#project = Project.where(:id == project)
end
def create
#asset = current_user.assets.build(asset_params)
if #asset.save
flash[:notice] = "Asset successfully added."
redirect_to(#project, :action => 'show')
else
render(:action => 'new')
end
end
private
def asset_params
params.require(:asset).permit(:id, :type,:url, :page_rank, :rev_company ,:social_pages)
end
end
Well, you are getting back an object of ActiveRecord::Relation, not your model instance, thus the error since there is no method called type in ActiveRecord::Relation.
This should work
#project = Project.where(:id == project).first
OR
You can do like this too
<% if #project.first.type == 'Website' %>
Doing #project.first.type works because #project.first is returning the first instance of the model that was found by the where
#views/assets/_new_asset.html.erb
<%= simple_form_for #asset do |f| %>
<% if (#project.type == 'Website') %>
<%= f.input :name %>
<%= f.input :url %>
<%= f.button :submit %>
<% else %>
You Should not see this line.
<% end %>
In Controller
#controller/assets_controller.rb
class AssetsController < ApplicationController
def new
#asset = Asset.new
# As if i have no idea from where youre getting :project_id
# in your code so i changed that. add that to asset_params
# if required. Thanks!!!
#project = Project.where(id: params[:project_id]).take
end
def create
#asset = current_user.assets.build(asset_params)
if #asset.save
flash[:notice] = "Asset successfully added."
redirect_to(#project, :action => 'show')
else
render(:action => 'new')
end
end
private
def asset_params
params.require(:asset).permit(:id, :type,:url, :page_rank, :rev_company ,:social_pages)
end
end

Resources