I have a register form for users to sign up to my rails app and I added a checkbox for users who do not want a special service. Here is the code from the view :
<%= form_for(#user, :url => { :action => "create" }, :html => { :multipart => true }) do |f| %>
<%= f.check_box :wantsaoc, :onchange => "check_field(this)" %>
[etc...]
<% end %>
In my model i have a wantsaoc method thats returns me a boolean depanding on some other attributes.
How can i handle the creation so that i can catch the wantsaoc parameter and behave depending on it ?
Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic before or after an alteration of the object state. For example,
class User < AR::Base
..
before_save :do_something
def do_something
if wantsoac.eql?('true')
.. # Do something
end
end
end
Go through various callbacks you can use and choose when and what you need to do.
Related
I am attempting to run a form destined for the app's home page, which uses simultaneously tableless and database-stored variables. I need to run validations on the form. I need to run validations, one in particular which compares two of the tableless values.
My eventual goal is to get the validations running client-side via the following gem:
https://github.com/bcardarella/client_side_validations
Initially though, I want to get the validations working upon form submission.
A first question that arises is which model should hold the mail validation logic. The homePage has to do many things, is not a model and, above all, the search results lead to a different controller.
Subsequently, although some validations are one-off and don't really need to be factored out, I would place the validation logic in app/validators/different_objects_validator.rb
class DifferentObjectsValidator < ActiveModel::Validator
def different_objects
errors.add(:different_objects, "cannot be less than quantity") unless
self.quantity >= self.different_objects
end
end
Form data is:
<%= form_tag result_quote_request_path, :method => :get do %>
Base <%= number_field :quote, :base, :size => 5 %>
Height <%= number_field :quote, :height, :size => 5 %>
Quantity <%= number_field :quote, :quantity, :size => 5, :value => 1 %>
Different source objects <%= number_field :quote, :different_objects, :size => 5, :value => 1 %>
<%= submit_tag "find", :name => nil %>
<% end -%>
Then
class Quote < ActiveRecord::Base
include ActiveModel::Validations
validates :different_objects, different_objects_valid => true
After restarting Thin web server, this results in:
ActionView::Template::Error (undefined method `name' for nil:NilClass):
But this is a bit of a red herring: without the validation, the form generates a search result
I realize many things may be wrong here. I am having trouble getting the proper focus of where the validations are invoked combined with tableless states.
Your error comes from the following:
<%= submit_tag "find", :name => nil %>
Simply do
<%= submit_tag "find" %>
I have user class (using devise) and my User class has email subscriptions
class User < ActiveRecord::Base
...
has_one :email_sub, :class_name => "Subscriptions::EmailSub", :dependent => :destroy
end
I have a route
match 'profile', :controller => 'users', :action => 'view_profile'
and related controller that calls a specific file
class UsersController < ApplicationController
def profile
#subscriptions_email_sub = current_user.email_sub
end
end
And in the profile.html.erb file have a form (technically in a partial included in the template but I don't think that will make a difference) where the user can use radio buttons to set subscription options (subscribed or unsubscribed). The button part of the form is below:
<%= form_for(#subscriptions_email_sub) do |f| %>
...
<tr>
<td>Announcements</td>
<td><%= f.radio_button :announcements, 'announcements', :checked => #subscriptions_email_sub.announcements %></td>
<td><%= f.radio_button :announcements, 'announcements', :checked => !#subscriptions_email_sub.announcements %></td>
<td></td>
<td>Updates about the website and service</td>
</tr>
<%= f.submit "Update Subscriptions", :id => 'update_subs' %>
<% end %>
I'm trying to add coffeescript so when the user clicks the button it saves the model and notifies the user the settings have been updated (or an alert if there's an error).
(Alternatively I'm fine saving the model and reloading the whole page.)
So far I have in users.js.coffee
$ ->
$('#update_subs').click ->
$('form').submit();
I can't figure out how to get the page to reload. I think the form submit will cause it to use the controller for Subscriptions::EmailSub which will then try to load app/views/subscriptions/email_subs/show.html.erb I suspect I may have to use javascript to save the class and the reload the page.
Thanks for any help.
You don't need that code in users.js.coffee, you can make the form remote:
<%= form_for(#subscriptions_email_sub), :remote => true do |f| %>
This will submit the form asynchronously, expecting a JS response (unless you specify another content-type such as JSON).
You'll need to have a controller action to handle this update, presumably your SubscriptionsController.
It should respond to javascript requests:
class SubscriptionsController < ApplicationController
respond_to :js
...
def update
#subscription = Subscription.find(params[:id])
...
respond_with(#subscription)
end
end
Then you can simply add an update.js.coffee file to your views/subscriptions folder and add any changes you want to make to the DOM in there, just as you would for regular javascript.
# update.js.coffee
alert "Email subscription updated!"
$('form').ignite_fireworks()
This question already has an answer here:
Rails simple_form server side validations lost URL params
(1 answer)
Closed 8 years ago.
I have a simple_form in my rails app that I want to pre-populate
some of the fields with URL parameters.
Example URL is:
http://localhost:3000/surveys/new?phone=8675309
This pre-populate the phone field corretly using this code:
<%= simple_form_for #survey, :html => { :class => 'form-horizontal' } do |f| %>
<%= f.input :state, :required => true %>
<%= f.input :zip, :required => true %>
<%= f.input :phone, :required => true, :input_html => { :value => params['phone'] } %>
<% end %>
The problem is if the form is submitted without a required field
the form is reloaded but the phone input value is not retained.
If the form is submitted without a value for zip
the reloaded page with validation errors in red URL is now:
http://localhost:3000/surveys
If, say, the state field is correct but no zip code the form is reloaded
with an error msg saying zip is required.
The state value is retained however the phone is not.
I guess it has to do with :value => params['phone'] in the f.input for the phone.
Is there anyway I can populate the simple_form with URL paramenters
on it's initial load and have those value retained if the serverside validation fails?
Many thanks.
Remove the :value from your view:
<%= f.input :phone, :required => true %>
and use this URL:
http://localhost:3000/surveys/new?survey[phone]=8675309
That should generate the "standard" survey parameter hash expected by the controller. In my test, that works the same as if the user had typed in the value, with the usual validation handling.
Inside the controller, the hash is called params[survey] with individual parameters represented as params[survey][phone], params[survey][zip], etc.
With the help of this answer, I found that you can generate the URL with the link_to signature link_to(body, url_options = {}, html_options = {}):
<%= link_to 'New survey', { :controller => "surveys",
:action => "new",
:survey => { :phone => "8675309", :zip => "10001" } },
{ :target => "_blank" } %>
Note that url_options is the first hash, and within that hash you have a survey hash for pre-loading values. Finally comes the optional hash of html_options (where I added target="_blank" for illustration purposes).
Thanks Mark,
I re-posted this question again a while back and it was answered corretly here:
Rails simple_form server side validations lost URL params
I'm trying to render the Devise edit password form within another view because I don't want to duplicate the edit pw logic.
I've tried the following (after generating the Devise views):
<%= render 'devise/passwords/edit' %>
<%= render 'devise/passwords/form' %>
And a number of other variations on render that all seem to give me the same error:
"ActionView::MissingTemplate in foo#foo
Missing partial devise/passwords/edit..."
This variation:
<%= render :file => 'devise/passwords/edit.html.erb' %>
Gave me some hope but the following error:
"undefined local variable or method `resource' for #<#:0x47ef0e0>"
around this line:
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
That makes me think I'm close (as that is code from the form that I want) but shouldn't that template be using the correct logic from the hidden Devise controller? Or do I need to do something in the routes file to get this to work?
Am I way off?
Try this:
<%= render :template => 'devise/passwords/edit',
:locals => {
:resource => my_user_model_variable,
:resource_name => my_user_model_name } %>
Where:
my_user_model_variable could be current_user
my_user_model_name could be "User"
I am experiencing problems with undefined method `to_key' for, on a form for polymorphic upload.
This is the form partial:
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<% end %>
This is the controller:
class UploadsController < ApplicationController
before_filter :find_parent
respond_to :html, :js
def index
#uploads = #parent.uploads.all unless #uploads.blank?
respond_with([#parent, #uploads])
end
def new
#upload = #parent.uploads.new unless #uploads.blank?
end
def show
#upload = #parent.upload.find(params[:upload_id])
end
def create
# Associate the correct MIME type for the file since Flash will change it
if params[:Filedata]
#upload.document = params[:Filedata]
#upload.content_type = MIME::Types.type_for(#upload.original_filename).to_s
#upload = #parent.uploads.build(params[:upload])
if #upload.save
flash[:notice] = "suceessfully saved upload"
redirect_to [#parent, :uploads]
else
render :action => 'new'
end
end
end
def edit
#upload = Upload.where(params[:id])
end
private
def find_parent
classes ||= []
params.each do |name ,value|
if name =~ /(.*?)_id/
#parent = classes << $1.pluralize.classify.constantize.find(value)
end
end
return unless classes.blank?
end
end
If i change
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
to
<%= form_for [parent, Upload], :html => { :multipart => true } do |f| %>
I get a new error: undefined local variable or method `parent' for #<#:0x21a30e0>
This is the error trace:
ActionView::Template::Error (undefined method `to_key' for #<Class:0x2205e88>):
1: <%= render :partial => "uploads/uploadify" %>
2:
3: <%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
4:
5:
6: <div class="field">
The "uploads/uploadify" partial is in this gist: https://gist.github.com/911635
Any pointers will be helpful. Thanks
From what I can see, your form_for should be something along the lines of
<%= form_for [#parent, #upload], :html => { :multipart => true } do |f| %>
as I'm assuming your upload object is nested within another object, similar to the following:
resources :posts do
resources :uploads
end
What form_for does when passed an array like this is construct the relevant path based on the class of the given objects and whether they are new records.
In your case, you create a new upload object in the new action of your controller, so form_for will inspect the array, get the class and id of #parent, then get the class and id of #upload. However, because #upload has no id, it will POST to /parent_class/parent_id/upload instead of PUTting to parent_class/parent_id/upload/upload_id.
Let me know if that doesn't work and we'll figure it out further :)
-- EDIT - after comments --
This means that one of #parent or #upload is nil. To check, you can put the following in your view
<%= debug #parent %>
and the same for #upload and see which is nil. However, I'm guessing that #upload is nil, because of this line in your controller:
# UploadsController#new
#upload = #parent.uploads.new unless #uploads.blank?
specifically the unless #uploads.blank? part. Unless you initialize it in the ApplicationController, #uploads is always nil, which means #uploads.blank? will always be true, which in turn means #upload will never be initialized. Change the line to read
#upload = #parent.uploads.new
and the problem will hopefully be resolved. The same is true of the other methods where you have used unless #uploads.blank?.
On a semi-related note, in UploadsController#find_parent, you have this line
classes ||= []
because the variable is local to the find_parent method, you can be assured that it is not initialized, and should rather write classes = [].
Also, you have this line of code
return unless classes.blank?
right before the end of the method. Did you add that in so that you return from the method once #parent has been initialized? If so, that line should be inside the each block.
Further, since classes isn't used outside of the method, why define it at all? The code could read as follows and still have the same behaviour
def find_parent
params.each do |name ,value|
#parent = $1.pluralize.classify.constantize.find(value) if name =~ /(.*?)_id/
return if #parent
end
end
Amongst other things, you'll see that this does a few things:
Avoids initializing a variable that is not needed.
Inlines the if statement, which helps readability for single line conditionals
Changes use of unless variable.blank to if variable. Unless your variable is a boolean, this accomplishes the same thing, but reduces the cognitive load, as the former is essentially a double negative which your brain has to parse.
-- EDIT - from email exchange about the issue --
You are correct - if #parent will return true if parent is initialized. As I mentioned on SO however, the exception to this is if #parent is initialized and set to false. Essentially what it means is that in Ruby, all values except nil and false are considered true. When an instance variable has not been initialized, it's default value is nil, which is why that line of code works. Does that make sense?
In terms of setting #parent in each action that renders form in the UsersController, which of these is the correct way to do this on the index action. I have tried all 3 but got errors
Remember that both #parent and #upload must be instances of ActiveRecord (AR) objects. In the first case, you set #parent to User.all, which is an array of AR objects, which will not work. Also, you try to call #parent.uploads before #parent is initialized, which will give a no method error. However, even if you were to swap the two lines around, you are calling #parent.uploads when parent is an array. Remember that the uploads method is defined on individual AR objects, and not on an array of them. Since all three of your implementations of index do similar things, the above caveats apply to all of them in various forms.
users_controller.rb
def index
#upload = #parent.uploads
#parent = #user = User.all
end
or
def index
# #user = #parent.user.all
#parent = #user = User.all
end
or
def index
#parent = #upload = #parent.uploads
#users = User.all
end
I'll quickly walk you through the changes I made. Before I start, I should explain that this
<%= render "partial_name", :variable1 => a_variable, :variable2 => another_variable %>
is equivalent to doing this
<%= render :partial => "partial_name", :locals => {:variable1 => a_variable, :variable2 => another_variable} %>
and is just a shorter (and somewhat cleaner) way of rendering. Likewise, in a controller, you can do
render "new"
instead of
render :action => "new"
You can read more about this at http://guides.rubyonrails.org/layouts_and_rendering.html Now on to the code.
#app/views/users/_form.html.erb
<%= render :partial => "uploads/uploadify" %>
<%= form_for [parent, upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<%end%>
On the uploads form, you'll see that I changed #parent and #upload to parent and upload. This means you need to pass the variables in when you render the form instead of the form looking for instance variable set by the controller. You'll see that this allows us to do the following:
#app/views/users/index.html.erb
<h1>Users</h1>
<table>
<% #users.each do |user| %>
<tr>
<td><%= link_to user.email %></td>
<td><%= render 'uploads/form', :parent => user, :upload => user.uploads.new %></td>
</tr>
<% end %>
</table>
Add an upload form for each user in UsersController#index. You'll notice that because we now explicitly pass in parent and upload, we can have multiple upload forms on the same page. This is a much cleaner and more extensible approach to embedding partials, as it becomes immediately obvious what parent and upload are being set to. With the instance variable approach, people unfamiliar with the code base might struggle to determine where #parent and #upload are being set, etc.
#app/views/users/show.html.erb
<div>
<% #user.email %>
<h3 id="photos_count"><%= pluralize(#user.uploads.size, "Photo")%></h3>
<div id="uploads">
<%= image_tag #user.upload.document.url(:small)%>
<em>on <%= #user.upload.created_at.strftime('%b %d, %Y at %H:%M') %></em>
</div>
<h3>Upload a Photo</h3>
<%= render "upload/form", :parent => #user, :upload => user.uploads.new %>
</div>
This is similar to the changes above, where we pass in the parent and upload objects.
#config/routes.rb
Uploader::Application.routes.draw do
resources :users do
resources :uploads
end
devise_for :users
resources :posts do
resources :uploads
end
root :to => 'users#index'
end
You'll see that I removed uploads as a top level resources in the routes. This is because uploads requires a parent of some sort, and so cannot be top level.
#app/views/uploads/new.html.erb
<%= render 'form', :parent => #parent, :upload => #upload %>
I made the same changes as above, passing parent and upload through explicitly. You'll obviously need to do this wherever you render the form.
#app/controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render :action => 'new'
end
end
def update
#user = User.find_by_id(params[:id])
#user.update_attributes(params[:user])
respond_with(#user)
end
def destroy
#user = User.find_by_id(params[:id])
#user.destroy
respond_with(#user)
end
end
I've removed any mention of #parent from the user controller, as we pass it through explicitly.
Hopefully that all makes sense. You can extrapolate from these examples and pass through the parent and upload object wherever you want to render an upload form.
[#parent, Upload] => [#parent, :upload]
<%= form_for [#parent, :upload], :html => { :multipart => true } do |f| %>
UPD
You should change places :upload and #parent
<%= form_for [:upload, #parent], :html => { :multipart => true } do |f| %>