Marrying Rails to an HTML form - ruby-on-rails

I am very new to Rails, and been asking lots of questions recently, I'm getting increasingly frustrated with figuring out how to work things in rails (i.e logging to console, routes, etc..), now I'm trying to figure out how to communicate between an HTML form (and please raw HTML, not form helpers or any of that voodoo magic, I really am trying very hard to like rails, so one thing at a time), if you want to add helpers, please explain what they are doing, where they go, how to use, etc...
I've tried all the examples on the first/second google page (gruesome I know! second google page?!)
What I am looking for is an example, and an explanation.
Also, how does it fit in with routes? (i.e what would be the proper way of setting up a controller/view, and routing them without the controller in the URL)
Please keep in mind, I am not using any models, just controllers, and views.
Your thoughts are much appreciated.

Oh no! Sorry that Rails is giving you grief, especially about form helpers! It's a shame because Rails' form helpers are pretty great, and IMHO are a super useful part of Rails in reducing code duplication, and allowing you to not worry about the specific field names that you are submitting to a server. (They're hard to get accustomed to, but worth it.)
Let me give you a summary of Rails form helpers. For starters, let's take this a simple example
<form action='/users' method='POST'>
<input type='text' name='username' />
<input type='password' name='password' />
<input type='submit' name='Create user' />
</form>
Ah, the most basic user signup form imaginable. This will POST to /users, which is REST-convention for "create a user".
The Rails Routing guide tells you that if you have a controller UsersController (in app/controllers/users_controller.rb -- the filename must be exactly this) and a route resources :users, then this will call the code in the create method of UsersController, and you will have access to the form POST data via the params hash, i.e. params[:username], params[:password], etc.
It is in that controller action where you would create an instance of your User model and save it to the database. (If you aren't using models, you're missing out on most of the magic of Rails's forms.)
Okay. So far so good, right?
Let's spice things up a bit by using Rails's form helpers.
A quick rewrite of the form using basic Rails form helpers would look something like this.
<%= form_tag '/users', method: 'POST' do %>
<%= text_field_tag 'username' %>
<%= password_tag 'password' %>
<%= submit_tag 'Create User' %>
<% end %>
So far, no Rails magic - it's a straight translation into HTML.
Let's add some Rails marrying.
Again, assuming you have a User model with a username column and a password attribute (attr_accessor :password), and your UsersController's #new method looks like this:
def new
#user = User.new
end
Then, in this form (which lives in app/views/users/new.html.erb), you can use that unpersisted User object to make the form more intelligent:
<%= form_for #user do |f| %>
<%= f.text_field :username %>
<%= f.password :password %>
<%= f.submit 'Create User' %>
<% end %>
Note the do |f| part of the form_for line. The f is a form object, which remembers the object it describes. So, you can call f.text_field :username, and the field will magically have the existing value of #user.username populated as the value of the <input>.
Then, if you have a UsersController#create method that looks something like this:
def create
# Instantiate a new user with the inputted parameters from the form
#user = User.new(params[:user])
if #user.save
# user is saved successfully! redirect somewhere....
else
# user couldn't be saved successfully, because of some rule that you
# define on the User model - like making sure that usernames are
# unique, or something like that.
#
# so, we can use the attributes on #user to prepopulate the new user form
render :new
end
end
Anyway, I hope that gives you a good overview of why Rails form helpers can be useful, and how to use them with your models. Let me know if you have any questions!

Related

Why is my Rails form helper written improperly?

I know I've written it wrong, but I'm looking at the documentation and can't figure out how.
My model is Quote and has three fields, body, attribution, and work, all strings. The form is intended to add a new quote to a page of quotations.
on main/index.html.erb
<%= form_for(:quote, url: {action: 'create'}) do |f| %>
<%= f.text_field :body %>
<%= f.text_field :attribution %>
<%= f.text_field :work %>
<%= submit_tag "Submit" %>
<% end %>
in main_controller.rb
def create
Quote.create(body: params[:body], attribution: params[:attribution], work: params[:work])
end
The form submits, and an entry is saved to the database -- but it's a totally blank entry. I'm not sure why. Help would be appreciated!
Three things:
The way rails forms are supposed to work, you're not meant to get body, attribution, etc independently, they should be wrapped up into a quote object. But...
In your form, your not properly binding an object to the form the way rails expects. You can read more in the documentation here: http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object. You could also generate a fake scaffold rails generate scaffold HighScore game:string score:integer to generate a fake model and see an example of how it's supposed to work. The default scaffolding even has simple examples of how to deal with save errors.
Finally, as #Paven suggested, when you get confused, be sure to look at what's going on in your log - i.e. what params are being posted to your create action. That is always helpful and a good way to diagnose problems quickly.
Your form does't need the action argument. The form_for helper uses ActiveRecord objects to determine the path, meaning as long as you build your object correctly, you won't need to determine your path individually:
<%= form_for #quote do |f| %>
Secondly, you'll want to look at your create method:
#app/controllers/quotes_controller.rb
def new
#quote = Quote.new
end
def create
#quote = Quote.new(quote_params)
end
private
def quote_params
params.require(:quote).permit(:body, :attribution, :work)
end
The problem is you're not sending an ActiveRecord object to your form_for helper. You can read the explanation here:
In Rails, this is usually achieved by creating the form using form_for
and a number of related helper methods. form_for generates an
appropriate form tag and yields a form builder object that knows the
model the form is about. Input fields are created by calling methods
defined on the form builder, which means they are able to generate the
appropriate names and default values corresponding to the model
attributes, as well as convenient IDs, etc. Conventions in the
generated field names allow controllers to receive form data nicely
structured in params with no effort on your side.
In order to get the form working correctly, you need to be able to provide a valid ActiveRecord object (#variable), which the helper can use to determine the url etc
My code above helps you provide a new ActiveRecord variable, and allows you to use it in the form. This should allow the form_for method to send your data to the create method, which will then create & save an object in the db for you

passing non-model fields with ActiveRecord

I have a situation in which I'm passing additional fields to form_for function, which don't belong to a model. Those fields are: redirect_to, redirect_time. I use those fields to know where to redirect the user after the form is successfully submitted - I don't store them, just read in the controller.
Now, everything is working perfectly when I have:
<%= form_for #mymodel .. %>
<%= if defined?(redirect_to) %>
<%= hidden_field_tag :redirect_to, redirect_to %>
<% end %>
<% end %>
And read in controller:
def action
if params['redirect_to']
redirect_to params['redirect_to']
end
end
But the problem occurs when the form has an error, Rails of course, won't pass those params that don't belong to the current resource.
Whats the most elegant way to deal with this situation in Rails to preserve those fields when form submission fails?
Thanks for help
I think you'd be better off storing these variables within the session rather than the form. Firstly it will make them much harder to interfere with if people started poking about in the source of your web page, and secondly they'll be available to whatever action or controller you need them in once you're done with processing the submitted form.
Since they're not related to the form itself it feels much cleaner abstracting them outside of it.
Just set the session variables you need in the controller prior to rendering the form:
session[:redirect_to] = url_to_redirect_to_after_submitting_form
Then you can redirect there after saving the record in your create action:
def create
# ... save record
redirect_to session[:redirect_to]
end
Passing non-model fields through ActiveRecord is poor solution to your problem of routing.
Keep to convention and use ActiveRecord as an interface to the state of your application data.

What is the best way to display form data in rails?

I am very new to Rails.
I am looking for the most common and 'rails-like' way to display the output of form data. I know that Simple form and Formtastic are good options to write concise code and display input fields in a user friendly manner. However, I am looking for something slightly different, I want to display the data from these forms as output, and I want to do it in a similar and concise manner. I haven't been able to find anything the last few days that I have been searching around so I figured I should ask Stack Overflow.
What do you normally do:
Write custom helpers to be able to write concise code and display the output data?
Use Simpleform/Formtastic, disable the fields, and modify the css to
make it look the way you want?
Is there a common gem that does this? Or perhaps another way I haven't thought about.
First, thanks for the initial responses. I thought I should clarify my question. Since the answers don't quite hit the mark. I already have a show method in the controller, as well as a view template for it. Currently I am displaying each field individually in the form for about 12 fields using form_for. See example snippet below:
<%= form_for(#event) do |f| %>
<div class="row-fluid">
<div class="span6">
<%= render 'shared/error_messages' %>
<%= f.label :title %>
<%= f.text_field :title, :disabled => true %>
<%= f.label :start_date %>
<%= f.date_field :start_date, :disabled => true %>
</div>
</div>
<% end %>
I guess maybe a better question would be, is their an equivalent to form_for method that display data for users, not to edit it but just to read it? It seems like their would be a a standard way to do it that I haven't discovered.
The easiest way to build forms is to use rails g scaffold Example name:string This would generate the Model, Views, Controller, and the necessary database migrations for a Model named Example with a Name attribute that is a string.
You would then use html and css to style the view how you want.
There is a ton of useful info for you on the rails guides here.
Although this question is rather ambiguous, you must appreciate that this functionality is exactly what Rails is built for (submit data & be able to display / manipulate it somewhere else)
Data
The data in an MVC application is bound by one very important factor - a database
If you're submitting data through a form, your goal is to store it in your database, and display it in other views / controller methods
Therefore, the blunt question to your answer is to abide by MVC processes, and save your data to a central repository (database or other), which you can call later:
View
To get your data into your data store, you first need to submit it
The view part of MVC is where you can display your UI, and consequently is where you can put your form. There is a whole tutorial about how to design this here, but for demonstrations' sake, here's an example of how you'd use your form:
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Controller
The data you input into your view will be sent to your controller on save
The data is sent via the HTML POST method to be read by Rails as a params[] hash. This hash contains all the sent items from your HTML form, and can be used like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body)
end
Model
The .save method on your newly created Post object, basically tells your model to put the data into your database (or other data store), which you can then pull later (using the .find method or similar)
#app/models/post.rb
Class Post < ActiveRecord::Base
#stuff here
end
It's important to note your models don't store your data, they simply provide an interface (API if you will) to save the data into some sort of data store (namely, a DB)
Models are super important because they allow you to structure & relate your data, creating the ability to deploy really deep & powerful applications

how form_for works in Ruby on Rails

I am an newbie. I have read the API documentation. But still don't understand how form_for works.
Firstly, from Ruby on Rails Tutorial, the form for follow button:
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
I understand current_user.relationships.build(followed_id: #user.id) means a new record. But why can we not just submit and trigger controller to save the record without hidden_field? Why do we still need to post followed_id to controller?
Secondly, in hidden_field, what does :followed_id means? I believe that is a symbol, i.e. it equals only "followed_id" not a variable of id. If that is only the name of the input field, then what is its value?
Thirdly, how does form_for know where the submission should be sent to? Which controller and action the form_for will post to?
Fourth, how does params work with form_for? In this follow button case, params[:relationship][:followed_id] will return #user.id in controller. How does it know the first hash attribute is :relationship? We have neither mentioned form_for :relationship nor form_for #relationship.
I know these questions can be very dumb, but I am really stuck. Any help will be appreciated.
I didnt do that tutorial so mind me if i dont answer directly to your question.
Take a look at the rails guide about form helpers and it explains in details your questions, probably in a more articulate way than i can.
form_for(path/to/your/controller/action) is a helper method to create HTML form elements with the url path to the POST or GET request. The helper knows if it should be a new record or an update record based on what you are asking to do in your controller action.
For example
In your controller
def new
#my_instance_variable = Myobject.new
end
In your view new.html.erb
<%= form_for #my_instance_variable do |f| %>
...
<% end %>
In your case the logic was directly written in the helper and you could also directly write
<%= form_for Myobject.new %>
Both will result with the following html
<form action="/myobjects/new" method="post">
# in this case rails knows its a `POST` request because the route new action
# is by default a POST request. You can check these routes and their request
# by using `rake routes` in terminal.
Then the hidden_field is another helper to contain a value, in your case the #user.id that will be passed as parameter then saved as a Create or update action for the given object. The reason it doesnt add the value in the hidden field tag is because you already have a model association that knows the id of user since the link of form uses the build method with user id.
Last part you need to understand the form_for link logic
current_user.relationships
# implies the association of the current_user has many relationships
current_user.relationships.build
# .build is a method to populate a new object that can be save as a new record
# means you will create a new relationship record by populating the user_id
# column with the current_user.id and the followed_id with the target #user.id
After reading the book The Rails 4 Way, I understand form_for better now.
11.9.1.5 Displaying Existing Values.
If you were editing an existing instance of Person, that object’s attribute values would have been filled into
the form.
in this way, when we build the relationship by usingcurrent_user.relationships.build(followed_id: #user.id), the relationship instance will be created and gain attribute followed_id. So that, instead of "creating" a relationship, we are actually editing the relationship by the form.
Then Rails will know you are editing and load the existing attribute "followed_id" to the field. Therefore, we don't need to assign value to the field like using f.hidden_field :followed_id, value: #user.id.
And the reason why we have to use a field to pass followed_id to params is because HTTP server is stateless, it doesn't remember you are creating a relationship with which user.
One of the advantages of writing form_for current_user.relationships.build(followed_id: #user.id) instead of standard form_for #relationship is we don't need to write "if-condition" in controller like this:
unless current_user.nil?
if current_user.following?(#user)
#relationship=current_user.relationships.find_by(followed_id: #user.id)
else
#relationship=current_user.relationships.new
end
end
params will be sent to the controller which belongs to the instance's model. "post" method will go to action create, "delete" will go to destroy, "patch" will go to update, etc.
params will be a hash with another hash inside like { instace_name: { field_1: value1, field_2:value2 } } or full params as below
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"afl+6u3J/2meoHtve69q+tD9gPc3/QUsHCqPh85Z4WU=",
"person"=>{"first_name"=>"William", "last_name"=>"Smith"},
"commit"=>"Create"}

simple_form_for: Removing root model name from params

When I create a form using simple_form_for #model, upon submit the post params has all the attributes grouped under params[model]. How do I get simple_form to drop this grouping and instead send it directly (under params root)?
<%= simple_form_for #user, do |f| %>
<%= f.input :name %>
<%= f.input :password %>
<%= f.submit %>
Now the name and password attributes would normally be sent under params[:user][:name], params[:user][:password] etc. How do I get simple_form to post these as params[:name], params[:password] etc.?
Thanks!
Ramkumar
ps: In case you are wondering why I need this, the bulk of my app is to serve as an API and I have built a set of methods to validate a request which expect some attributes to be in root. In a rare instance (forgot password), I actually need to present a form and I am looking for a way to use these methods.
you can explicitly define the name for an input by passing input_html to it:
input_html: { name: :name }
(needed this myself for sending an resource to a thirdparty endpoint with redirect to my side which relied on the plain attribute names, but i actually wanted not to built up label and input via the tags ;) )
also see simple form builder impl
Two ways I can think of:
The first is, don't use simple_form to build your form, but do it by hand or with the form_tag and *_tag methods. These will allow you to more closely specify what parameters are used in your form.
If you want to keep simple_form, though, then have it call a different controller action. Refactor the controllers to strip out the logic into a separate method. Something like:
class UsersController
def create_from_api
controller_logic(params)
end
def create_from_form
controller_logic(params[:user])
end
def controller_logic(params)
[actual work happens here]
end
end

Resources