kaminari pagination ajax call renders to wrong controller - ruby-on-rails

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'}

Related

In Rails when using AJAX, how to change the js file it loads?

For example, I use data remote for the pagination like this :
<%= paginate #products, :remote => true %>
It works. But the problem is I have multiple lists to paginate in one page. So, I need a way to send the call to a separate files so I can render the appropriate content. How can I do this?
My index.js.erb :
<% if params[:list] %>
$('#products').html('<%= escape_javascript render(#products) %>');
$('#paginator').html('<%= escape_javascript(paginate(#products, remote: true, param_name: 'list' ).to_s) %>');
<% end %>
For this you need to add another param with paginate.
<%= paginate #products, :remote => true, :param_name => param_name %>
This params name will pass as parameter to controller so you can control which ajax pagination you are calling.
If you are calling same action then on the basis of param name you can change the value of #products also. One more thing you need to consider in your xxxx.js.erb file you need to render paginate object also.
In your js file you can change html on the basis of param also.
- if params[:xxx]
$('#xxx').html('#{escape_javascript render(:partial =>"partial_name",:locals => { :posts => #products, :param_name => 'xxx'})}');
$('#xxx #paginator').html('#{escape_javascript(paginate(#products, :remote => true, :param_name => 'xxx').to_s)}');
- if params[:yyy]
$('#yyy').html('#{escape_javascript render(:partial =>"partial_name",:locals => { :posts => #products, :param_name => 'yyy'})}');
$('#yyy #paginator').html('#{escape_javascript(paginate(#products, :remote => true, :param_name => 'yyy').to_s)}');
In your controller you should use param_name instead of params page

Bind destroy link for a newly created item with Ajax in Rails 4

I am implementing comments in Rails 4 application.
I want to create comment and delete comment to work remotely with Ajax.
It works fine except the delete link for a newly added comment doesn't work.
What is the best way of making delete link work for a newly created item?
/views/posts/ show.html.haml
.post
%h1= #post.name
%h2 Comments
= render 'comments/form', comment: #comment
%ul#comments
= render 'comments/comment', :collection => #post.comments, :as => :comment
/views/comments/_form.html.haml
.comment-form
= simple_form_for comment, :remote => true do |f|
= f.input :body, :input_html => { :rows => "2" }, :label => false
= f.button :submit, :class => "btn btn-primary"
/views/comments/_comment.html.haml
%li{:"data-id"=>comment.id}
=comment.body
%br
= link_to "Destroy", comment_path(comment), :confirm => "Are you sure?", :method => :delete, remote: true, :class=>'link_comment_delete'
/views/comments/destroy.js.rb
$('#comments li[data-id=<%=#comment.id%>]').hide();
/views/comments/create.js.rb
$("#comments").append("<li><%= escape_javascript render #comment %></li>");
// ??? what to write here to make Delete link work
I've had the same exact situation, and ended up re-thinking the delete view by using .closest instead of biding to data-id, so you can do something like:
$("a.link_comment_delete").on('ajax:complete',function(e) {
$(this).closest('div.comment.body [or whatever class / id you are using]').fadeOut(100)
});

IE9 nested simple_form_for with haml

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;
}

Undefined local variable in rails form

Edit:
It turns out, this was really an issue of syntax when using render for partials. It should be render partial:
I am following this blog post guide to implement the acts_as_commentable_with_threading gem.
http://twocentstudios.com/blog/2012/11/15/simple-ajax-comments-with-rails/
I have a character model with the following comment form partial in the show.html.haml view.
This line is giving me an undefined local variable or method for 'comment'
app/views/characters/show.html.haml
...
= render 'comments/form', :locals => { comment => #new_comment }
...
The hash inside the locals hash seemed off to me, so i changed the comment local variable to :comment. This worked fine, but I don't believe that is I am supposed to do.
When I do this, the form partial that is rendered also uses a comment local variable.
app/views/comments/_form.html.haml
.comment-form
= simple_form_for comment, :url => comment_path, :remote => true do |f|
= f.error_notification
= f.input :body, :input_html => { :rows => "3" }, :label => false
= f.input :commentable_type, :as => :hidden, :value => comment.commentable_type
= f.input :commentable_id, :as => :hidden, :value => comment.commentable_id
= f.error :base
= f.button :submit, :class => "btn btn-primary", :disable_with => "Submitting…"
Notice that the object passed into the simple_form_for method is also a local variable. This raised an error too, and i turned it into a symbol as well. Next, the comment.commentable_type raised an error, naturally, because comment was not defined. I cannot turn this into a hash because it is having a method call on it, right?
Whatever the answer is to the question is, it seems like I am going about this the wrong way. I shouldn't be turning things into symbols, when the problem is really that it isn't defined. Where should I define it? Where and how should I define it? I tried simply doing comment in the comments controller,
I am using rails 4.0.0, using simple form, and haml. Could it be a bad syntax for simple form?
EDIT: it was a second line rendering the comment partial that was raising the error. (everything being named comment was making it hard to tell where it was coming from.
= render 'comments/form', comment: #new_comment
= render 'comments/a_comment', collection: #comments, as: :comment
-# partial for a single comment.
%div.comment{ :id => "comment-#{comment.id}" }
%hr
= link_to "×", comment_path(comment), :method => :delete, :remote => true, :confirm => "Are you sure you want to remove this comment?", :disable_with => "×", :class => 'close'
%h4
= comment.user.username
%small= comment.updated_at
%p= comment.body
%h4
In your controller:
def show
#character = Character.find(params[:id])
#new_comment = #character.comments.build
end
Assuming there is a has_many relation between character and comment.
In your view:
render partial: 'comments/form', locals: { new_comment: #new_comment }
or
render 'comments/form', new_comment: #new_comment
In your partial:
= simple_form_for new_comment, remote: true do |f|

How can I update the content of certain column without any page loading?

User has_one UserProfile.
Then UserProfile has wanted_message column as string.
Here, I'm showing input box to update wanted_message
I want it to be updated if certain user inputs any message then press "Update" button.
How can I? now, It's taking me to the url "/users/12" if I press "Update"
I don't want that:( I want it to be updated without any page loading(Ajax Request).
Then I want to have Action called update_message in User contoroller
How can I?
<%= form_for(current_user, :url => {:controller => "user", :action => "update_message" }, :remote => true, :class => 'form-search') do |f| %>
<%= f.fields_for :user_profile do |profile_form| %>
<div class="input-append">
<%= profile_form.text_field :wanted_message, :class => 'span6' %>
<button type="submit" class="btn">Update</button>
</div>
<% end %>
<% end %>
You have to set remote => true in your form and also set :method => put Put lets you update columns in your database and remote true will configure the form to send an ajax request. You'll also have to configure the update_message action in your controller to handle ajax requests. Finally, make sure your routes are configured correctly. You'll have to define that route in routes.rb and probably do an :as => 'update_message' to have access to that route helper method
This may help you with ajax in rails forms http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html
Here's a sample form, it's in haml though:
= link_to "Start", polls_toggle_live_path(poll.id), :method => :put, :remote => true, :class=> 'btn btn-success btn-small start-poll', :id => poll.id
Which links to this controller action
def toggle_live
#poll = Poll.find(params[:poll_id])
#poll.toggle_live
#poll.save!
end
Calls this method in the associated polls model to switch the db column value
def toggle_live
self.is_live = !self.is_live
end
Finally its configured in routes.rb this way to pass along the correct updates
match '/polls/toggle_live/:poll_id' => 'polls#toggle_live', :via => :put, :as => 'polls_toggle_live'

Resources