I have the action called dashboard in the Users controller.
It's something like this:
def dashboard
#data = #user.datas.includes(:user).includes(:images)
render 'datas/show'
end
and I am getting error undefined method 'user' for #ActiveRecord::Relation:0x007fc702d34868>
on this line:
= render 'users/header', :user => #data.user
How is that possible? In the #data variable is everything what's needed...
Your problem is that you are calling an instance method on a list of objects.
#data = #user.datas.includes(:user).includes(:images)
This is actually an array of Data objects belonging to the user's instance #user.
To solve this, you can get the first element of the list and get its user:
= render 'users/header', :user => #data.first.user
But you should know that all the #variables are shared between the controller's action, the view and the invoked partials. With this in mind, you will notice that you can simply do the following:
# in datas/show.html.haml
= render 'users/header'
# in users/_header.html.haml
# you can use the #user variable as your wishes
= #user
Related
Rails 5.2
In my inventories_controller.rb, I have the following:
before_action :fetch_product, only: [:show]
def show
........
end
def fetch_product
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
This works fine, when I do: http://0.0.0.0:3000/sku/12345678
I am trying to implement search functionality, so I modified nventories_controller.rb as follows:
def fetch_product
if params.has_key?(:search) && !params[:search].blank?
product = Product.find_by_sku(params[:search])
if !product
params = params.except[:search]
redirect_to product_show_path, alert: 'Product was not found'
end
params = params.merge!(:sku, product.sku)
end
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
When I do: http://0.0.0.0:3000/sku/12345678
I get an instant error message:
undefined method `has_key?' for nil:NilClass
Using my debugger, I find that on entry into the fetch_product method, params is nil
Any idea what's going on?
params = params.merge!(:sku, product.sku) modifies the hash in place and returns nil, don't do that assignment, just call params.merge! (if you still want to do the assignment, remove the "!").
Personally, I wouldn't modify the params hash unless it's really really needed, I would use another variable.
On an unsuccessful save, I would like to redirect to the previous view but with the error message.
redirect_to user_path(#user_id), errors: #user.errors
but in the view, when I check for errors I get an undefined variable errors.
I am not using the same controller new and create, so I can't have #user.errors.any in new.html.erb. I have two different controllers, one in which form is there, and another controller which will take care of create, if the create is not happening I need to redirect to the previous controller.
You may need to use render instead of redirect_to.
Something like this:
# controller_1
def step_1
#user = User.new
#user.do_something
...
end
# controller_2
def step_2
if #user.save?
# redirect to another...
else
render 'controller_1/step_1`
end
end
Then on view step_1.html.erb, you can print out errors of #user with #user.errors.
You have to pass the parameters inside the redirect_to helper like below,
redirect_to user_path(id: #user_id, error: #user.errors.messages)
Please check the rake routes and pass the appropriate key for id, whether it's :id, or :user_id
I have a controller and need to pass objects which validation fail to another controller action to process them and show to user:
class PersonController
def save_persons
invalid_persons = ... #array of Person.new objects
flash[:invalid_persons] = invalid_persons
redirect_to action: :fix_errors
end
def fix_errors
invalid_persons = flash[:invalid_persons]
invalid_persons.each do |invalid_person|
puts invalid_person.errors #here i get exception!
end
end
end
When i try to puts invalid_person.errors i get error: undefined method errors for #Hash:0x007fd594e79098>. It seems that rails transform my objects array to some hash
Can you suggest me, what is the right way to pass some object through flash?
Edit: it turns out I made a very simple mistake and had a Template that was associated with a LocalTemplate id that no longer existed. If anyone has this problem and thinks that they somehow are unable to unable to associate the id of another model in their update action, make sure that you didn't accidentally delete the parent object causing that id to no longer exist!
The code below, while dramatically simplified did work for me.
I have a Template model in my rails app. It has a method "data" defined in it.
I am able to access this method in the create and show actions with #template.data, however when using the same #template.data in the update action of my controller I get a no method error because I am not showing the correct local template id to it. This line can be found in the model where it reads base_data = YAML.load(local_template.data)
I stored an id of the associated local_template when initially saving a new template, but how can I make sure I reference that id again in the update action so I do not get a no method error?
Here is a simplified version of the Template model and controller
Model:
class Template < ActiveRecord::Base
def data
base_data = YAML.load(local_template.data)
# couldn't pass the correct LocalTemplate here because
# the local_template_id I had in my Template model no
# longer existed. Changing the id to a LocalTemplate
# that did exist fixed the issue.
end
end
Controller:
class TemplatesController < ApplicationController
def index
#business = Business.find(params[:business_id])
#templates = #business.templates.all
end
def new
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build
end
def create
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build(template_params)
if #template.save
#template.data #works fine here
redirect_to business_url(#template.business_id)
else
render 'new'
end
end
def show
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
#template.data #works fine here too
end
def edit
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.find(params[:id])
end
def update
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
if #template.update_attributes!(pass_template_params)
Api.new.update_template(#template.data.to_json) #this is where I had a problem
redirect_to business_url(#template.business_id)
else
render 'edit'
end
end
end
You are mixing a lot. There is a lot to refactor in your controller...
First of all, your TemplatesController should be about the template resources, but your controller looks more like a BusinessesController. In general your update action for example should look more like:
def update
#template = Template.find params[:id]
#template.attributes = template_params # though this should raise a NoMethodError, because you dind't define it; I'd prefer params[:template] if possible
if #template.save
redirect_to business_url(#template.business_id)
else
#local_templates = LocalTemplate.all
render 'edit'
end
end
Instantiating #business and #local_templates makes non sense, because you don't use it at all. Speed up your responses if you can! :)
Fixed that, there is no need for the overhead of a nested resource in update (as you did).
If saving #template fails for validation reasons, you better should load the business object late by:
#template.business
in your /templates/edit.html.erb partial. Then you also do not need a nested route to your edit action... You see, it cleans up a lot.
As a general guideline you should create as less as possible controller instance variables.
If you cleaned up your controller and views, debugging your data issue will be easier.
I assume:
local_template
in your Template model to be an associated LocalTemplate model object. So it should no issue to call that anywhere if you ensured the referenced object exists:
class Template < ActiveRecord::Base
def data
return if local_template.nil?
YAML.load(local_template.data)
end
end
or validate the existence of the local_template object. or even b
You should confirm #template is not nil, if #template is nil, you can't use data method.
1.9.3-p547 :024 > nil.data
NoMethodError: undefined method `data' for nil:NilClass
from (irb):24
from /Users/tap4fun/.rvm/rubies/ruby-1.9.3-p547/bin/irb:12:in `<main>'
And you should use update_attributes!, it can raise an exception if record is invalid.
You can do like this.
if #template
#template.update_attributes!(template_params)
#template.data
end
I have this:
ActiveAdmin.register User do
controller do
def show
#user = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
link_to #user.display_name, user_path(#user.slug)
end
end
end
end
But when I load the page, I get an error saying:
undefined method `display_name' for nil:NilClass
which means that #user is nil. I am positive that #user is set appropriately (meaning the finder is getting appropriate data that exists in the db). I'm thinking it has something to with how ActiveAdmin works that I'm unfamiliar with. Any thoughts?
Also, I know I could do show do |user|, but there are more complicated things I am using this for and need access to the user object in the controller.
Just in case someone else stumbles upon this:
controller.instance_variable_get(:#user)
should work as well.
There is controller in active admin, despite this you can not pass instance variable to arbre part. But you can use params hash for this:
ActiveAdmin.register User do
controller do
def show
params[:user] = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
link_to params[:user].display_name, user_path(params[:user].slug)
end
end
end
end
P.S.: If you don't want to change params, then all instance variables are stored in #arbre_context.assigns. You may also do like:
link_to #arbre_context.assigns[:user].display_name, user_path(#arbre_context.assigns[:user].slug)
Instance variables are defined as helper methods. If you have that defined in your controller, you can access it. Alternatively, you can simply call resource, which will have reference to the active record object.
ActiveAdmin.register User do
controller do
def show
#user = User.find(params[:id])
show!
end
end
show do
attributes_table do
row "User" do
# note that your have access to `user` as a method.
link_to user.display_name, user_path(user.slug)
end
end
end
end
It seems does not work that way in activeadmin. The only instance variable available inside "form" block is #config.
The best way to solve this issue is to use partials as described in "Customizing the Form"
http://activeadmin.info/docs/5-forms.html
Not entirely sure how selects the correct instance variable on the model, but, you could give pretty much any name to the instance variable, i test some cases and it seems that just looks for the one that haves the same model type when you don't specify it, to answer your other question, you have many ways to do it
the simples one, just the same name as your instance variable,
in your case,
row :attr do
link_to user.display_name, admin_user_path(user)
end
the you have
row :attr do |any_name|
link_to any_name.display_name, admin_user_path(any_name)
end
and the last method i know, you have two escenarios, one for your active_admin files(.rb)
#eg: admin/user.rb
#arbre_context.assigns[:user]
or in custom .arb views, like a form for a custom collection_action(same but direct access)
assigns[:user]
eg:
#views/admin/users/new_invitation.html.arb(arbre) or html.erb
active_admin_form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
....
end
form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
....
end
semantic_form_for assigns[:user], :url => send_invitation_admin_users_path do |user|
.....
Like i say, i'm not sure how active_admin deals with instance variables, but a least you have multiple options, regards
If your goal is to set #user for the show action template, it's not necessary to do so, because active admin is already doing this for you.
If you use member_action, the #user object is there for you, it's called resource.
You can define a singleton method on your resource, it would be available in the view. This could make sense in some cases.
This is another way to pass information from the controller to the view.
member_action :show, method: :get do
resource.instance_eval do
define_singleton_method('language') do
'English'
end
end
end
show do
attributes_table do
row :name
row :email
row :id
row :language
end
end