In order to not repeat myself, I would like to create a function that render a form block (text field or text area) in a specific way. I want a result like this one (using Haml, and twitter-bootstrap):
.form-block.input-prepend
%span.add-on>
%i.icon.icon-home
= f.text_field :name, :value => #store.name, :placeholder => 'Company Name'
To do that, I created a file views/layouts/_form-block.html.haml where I inserted the following code:
.form-block.input-prepend
%span.add-on>
%i{ :class => "icon icon-#{icon}" }
= yield
And I call the block in my view using:
- render :template => 'layouts/_form-block', :locals => { :icon => 'home' } do
= f.text_field :name, :value => #store.name, :placeholder => 'Company Name'
But It doesn't work. I have the following error 'nil' is not an ActiveModel-compatible object that returns a valid partial path.
Do you have any idea? Is this the best way to do?
Thanks in advance.
If you want to use = yield to pass a block, you need to render as a layout. Use render :layout => instead of render :template =>.
Related
I have a page That starts with a form. Inside that form, I render another page called information. Inside this render I have another render for a modal. This modal is another form. So at this point i have one nested form. This works great in all browsers except IE9. I think what IE9 is trying to do is it sees when the second form is ending, and it also ends the first form, so everything that is after the nested form is screwed up.
Has anyone else ran into this problem? and how do you fix it?
Parent File (form):
= simple_form_for #form do |f|
#the_form
= render 'information', :f => f
.buttons
%input{:name => "submit", :type => "submit", :value => "SUBMIT"}
%input{:name => "cancel", :type => "submit", :value => "Cancel"}
Render information file:
#information
%fieldset
%legend
Form Title
= f.input :form_id, :url => form_name_path, :label => 'Field Name'
= render 'modal'
(the rest of the code here breaks)...
Render modal file:
.modal.hide.fade
.modalBox
%h3
New Form Name
%a{href: "#", class: "x", title: "Close" : 'data-dismiss' => "modal"}
.diagRepeater
.modal-body
= simple_form_for Form.new, :url => {:controller => :form, :action => :modal_create} do |o|
=o.input :name, :label => 'Name', :required => true
=o.input :form_id, :as => :hidden
It is in this last file that I see the problem. If I comment out the simple_form_for and on, it will work great. If I leave it, it will break the rest of the form.
HTML not support nested form.
You can have several forms in a page but they should not be nested.
As you say: this work great in all browser for me it is miracle because 'You would even have problems making it work in different versions of the same browser' so avoid using that.
Webkit explain why HTML not supporting nested form
bool HTMLParser::formCreateErrorCheck(Token* t, RefPtr<Node>& result)
{
// Only create a new form if we're not already inside one.
// This is consistent with other browsers' behavior.
if (!m_currentFormElement) {
m_currentFormElement = new HTMLFormElement(formTag, m_document);
result = m_currentFormElement;
pCloserCreateErrorCheck(t, result);
}
return false;
}
First we start off with conversations/index.html.haml to create a message
#new_message_conversation
.panel.panel-info
.panel-heading
%h4 Send a Bark!
.panel-footer(style="padding-top: 20px")
= simple_form_for :message, url: :messages, :remote => true do |f|
.form-group
= f.input :master_name, placeholder: 'Choose master...', label: false, :url => autocomplete_master_name_conversations_path, :as => :autocomplete, id_element: "#master_name_id", input_html: {class: "form-control"}
= f.input :recipient_id, as: "hidden", input_html: {id: "master_name_id"}
= f.input :body, label: false, as: "text", placeholder: 'Write message...', :input_html => { :rows => 5 }
= f.button :submit, 'Send', :class => "btn btn-lg btn-primary", :disable_with => "Sending..."
which then goes to the messages#create action which has
...
respond_to do |format|
format.js { render "create", locals: { conversation: #conversation, conversations: #conversations, receipts: #receipts }}
end
...
which sends the work to the conversations/create.js.erb file
$('#new_message_conversation').prop('disabled', true).html("<%= raw escape_javascript(render(:partial => 'conversations/show', locals: { conversation: conversation, receipts: receipts })) %>").hide().fadeIn('fast');
which adds the conversations/show partial, _show.html.haml which has
...
%ul.pager.pull-left(style= "padding-left: 10px")
%li#paginator_3= link_to_previous_page #receipts, "Newer", :remote => true, :param_name => 'page_2'
%li#paginator_4= link_to_next_page #receipts, "Older", :remote => true, :param_name => 'page_2'
...
everything works excepts now the pagination buttons don't work. and when I click a pagination button the server says
Rendered messages/index.js.erb
Why does a partial that's in views/conversations that has a remote ajax call render to a different controller (messages)? It should be rendering conversations/show.js.erb because the partial is conversations/_show.html.haml right?
here are my routes also
...
resources :conversations do
get :autocomplete_master_name, :on => :collection
end
resources :messages
...
Even though you're rendering views and partials from the conversations path, you never even touched the ConversationController.
You can render whatever views you want for the action you're executing. The only thing connecting the ConversationController with the views/conversation/file.html.erb and similar view files is a loose naming convention. When rendering say render 'index' from an action of the ConversationController, it just assumes by that convention you meant the views/conversation/index.html.erb file.
Your view or partial alone cannot reference the controller it would belong to (when going by the naming convention) because it is just used as a template by the render command in your action. The view doesn't care whether the controller behind it is the appropriate one. In this case, the render was originally executed in the MessagesController, so the view also just has a reference to that one.
To still have the links point to the correct controller, you need to specify the controller to be used for the url. Otherwise, it is assumed that you want to use the very same controller you used to render the page.
The culprit is probably somewhere in the link_to_previous_page and link_to_next_page helpers by kaminari. When using the full paginate helper, you can set the controller and action you want to use like this:
<%= paginate #users, :params => {:controller => 'foo', :action => 'bar'} %>
The documentation (here: https://github.com/amatsuda/kaminari) doesn't say whether this param is also possible with the other helpers, but the helper uses a simple link_to (see here: https://github.com/amatsuda/kaminari/blob/master/lib/kaminari/helpers/action_view_extension.rb), so you should be able to do something like this:
link_to_previous_page #receipts, "Newer", {:controller => 'foo', :action => 'bar', :remote => true, :param_name => 'page_2'}
I have a reusable field template, called _field.html.haml, that looks like (in haml):
= f.label field
= f.send(field_type, field, :class => field_type)
The advantage of this is that it can be called from a parent form template and provides reusability. This lets me dynamically render form fields based on a field type. The calling template looks like:
= form_for(#model) do |f|
= render :partial => 'field', :locals => { :field => :first_name, :field_type => :text_field }
= render :partial => 'field', :locals => { :field => :last_name, :field_type => :text_field }
How could I add auto-generating of the placeholder text based on the label text to this _field.html.haml template? Maybe the correct question is what is the code that the label hdlper uses to convert the field name to an capitalized english phrase?
I.e. code would look like:
= f.send(field_type, field, :class => field_type, :placeholder => #INSERT CODE HERE to get friendly placeholder text)
the label_tag method uses `String#humanize" to make it look nicer (http://api.rubyonrails.org/classes/String.html#method-i-humanize). So in your example you could do:
= f.send(field_type, field, :class => field_type, :placeholder => field.to_s.humanize)
As previously mentioned in the comments, you should have a look at available form-builders. They abstract a lot of pain associated with making nice forms. Some links are:
https://github.com/plataformatec/simple_form
https://github.com/justinfrench/formtastic
In my new.html.erb page, i use the following line to render a partial and it works fine.
<%= render :partial => "submissions/player_form", :locals => { :submission => #submission } %>
Now i want to render exactly the same partial via RJS
<p>Player Type: <%= f.select(:PLAYER_TYPE, $playersList, {:prompt => 'Select the Player Type'} %></p>
<%= observe_field("submission_PLAYER_TYPE", :frequency => 1,
:url => { :controller => 'submissions',
:action => :display_player_form },
:with => "'player='+value") %>
display_player_form.rjs:
page.replace_html 'observed_assay_form', :partial => 'submissions/player_form', :locals => {:submission => #submission }
Nothing is displayed!!
Am i missing something??
Thanks for helping me out with this :)
I finally figured it out. So here are my findings:
In the partial, include the form_for tag, just like in the original form--
<% form_for #object do |f| %>
In the action used when observing the field, in my case, 'display_player_form', create a new instance of the object(see below)
#object = Object.new
In your rjs file, enter the following:
page['id of div'].replace_html :partial => 'your_partial_name'
There you go...
Hope this helps
I would rename display_player_form.rjs to display_player_form.js.erb and have its contents look like this:
$("#observed_essay_form").html('<%=
escape_javascript(
render :partial => 'submissions/player_form', :locals => {:submission => #submission }
)
-%>');
$("img[src$='spinner.gif']:visible").hide(); // optional - hide any visible spinner.gif images
I use jQuery, not Prototype, by the way.
a need how convert string value in field name valid:
Example:
<%="price.list_"+current_user.price.to_s%>
so
price.list_1
then is my real field name. this name.this field will use it to do more operations in my view.
I think I understood your question. You will need to use the send function
<%= price.send("list_#{current_user.price}".to_sym) %>
That should work but you can also do
<%= "price.list_#{current_user.price.to_s}" %>
OR
<p>
price.list_<%= current_user.price.to_s %>
</p>
UPDATE: I misunderstood the question. This is going to require some Javascript or AJAX, depending on your exact application.
JS:
:onchange => 'update(this.value)'
function update(new_value) {
var update_me = document.getElementById('update_this_field');
update_me.value = new_value;
}
AJAX on RAILS
:onchange => remote_function(:url => {:action => 'update'}, :with => 'Form.element.serialize(this)'), :name => 'foo'
def update
bar = params[:foo]
render :update do |page|
page.replace_html 'update_this_field', "price.list_" + bar
end
end