Setup default CSS class for Rails' form builder helper methods - ruby-on-rails-3

I'm looking for a generic way to apply some external CSS frameworks in a Rails application. These frameworks typically define a set of class names which should be used for certain HTML elements.
Consider jQuery UI. To achive a consistent form style, you would to something like this:
# in a view
<% form_for #foo do |f| %>
...
<%= f.text_field :bar, :class => ['ui-widget', 'ui-widget-content', 'ui-widget-container', 'ui-corner-all'] %>
or
<%= f.text_field :bar, :class => 'ui-widget ui-widget-content ui-widget-container ui-corner-all' %>
<% end %>
Doing this for each input field is not DRY at all.
Even a helper method like
# in application_helper.rb
def jquery_ui_classes
'ui-widget ui-widget-content ui-widget-container ui-corner-all'
end
# in a view
<%= f.text_field :bar, :class => jquery_ui_classes %>
or
# in application_helper.rb
def jquery_text_field(form_builder, method, opts = {})
ui = ['ui-widget', 'ui-widget-content', 'ui-widget-container', 'ui-corner-all']
klass = [opts.delete(:class), ui].flatten.compact
form_builder.text_field method opts.merge(:class => klass)
end
# in a view
<%= jquery_text_field f, :bar %>
does not look right (or more DRY), since you still would have to touch the generated form view...
Alternative ways might now be monkey-patching the InstanceTag class or hacking into the form_for helper.
Has anyone done this before?
By the way: I would avoid using jQuery (or Javascript in general) to apply the class attributes, since JS might be disabled or blocked or even delivered with delay and cause a flickering...
Cheers,
Dominik

https://github.com/phallstrom/form_helper_css
You can't specify the classes you want, but it will add reasonable class names to all inputs to allow you to easily target them (in all browsers).

Related

In Rails 3 how does the simple_form_for method know whether to include the params[:id] in the action attribute?

I'm new to rails, and I'm trying to figure out how to make the [action] attribute on a form dynamic so that I can reuse the form markup.
In MVC.net it's easy as you usually specify the :controller and :action.
However in rails there's just a "simple_form_for(#my_model)" method.
If I browse to /my_models/new the action attribute is:
action = "/my_models"
But if I go to /my_models/1/edit the action attribute is:
action = "my_models/1"
What if I want to create a new action for handling POSTs of my_model AND still reuse the same _form.html.erb... how does that work?
I think it's actually rails doing this
https://github.com/rails/rails/blob/master/actionpack/lib/action_view/helpers/form_helper.rb#L230
In short it just infers the url based on the resource and whether it is new or an existing one.
SimpleForm's FormBuilder is inherited from ActionView::Helpers::FormBuilder
You can check out that code https://github.com/plataformatec/simple_form/blob/master/lib/simple_form/form_builder.rb
I guess I'm not understanding the last part. Say you wanted to use a custom action instead of the inferred ones you could just make a partial of the form elements and use that partial for all forms.
so _form.html.erb
<%= simple_form_for #my_object do |f| %>
<%= render 'form_elements' %>
<% end %>
_custom_form.html.erb
<%= simple_form_for #my_object, :url => custom_url do |f| %>
<%= render 'form_elements' %>
<% end %>
_form_elements.html.erb
form elements as usual

Correctly generate a form builder object while discarding the HTML without a deprecation warning

