Retaining form values in Ruby on Rails - ruby-on-rails

I am working on an ROR application and i would like retain form values after the form reloads on some error from the server side so that the user doesn't have to go through filling the form again.

This is standard functionality, which is enabled with use of an #instance variable. This allows you to send data to a Ruby class (controller) and process the returned data in a single instance
You'd do it like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new #-> notice instance variable
end
def create
#post = Post.new(post_params)
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body)
end
This enables you to maintain the instance of your newly created ActiveRecord object. It means each time you can't submit your form, you'll receive errors with the previously inputted data

Related

Overwrite permited_params if rails controller has been inherited

Here an example:
class Base<ApplicationController
private
def permited_params
params.require(:object_name).permit(:name, :description)
end
end
class Post<Base
private
def permited_params
params.require(:post).permit(:name, :description, :owner)
end
end
I'm getting an error ActiveModel::ForbiddenAttributesError when call action create. How I can overwrite this permited_params
Params, in general, have a good reason to exist and make sure that not everything can be saved into your database. However, if you want to permit all params you can call
params.require(:post).permit!
In case you just want to change the params you can change the attribute names.
params.require(:post).permit(:name, :description, :some_you_want, some_more ) etc.
In general, you should add all params you want to save into the list of permitted params. So you make sure that all the attributes you want to save will be stored and no more. You can have permitted_params in every controller. You do not need to call it permitted params. For instance you can call it like this in your posts_controller:
def create
#post = Post.new(post_params)
#.... your code
end
private
def post_params
params.require(:post).permit(:name, :description, :owner)
end
This also works for inherited controllers.
Instead of params.require(:post).permit(...
you can use whatever params you want, like params.require(:reply).permit(...
The required param will throw an error if it is not available. So you need to make sure it exists for example by
#post = Post.new
Other params are optional and will not cause an error by default.

Understanding Rails Instance Variables

In my application I have a products_controller that makes use of instance variables. My understanding of instance variables in Ruby is that you can use them within different methods of the same class. So why is it that we use the same instance variable in multiple methods of rails apps? Below we have an instance variable #product set twice, is the #product variable in the new action not overwritten when we use it in the create action?
I am just a little confused as to the scope of these variables within methods of the same class.
def new
#product = Product.new
end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Instance variables are in the instance class scope. In Ruby on Rails, because the way how that API was built, instance variables are also available in the views.
You need to be aware of that new and create methods are commonly used in different ProductsController instances.
First request: GET http://localhost:3000/product/new
When you ask for the new action (I suppose that is a form), Rails API implementation at a given point creates an instance of ProductsController and sends the new message to that instance (calls the new method). Then, the instance variable #product is created and available in any method, or in any view that the action renders. At a given point, Rails replies with a web page and the class instance, and all its instance variables, are destroyed (won't be available anymore).
Second request: POST http://localhost:3000/product/create
When you submit the form for database persistence, again a new controller instance is created, and the create method is called. Because is a new instance, the #product doesn't have any value.
Note, however, that there is a difference between rendering a view (like its happening in the new action) and a redirect (like you do in the create action if #product.save is true). When you render, you remain in the same controller instance, with you redirect, new server request happens, so the previous controller instance is destroyed and a new controller instance is created.
The before action
before_action is called before you actually start executing the action code. In Rails perspective, an action is not a Ruby method. The class method is the definition of that action:
From Rails guides:
A controller is a Ruby class which inherits from ApplicationController
and has methods just like any other class. When your application
receives a request, the routing will determine which controller and
action to run, then Rails creates an instance of that controller and
runs the method with the same name as the action.
The action acts as an entry point determined by the routes. If you call create inside new, it won't trigger that before_action again.
No, it doesn't overwrite it. An instance variable (#variable_name) is accessible within all methods of a single instance object of a class.
Now imagine, there's a client request to the "new product route". Rails creates an instance object of your products_controller and invokes only the new action of that instance. That defines #product = Product.new, renders your new.html.erb template and that's it. After that, the controller instance will be forgotten.
Next, your client clicks the "create product button" of your "new product form". Another request arrives the server. Rails creates another instance of your products_controller and calls the create action. The new action isn't invoked. And so, you have a new product instance (#product = Product.new(product_params)) with the attributes sent by the form.
Of course, you could call the create method from your new action ...
# only an example
def new
#product = Product.new
create
end
... or the other way round. This would overwrite the #product variable. But why should you do that?
An instance variable is accessible in any instance method, for that instance of the class. Multiple instances of a class will each have their own copy of a given instance variable.
You mentioned the new and show methods -- did you mean the new and create methods?
Note that the new method shown is an instance method, and not the class method that you are accustomed to seeing used to instantiate objects. I think the point of the two methods is that only one of the two will be used to created a given instance, so there is no issue of collision.
But yes, if you called one of the methods and then called the other, the second would overwrite the value assigned by the first.
In a rails controller instance variables are accessible from :
When a Rails controller action renders a view, the instance
variables can be accessed over there.
It is also available within instance methods of that controller. (Also the superclass instance methods)
So here #product in new and create both refer to different instance variables.
So in your case taking example of the new action, the variable #product is accessible in your new form because it's an instance variable.
No, you are not able to set one instance variable to all until, you are not setting it with before_action method.
For example below controller, You are on index page and after going to create new product page you will get an error in a form. Such as not defined...
It means didnt set a variable to new method that we called in index method.
def index
#products = Product.all
#adding this
#product = Product.new
end
def new
# leaving empty
end
As the same thing will happens in create method. if we are not defining, it will return an error.
To set an instance variable once, You have to make it like this below. But its not right way to make it, its really messy and not suggestible.
class ProductsController < ApplicationController
before_action :set_new_product_variable, only: [:new, :create]
def new
end
def create
#product.title = params[:product][:title]
#product.price = params[:product][:price]
#product.description = params[:product][:description]
#product.image = params[:product][:image]
#product.blabla = params[:product][:blabla]
#look above its really messy and it gets bigger. Below example much more efficient, it covers everything in just one line of code.
#product = Product.new(product_params)
redirect_to #product if #product.save
end
private
def set_new_product_variable
#product = Product.new
end
end

Passing params[:id] to create method in Rails?

I'm trying to write a create method that collects the ID of the profile the user is currently viewing, along with some other information that is irrelevant to this question. However, because the create method POSTs rather than GETs (as I understand it), the value of params[:id] doesn't exist so it's always null. My code is as follows:
class PostsController < ApplicationController
def new
#Post = Post.new
end
def create
#Post = Post.new(post_params)
#Post.user_id = current_user.id
#Post.target_id = params[:id] #this
if #Post.save
redirect_to :back, notice: "You added a post!"
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Is there a way to get the value of params[:id] from elsewhere, perhaps from my Users controller in the show method where it actually exists?
Keep in mind that I was successfully able to create a hidden field in the Posts form, but I didn't like the fact that users were able to edit the value using Developer Tools, allowing them to change what profile the post would go to.
If there is a direct relation between the Target and the Post model, you should express this in the controller and model structure: link
This expresses your intention and it provides all the rails automations like routing, url helpers, form helpers, a.s.o.
In your concrete example, my guess is the Target would have many Posts:
class Target < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :target
end
Which would lead to the following route structure:
resources :targets do
resources :posts
end
To create a new post for the current target you would post to:
targets/:target_id/posts
And the target id would be accessed via params[:target_id]

Multiple forms in Rails

Not sure whether my database architecture is correct for rails. However below is my database architecture
Database Relations
Each User instance has only one PhoneBook instance.
A single Phonebook instance can have multiple Contact instances
A single Contact instance can have multiple Mobile instances
A single Contact instance can have multiple Email instances
The question is how should I implement my controller and views if I want to add a new contact for a signed in user in his phonebook.
you can do that with accepts_nested_attributes_for:, like a nested form
you could define the current user like so
controllers/application_controller.rb
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
# or find_by_authtoken!(...)
end
then you could do
controllers/phonebooks_controller.rb
def create
#phonebook = Phonebook.create(phonebook_params)
if #phonebook.save
# redirects here
end
end
.....
def phonebook_params
params.require(:phonebook).permit(:phonebook_params....).merge(:user_id => current_user)
end
and in your contacts controller
controllers/contacts_controller.rb
def create
#contact = Contact.create(contact_params)
if #contact.save
# redirects here
end
end
.....
def contact_params
params.require(:contact).permit(:contact_params....).merge(:user_id => current_user, :phonebook_id => current_user.phonebook)
end
Like that, you can use your forms in a simple manner, without having to generate routes like /user/id/phonebook/id/contacts
in addition to the links below the first answer, maybe have a look at this basic form. It it is not a direct answer to your question, but maybe it'll help you getting an idea of how a form could look like.

where does this param symbol come from in Rails session

I'm reading Rails 3 Way by Obie Fernandez. He's demonstrating the use of a plugin Authlogic, and created a User and UserSession model and a UsersController and UserSessionsController.
He hasn't created any views (but he might assume some exist)
In the UserSessionsController, he creates this code
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
redirect_to user_path(current_user)
else
render :action => :new
end
end
def destroy
current_user_session.destroy
redirect_to new_user_session_path
end
end
My question relates to the create method. When he writes
UserSession.new(params[:user_session])
where is :user_session coming from? I undersdtand that UserSession.new instantiates a new object, but where do the params come from? and what names would they have?
Does it depend on something in the imaginary view? or are these params automatically generated by Rails based on the name of the Models?
params is a special hash that is passed to all actions, regardless of the type. If a given action has no parameters, then it's simply empty. It's how you can pass parameters from a page/form/URL parameters into the action. One of the most common sources of parameters are data elements from a form.
In the case of authlogic, it contains user credentials for creating the user session (username, password).
Check out the parameters section for more information.

Resources