Rails can't create new object inside of ActiveAdmin controller - ruby-on-rails

I'm working on messaging system between User and AdminUser. The User part is ready now I'm struggling how to allow Admin to send a reply to a conversation started by a User, inside of ActiveAdmin.
Code below:
# app/admin/conversations.rb
ActiveAdmin.register Conversation do
decorate_with ConversationDecorator
# ...
controller do
def show
super
#message = #conversation.messages.build
end
end
end
app/views/admin/conversations/_show.html.erb
# ...
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :messageable_id, value: current_user.id, type: "hidden" %>
<%= f.text_field :messageable_type, value: "#{current_user.class.name}", type: "hidden" %>
<%= f.submit "Send Reply" %>
<% end %>
<% end %>
Which gives me an error:
First argument in form cannot contain nil or be empty
Extracted source (around line #51):
51 <%= form_for [#conversation, #message] do |f| %>
When I tried to debug it turned out #message = nil inside of _show.html.erb. How is that possible if I defined #message inside of ActiveAdmin controller ?
[EDIT]
In case you're curious, ConversationController below:
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#admins = AdminUser.all
#conversations = Conversation.all
end
def new
#conversation = Conversation.new
#conversation.messages.build
end
def create
#conversation = Conversation.create!(conversation_params)
redirect_to conversation_messages_path(#conversation)
end
end
#routes
resources :conversations do
resources :messages
end

Normally you set up instance variables in your controller, and then Rails later does an implicit render of the view once the controller method completes.
However, it is possible to do an explicit render of the view, by calling something like render action: or render template: while the controller method is running, and presumably this is happening within the call to super.
See the Layout and Rendering Rails Guide for more information.
You'll need to move the assignment to be before the call to super.
You may also need to replace #conversation with resource in the ActiveAdmin controller (this is an ActiveAdmin/InheritedResources gem thing).

Related

How to properly pass params to an object using form_for?

I'm trying to make an API which passes the filled form symbols, new.html.erb, to an object in another view, using form_for and params[], show.html.erb, that shows a post with the filled form, but I can't find the right way, or the best way to do it, something goes wrong when I try to pass the params to the other view
views/recipes/new.html.erb
<%= form_for :recipe, url: recipes_show_path do |r| %>
Título: <%= r.text_field :title%><br />
Tipo da Receita: <%= r.text_field :recipe_type%><br />
Cozinha: <%= r.text_field :cuisine %><br />
Dificuldade: <%= r.text_field :dificulty %><br />
Tempo de Preparo: <%= r.text_field :cook_time_min %><br />
Ingredientes: <%= r.text_field :ingredients %><br />
Como Preparar: <%= r.text_field :cook_method %><br />
Enviar: <%= r.submit %>
<% end %>
views/recipes/show.html.erb
<h3>Detalhes</h3>
<p><%= #recipe.recipe_type %></p>
<p><%= #recipe.cuisine %></p>
<p><%= #recipe.difficulty %></p>
<p><%= #recipe.cook_time_min %></p>
<h3>Ingredientes</h3>
<p><%= #recipe.ingredients %></p>
<h3>Como Preparar</h3>
<p><%= #recipe.cook_method %></p>
<%= link_to 'Voltar', root_path %>
recipes_controller.rb
class RecipesController < ApplicationController
def index
#recipes = Recipe.all
end
def new
recipe = Recipe.new
recipe.title = [:title]
recipe.recipe_type = [:recipe_type]
recipe.cuisine = [:cuisine]
recipe.difficulty = [:difficult]
recipe.cook_time_min = [:cook_time_min]
recipe.ingredients = [:ingredients]
recipe.cook_method = [:cook_method]
recipe.save
redirect recipes/show
end
def show
#recipe = Recipe.find(params[:id])
end
end
routes.rb
Rails.application.routes.draw do
get 'recipes/show'
get 'recipes/new'
root to: 'recipes#index'
resources :recipes
end
Do not use symbols with form_for. This feature has repeatedly been flagged for depreciation and is not recommended, instead pass an actual variable.
<%= form_for(#recipe) do |f| %>
# ...
<% end %>
Avoid explicitly adding a URL to the form - if you embrace convention over configuration you can reuse the same form for the edit/update action.
If what you want is to actually persist the object to the database you would set it up like so:
# config/routes.rb
resources :recipies, only: [:new, :create, :show]
# get rid of that other junk
class RecipesController < ApplicationController
# GET /recipies/:id
def show
#recipe = Recipe.find(params[:id])
end
# GET /recipies
def index
#recipes = Recipe.all
end
# this action just displays a form
# GET /recipies/new
def new
#recipe = Recipe.new
end
# this action handles the form submission and saves the record in the db
# POST /recipies
def create
#recipe = Recipe.new(recipe_params)
# don't just assume the input is valid!
if #recipe.save
redirect_to #recipe # redirects to recipes/:id
else
render :new # just renders the view - does not redirect
end
end
private
# this method whitelists the parameters we want to assign
# if you are copying a hash key by key you're doing it wrong
def recipe_params
params.require(:recipe).permit(
:title, :recipe_type, :cuisine, :difficult,
:cook_time_min, :ingredients, :cook_method
)
end
end
What you want to do is really strange. If you really wanted to pass an unpersisted object through a redirect you would have to place all the parameters in the query string:
# recipes/show?recipe[title]="Foo"&recipe[recipe_type]="Bar"...
redirect_to(recipe_path(recipe: recipe.attributes))
And then extract out all the parameters again on the other end:
#recipe = Recipe.new(recipe_params)
That's why you render instead of redirecting when a record is invalid. And when you redirect to the new record you just use the id of the record instead of trying to pass all the attributes.
Here is what you have to do
First adjust your new method
def new
#recipe = Recipe.new
end
Now build out a create method to handle saving the data.
def create
recipe = Recipe.new <-- I think you will need this with this set up
recipe.title = params[:recipe][:title]
recipe.recipe_type = params[:recipe][:recipe_type]
recipe.cuisine = params[:recipe][:cuisine]
recipe.difficulty = params[:recipe][:difficult]
recipe.cook_time_min = params[:recipe][:cook_time_min]
recipe.ingredients = params[:recipe][:ingredients]
recipe.cook_method = params[:recipe][:cook_method]
if recipe.save
redirect_to recipe
else
# Probably some error handling?
end
end
Change where the form submits to (the variable has to match what is set in the new method so it submits to the create method, saves the data and then redirects to the show method.
<%= form_for(#recipe) do |r| %>

Rails create additional create action and redirect new to it

Let say I want to create an additional create action. Let's call it create2.
items_controller:
def new
#item = Item.new
and
def create
.....
end
def create2
.....
end
items/form:
<%= simple_form_for (#item) do |f| %>
<%= f.input :name %>
<%= f.submit %>
<%= end %>
routes:
post 'create2', to: 'items#create2', as: :create2
Once I submit form, how can have it to execute create2 instead of create?
for example your model is User with users_controller and you want to create another "new-create"
inside your routes you add some thing like this
resources :users do
collection {
get :new_special_user
post :create_special_user
}
end
inside your users_controller you create 2 methods
def new_special_user
end
def create_special_user
end
inside new_special_user.html.erb, with url that will direct to create_special_user method in user contoller, below is the sample
<%= form_for #user, url: create_special_user_users_path do |f| %>
<% end %>

Undefined method 'to_key' error using form_for

I am getting the following error when trying to use form_for in my Rails application:
undefined method `to_key' for #<Table::ActiveRecord_Relation:0x8a09ca8>
My config/routes.rb is:
root 'welcome#index'
post 'foo', as: 'foo', to: 'welcome#index'
The controller is:
class WelcomeController < ApplicationController
def index
#tables = Table.all
end
def test
#tables = Table.all
end
end
And the welcome/index.html.erb view is:
<p>
<%= form_for #tables, :url => foo_path do |t| %>
<%= t.text_area :name %>
<% end %>
</p>
I've tried to do the url workaround that had been suggested in the documentation, but I'm still getting the same error.
Does anyone know what I am doing wrong? I would like to understand this bug a bit more so I can better deal with it.
As per your code, index is returning a collection. However your view tries to define a form for it. This is unlikely going to be succeed.
Form is for an object, not for collections.
Perhaps you can do something like
def new
#table = Table.new
end
and in new.html.erb
<%= form_for #table do |f| %>
...
<% end %>
And if you would like to stick with index.html.erb with a form. Then you have to edit your routes for index action and also in controller it should be for creating a new object.
def index
#table = Table.new
end
Hope it helps!
I see your code have 3 not true things
As RESFUL standard then:
index action always go through with get action so in route file you should define again same that:
root "wellcome#index"
get "foo", to: "wellcome#index", as: :foo
form_for usually use with model object but not collect as you use #tables, if model object not save into database form_for using to create 1 object to database, otherwise form_for using update that object
if you want create form at index action you can follow me:
def index
#tables = Table.all
#table = Table.new
end
index.html.erb file
<%= form_for #table do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
you need create tables_controller to process request from form send to server. you run: rails g controller tables
In table_controller.rb you write same as:
def create
#table = Table.new table_params
if #table.save
redirect_to root_path, notice: "success"
else
redirect_to root_path, alert: "fail"
end
end
private
def table_params
params.require(:table).permit :name
end
so that. end. Have nice day!

Form_for data not accessible in custom action

I have a form_for #user to update a column in user model .
I have given html method as get and submitting to action look like
#user = User.find(params[:id])
#user_update_attribute(:phno,params[:phno])
and in view its look like
<%= form_for :#user, url: addphno_addphno_path , html: { method: :get } %>
The issue is I am not able to get data in controller action.the error is
could not find record of "id="
If you wanted to have a separate "phoneno" action:
#config/routes.rb
resources :users do
match :addphone, via: [:get, :post]
end
#app/views/users/add_phone_no.html.erb
<%= form_for #user, user_addphone_path(#user) do |f| %>
<%= f.text_field :number %>
<%= f.submit %>
<% end %>
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def addphone
#user = User.find params[:id]
#user.update( update_params ) if request.post?
end
private
def update_params
params.require(:user).permit(:phno)
end
end
If you wanted to use the update action (as is convention):
#config/routes.rb
resources :users
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def edit
#user = User.find params[:id]
end
def update
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:phno)
end
end
This will allow you to call:
#app/views/users/edit.html.erb
<%= form_for #user do |f| %>
<%= f.text_field :phno %>
<%= f.submit %>
<% end %>
Bottom line is that you should be using the second batch of code if you're updating your #user object.
I originally thought you wanted to add a phone number as associative data to your #user... but it seems that you just wish to add a phone number for the user. To do this, the above code will suffice.

Updating model data from form value ruby on rails

Hello I'm very new to ruby on rails. Here i have form
<%= form_tag do %>
<%= text_field_tag :fullname, params[:fullname], placeholder: 'fullname' %>
.
.
.
<%= submit_tag "save" %>
<% end %>
Those form is to update model data. In my controller I have
def updateUser
user = Users.find(session[:user_id])
user.fullname = params[:fullname]
user.save
render 'profile'
end
It's not working (data doesn't updated), but when I tried
def updateUser
user = Users.find(session[:user_id])
user.fullname = 'david'
user.save
render 'profile'
end
It's working (the data updated). I don't know where did I go wrong, please kindly help me. Sorry for asking such easy question, I'm a newbie to Ruby (and so does Rails), I searched but didn't get a suitable answer for this case. Thank you
I'm very new to ruby on rails
Welcome - let me give you some ideas!
--
Form
Firstly, your form_tag is not created properly. You need to put a route in this (so it knows where to submit the data):
<%= form_tag your_path_here do %>
This is for the form_tag helper, however, as you're editing an object, you'll probably be better using the form_for helper - which takes an actual object (value):
<%= form_for #user do |f| %>
<%= f.text_field :full_name %>
<%= f.submit "Submit" %>
<% end %>
This type of form has to be used with the resourceful structure of Rails, which is my second point...
--
Resources
Rails is built around the idea of a resourceful infrastructure (where every data object is a resource). If you're updating / editing an object, the typical explanation is that you'll be editing a resource, and consequently will want to employ rails' resourceful structure to handle it:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:edit, :update, :show]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.save
end
def edit
end
def update
#user.update(user_params)
end
private
def user_params
params.require(:user).permit(:fullname, :etc)
end
def set_user
#user = User.find params[:id]
end
end
This will allow you to define the resourceful routes for this:
#config/routes.rb
resources :users
Using this setup with form_for will work for you
Didn't notice in the beginning. Your form is incorrect. You did not specify a URL for action, and you put your submit tag within a link so basically your link is getting called not your form submitted.
<%= form_tag '/userEdit' do %>
<%= text_field_tag :fullname, params[:fullname], placeholder: 'fullname' %>
.
.
.
<%= submit_tag "save" %>
<% end %>
Make sure that you have specified a route for userEdit post method.

Resources