I have a partial which generates a div with some form fields in it. It uses the form builder variable "f" which is provided as input to correctly name the fields in the parameter has (fields are actually nested attributes, so the name is like "[author][book][0][title]").
I want to use that same partial when receiving an AJAX call to regenerate the div based on new user information. I am currently using <% form_for ... |f| %> in my erb file, but that generates a warning that "<% %>" is deprecated.
My erb file looks like the following:
<% if f.nil? %>
<% form_for(#author, :id => :coupon_form) do |f| %>
<%= render "books_detail1", :f => f %>
<% end %>
<% else %>
<%= render "books_detail1", :f => f %>
<% end %>
So what is the correct way to create a form builder context while discarding the generated HTML?
The correct answer is to use fields_for. It generates the same form builder object without the html. I lost track of this in it's use for sub-forms, but it's really the same thing.

RoR: form_for action button doesn't respond

I'm coding the project from Ruby on Rails Tutorial: Learn Rails by Example and am having trouble with the following and unfollowing features.
I have a piece of HTML in one of my pages that looks like this:
<%= form_for current_user.relationships.build(:followed_id => #user.id),
:remote => true do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<div class="actions"><%= f.submit "Follow" %></div>
<% end %>
My RelationshipsController has a create method, but it is never called. The same applies to my 'unfollow' html and corresponding destroy method. Is there something I need to add to my project to let Rails know that the relationships.build method should call the create method, or is that automatic?
Thanks in advance.
RelationshipsController has a create method
current_user.relationships.build()
Is it create or build?
Show us the controller code. Since you use "remote=> true" you probably need to change the "respond_to" code in there and create a js.erb file.

Ruby on Rails: Custom Actions (Follow up)

This question is a follow up to this previous question: Ruby on Rails: Custom actions
As a follow up, what would be the syntax to use a custom action in a form_for? For my app, I have a partial called _invite_form.html.erb, and set the form to have a :url specification that I thought would link the form to the invite action on the Users controller:
<div id = "invite_form">
<h1>Invite</h1>
<%= form_for(invited, :url => invite_user_path) do |f| %>
<div class="field">
<%= f.text_field :email, :class => "inputform round", :placeholder => "email" %>
</div>
<div class="actions">
<%= f.submit "Invite", :class => "submit_button round" %>
</div>
<% end %>
</div>
This partial is called on certain pages, and this error is given:
"No route matches {:action=>"invite", :controller=>"users"}"
In my routes.rb file I have included the appropriate lines:
resources :users do
member do
get :invite
post :invite
end
end
Why is it that the route doesn't work? How do I change these files to make the form use the action "Invite" on the Users controller?
** Forgot to mention earlier: I defined invited in the Users helper: users_helper.rb:
module UsersHelper
def invited
#invited = User.new(params[:user])
end
end
As you don't have a persistent User just yet, make this a collection operation by:
Changing invite_user_path to invite_users_path in your controller
Changing member do to collection do in your routes
invite_user_path expects a user as an argument. Try invite_user_path(invited). You will also need to save the user before you can compute a path to it.
To experiment, go into rails console and see the difference between the following:
app.invite_user_path
app.invite_user_path(User.first)
app.invite_user_path(User.new)

Ajax Callbacks in Rails 3 with Prototype, not jQuery

I'm upgrading an app from Rails 2 to 3 and am reworking all of the remote functions to use Unobtrusive Javascript. Where I'm struggling is handling ajax callbacks in UJS.
There are a lot of resources I've found that show how to implement these callbacks with jQuery, but not much for prototype. Perhaps you can help me figure this out.
In Rails 2, I had this:
<% remote_form_for #foo, {:loading => "loading_function()", :complete => "complete_function()" } do |f| %>
...
<% end %>
In Rails 3, I have this:
<%= form_for #foo, :remote => true do |f| %>
....
<% end %>
From what I've figured out so far (which may be wrong), I need to attach my old loading/complete functions to the form so that they'll be fired by the handleRemote function in Rails.js. I'm just not sure how to go about that.
Again, I'm doing this in Prototype. So answers specific to that framework are appreciated.
The answer is the following:
<%= form_for #foo, :remote => true do |f| %>
...
<% end %>
...
<script type='text/javascript'>
$('edit_foo').observe('ajax:before', loading_function());
$('edit_foo').observe('ajax:complete complete_function());
</script>
Try this link. Yes, it is JQuery, but JQuery and Prototype do not differ the way how things work together. Here is a code fragment that adds a new task directly in the index page - and it uses Prototype:
views/tasks/_newform.html.erb:
<%= form_for(#task, :remote => true) do |f| %>
<div>
<%= f.label 'Add a new task: ' %>
<%= f.text_field :name %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
views/tasks/index.html.erb:
<div id='newform'>
<%= render :partial => "newform", :locals => { :#task => Task.new } %>
</div>
views/tasks/create.js.rjs:
page.insert_html :after, 'tablehead', :partial => #task
page.replace_html 'newform',:partial => "newform", :locals => { :#task => Task.new }
Edit: you need to add "format.js" to our create method of the task controller
For people with a similar issue, it may also help to look at the source code for the remote helpers in the Rails 2.3.x source code.
In my case, I wanted to figure out what to do with the ':update' parameter, as in:
remote_form_for(#obj, :update => "new_obj", :before => "some js code") do |f|
I had to find the update functionality in the remote_function code.
For my specific issue, it looks like it's impossible to get the equivalent of :update with Rails 3 UJS helpers. The rails.js in Rails 3 wraps :remote => true requests with the Ajax.Request(...), whereas the :update function in Rails 2 wraps Ajax requests with Ajax.Updater(...). For people looking to replace the :update feature from Rails 2, I see 2 options:
Switch to jquery-rails, so that you can access the response from the Ajax request, with code like this:
$("#elem").bind("ajax:complete", function(et, e){
$("#results").html(e.responseText);
});
Write your own Prototype based code to grab the form and submit it via ajax, using Ajax.Updater(...) instead of Ajax.Request. Do NOT use :remote => true, since this will attempt to use Ajax.Request.
Side note: I played around with the callback object provided in the ajax:complete event
$('new_obj').observe('ajax:complete', function(request){
console.info(request);
});
The request object doesn't appear to contain the response anywhere in it. It is pretty massive, though, so I could be wrong. Hopefully this will help someone else trying to upgrade from Rails 2 to 3, though.
There's a way to get the response from the Ajax.Request invocation, if you were using remote_form_for with :update option. So, you probably don't need to change it to use Ajax.Updater as a workaround. Basically, you use respone.memo.responseText, in your example it would be something like this:
$('new_obj').observe('ajax:complete', function(response){
console.info(response.memo.responseText);
// Probably you would use it like this:
$('new_obj').update(response.memo.responseText);
});