rails will_paginate with different partials - ruby-on-rails

I have some new actions for the notification model and things started getting messy so I refactored from <% = render #other_notifications %> with notifcations/_notification.html.erb to the following structure.
My problem is the following. The page renders everything well, but the pagination doesn't work properly. So IF I have the structure below and doesn't delete _notification.html.erb then the page will be loaded with the new action partials and the pagination objects will be loaded with _notification.html.erb. IF I delete _notification.html.erb then the page still loads with the new partials, but pagination doesnt't work. How should I change the pagination to make this work?
notifications_controller.rb
def other_notifications
#other_notifications = current_user.notifications.not_chat.order(created_at: :desc).includes(:sender_profile).
paginate(page: params[:page], per_page: Notification.pagination_per_page)
current_user.reset_new_other_notifications
respond_to do |format|
format.html
format.js
end
end
other_notifications.html.erb
<div class = "other-notifications-index">
<% #other_notifications.each do |notification| %>
<% if lookup_context.template_exists?(notification.action, "notifications/actions", true) %>
<%= render partial: "notifications/actions/#{notification.action}", locals: { notification: notification } %>
<% end %>
<% end %>
</div>
<div id="infinite-othernotification-scrolling">
<%= will_paginate #other_notifications %>
</div>
other_notifications.js.erb
$('.other-notifications-index').append('<%= j render #other_notifications %>');
<% if #other_notifications.next_page %>
$('.pagination').replaceWith('<%= j will_paginate #other_notifications %>');
<% else %>
$(window).off('scroll');
$('.pagination').remove();
$('.no-more').delay(1000).fadeIn(1500);
<% end %>

I solved it like this. So will paginate will look for _notification partial, which will always be found with the following code, and the _notification partial will call the rest of the partials.
other_notifications.html.erb
<%= render #other_notifications %>
<%= will_paginate #other_notifications %>
_notification.html.erb
<% if lookup_context.template_exists?(notification.action, "notifications/actions", true) %>
<%= render partial: "notifications/actions/#{notification.action}", locals: { notification: notification } %>
<% end %>

Related

Rails view not displaying partial - "response has no matching <turbo-frame id="events"> element"

I've got an index page with a search which mostly works, aside from when there are no results, which is when I get the error:-
response has no matching <turbo-frame id="events"> element
I don't get this when there are search results. Do I need to specify a partial to render in my index action, when results are empty? Why isn't this being evaluated in my view?
index.html.erb
<div id="events-wrapper">
<% if #events.any? %>
<%= render "events", events: #events %>
<% else %>
<%= render "no_events" %>
<% end %>
</div>
_events.html.erb
<%= turbo_frame_tag "events" do %>
<%= render collection: #events, partial: 'event' %>
<%= raw pagy_bootstrap_nav(#pagy) %>
<% end %>
_no_events.html.erb
<div class="no-events-notice alert alert-primary">
No events found
</div>
events_controller.rb index action
def index
#pagy, #events = pagy(find_events(filter_params))
end
Basically my search works fine, but when there are no results - my no_events partial doesn't get rendered, unless I refresh the page. What am I missing?
I've removed the partial from the equation and just replaced that line with a simple div instead and it's still not displayed.
The solution was to wrap the results in a turbo frame with an id of "events" like so:-
<turbo-frame id="events">
<% if #events.any? %>
<%= render "events", events: #events %>
<% else %>
<%= render "no_events" %>
<% end %>
</turbo-frame>

Ruby on Rails if/unless statment for AJAX destroy when cart is empty

I am building webstore. I added a bit of AJAX functionality to add, update and remove items from my cart without refreshing.
It works perfect as long as there are no items in a cart and render cart_empty partial properly.
The problem is when I add few items, and I can update/remove them without refreshing and
finally I remove the last one, and it do not switch to the cart_empty partial and there are only empty table headers left from cart-items partial.
Can I add some logic to destroy.js.erb?
destroy.js.erb
$("#cart-item").html("<%= j render 'carts/cart_items' %>");
the show.html.erb contains this unless function:
<% unless current_order.order_items.empty? %>
<div id="cart-item">
<%= render 'cart_items' %>
</div>
<% else %>
<div id="cart-empty">
<%= render 'cart_empty' %>
</div>
<% end %>
Maybe should I modify somehow my controller to check if there are order_items in a current_order?
Thank you!
A JS.erb template is evaluated in exactly the same way as a HTML template. So you can just add a condition:
<% if current_order.order_items.any? %>
$("#cart-item").html("<%= j render 'carts/cart_items' %>");
<% else %>
$("#cart-item").html("<%= j render 'carts/cart_empty' %>");
<% end %>
None that this implies that current_order is either a helper or a local passes to the view.
But you might want to refactor this and push the condition down to the partial:
# carts/cart_items.html.erb
<% if cart_items.none? %>
<% render :cart_empty %>
<% else %>
# ...
<% end %>
Which will let you just write:
<%= render 'carts/cart_items', cart_items: current_order.order_items %>
See:
Layouts and Rendering in Rails - passing local variables

Will_paginate for includes comments

I'm discovering the gem will_paginate which is great ! But I'm facing a problem of using. I'm building a group>post>comments app, so in my group show page i'm displaying posts and their comments. To limit the numbers of queries, i'm using includes method like this :
Group_controller :
def show
#posts = #group.posts.order(upd_at: :desc).includes(:user).includes(comments: :user).paginate(page: params[:page], per_page: 10)
end
So I would like to also paginate my comments. Do you know a way to do that ?
My code :
Group_show =
<h1>Groupe <%= #group.name %></h1>
<div class="post_list<%=#group.id%>">
<%= render #posts %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails %>
And my posts/_post =
<% #comments = post.comments %>
<ul id="comment_list<%=post.id%>">
<%- if #comments.any? %>
<%= render #comments, post: post %>
<%= will_paginate #comments, renderer: BootstrapPagination::Rails %>
<% end %>
</ul>
By the way if you have a method to define #comments directly in the Groups_controller(show), it can be really useful ;)
Not 100% tested, but I think this should work. Do you know how all these components work? If not, let me know and I can explain.
posts/_post
<% #comments = post.comments.order(created_at: :desc).limit(3) %>
<ul id="comment_list<%=post.id%>">
<%- if #comments.any? %>
<%= render #comments, post: post %>
<%- if post.comments.offset(3).exists? # this is more efficient than count > 3 bc it quits counting after 3 %>
<!-- the below link_to creates: href="/posts/:id/comments" ... -->
<!-- ... and `remote: true` makes that an ajax request -->
<li><%= link_to "more", comments_post_path(post), class: "more-comments-btn", remote: true %></li>
<% end %>
<% end %>
</ul>
config/routes.rb
resources :posts do
# `member do` is explained here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
member do
get :comments
end
end
posts_controller.rb
# GET /posts/:id/comments
def comments
#post = Post.find(params[:id])
#comments = #post.comments.order(created_at: :desc)
# since you requested this url via ajax with `remote: true` rails will automatically render `posts/comments.js.erb` ...
# ... rather than a typical html request where rails would automatically render `posts/comments.html.erb`
end
views/posts/comments.js.erb
// some people like to use render #comments as shorthand like you did above. I'm a fan of being more explicit like the below
$("#comment_list<%= #post.id %>").html("<%= escape_javascript(render partial: 'comments/comments', locals: {comments: #comments, post: #post}) %>");
// now remove the more comments button
$("#comment_list<%= #post.id %>").find(".more-comments-btn").remove();
The documentation here explains the use of remote: true for ajax requests. Scroll down to section "3.1.2 link_to" and then section 5.1 for the controller and js.erb view.

Render to partial by Ajax in Rails App

I have almost done! but I have an issue, in my Controller file have this:
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate(page: params[:page])
end
Then I have this piece of code in my file show.html.erb:
<div class="span8">
<%= render 'follow_form' if signed_in? %>
<% if #user.posts.any? %>
<h3>Microposts (<%= #user.posts.count %>)</h3>
<div id='posts'>
<div class='page'>
<ol class="microposts">
<%= render #posts %>
</ol>
</div>
</div>
<% end %>
</div>
At the bottom of this file, I have a Javascript code that I have taken from the tutorial: https://github.com/amatsuda/kaminari/wiki/How-To:-Create-Infinite-Scrolling-with-jQuery
In the same folder I have the file index.js.erb with:
$("#articles").append("<div class='page'><%= escape_javascript(render(#users)) %></div>");
$("#posts").append("<div class='page'><%= escape_javascript(render(#posts)) %></div>");
In a partial _posts.html.erb have this:
<div class='article'>
<%= image_tag(user.picture_url, :width => 50) %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</div>
The first one already works in my file index.html.erb, the problem is with the second piece of code, when I try to render the partial at #post, It brings the follow log:
**
'nil' is not an ActiveModel-compatible object that returns a valid partial path.
Extracted source (around line #2):
1: $("#articles").append("<div class='page'><%= escape_javascript(render(#users)) %></div>");
2: $("#posts").append("<div class='page'><%= escape_javascript(render(#posts)) %></div>");
**
How can I render that partial?
Thanks a lot :D
I usually use the .js.erb template to render the partial into a var then set it on the page using JS. Be sure to escape_javascript the template content otherwise you will get JS errors.
<% template_html = render :partial => '_feed_item.html.erb' %>
<%="$('div#content').html('#{escape_javascript(template_html)}</div>');" %>
I think the above should work in erb. I use HAML so mine looks like:
-template_html = render :partial => 'partial_template'
!="$('div#content').html('#{escape_javascript(template_html)');"
Cheers.
Based on your In script I'm calling to /users?page=, and I was wondering that It may calls the line, you must make sure that your controller is populating the #posts variable if your view is using this. Can you show the controller method users?

Ruby on Rails: Deleting a post with Ajax in navigation and showing the following

Thanks for reading!
So I'm relatively new to RoR (Rails 5) and I try to render posts of a user in a partial (_posts_panel.html.erb) which contains another partial: the navigation (_posts_navigation.html.erb). I'm using ajax to show the posts when clicked on in the navigation which is working fine.
Now I want to use it for deleting a post. The post is destroyed when I click on the button and I managed to remove it in navigation.
What is not working:
the next post should be shown in the panel after the deletion
when there is no post left I want to reload the panel because another partial (_posts_empty.html.erb) should be rendered
My Code:
_posts_panel.html.erb: (it is rendered in show.html.erb of user)
<% if #user.simple_posts.empty? %>
<%= render 'simple_posts/shared/posts_empty' %>
<% else %>
<div id="post-navigation">
<%= render 'simple_posts/shared/posts_navigation' %>
</div>
<%= simple_posts_edit_button(current_user, #simple_post)%>
<%= simple_posts_delete_button(current_user, #simple_post)%> #button helper
<div id="show_post">
<%= render #simple_post %>
<%= yield %>
</div>
<%end %>
_posts_navigation.html.erb
<div class="bootstrap-vertical-nav">
<ul class="nav flex-column">
<% #user.simple_posts.reverse_each do |post|%>
<li class="nav-item" id="<%= #simple_post.id %>">
<%= link_to post.title, user_simple_post_path(#user, post), remote: true, class:"nav-link text-dark" %>
</li>
<% end %>
</ul>
</div>
<% if #user.id == current_user.id %>
<%= link_to "+ New Post", new_user_simple_post_path(current_user), class: "btn btn-info w-100", role: "button" %>
<% end %>
destroy.js.erb
$('li#<%= #simple_post.id %>').remove();
simple_posts_controller.rb
def destroy
#simple_post.destroy
#simple_post =current_user.simple_posts.first
respond_to do |format|
# format.html { redirect_to simple_posts_url, notice: 'Simple post was successfully destroyed.' }
format.js
end
end
I tried to append in destroy.js.erb the same code from showing the post $('#show_post').html("<%= escape_javascript render #simple_post %>"); but it's not working correctly. When deleting the first post (but only that one) the next one is shown but the post is not removed from the navigation. I also tried $('#post-navigation').html("<%= escape_javascript render partial: 'simple_posts/shared/posts_navigation' %>"); and it nothing happens.
Also I tried to reload the whole panel with $('#post-panel').html("<%= escape_javascript render partial: 'simple_posts/shared/posts_panel' %>");in show.html.erb of user but again: nothing. I'm not sure if I have to specify in which view or partial exactly it has to render the partial.
I don't really know how to approach this further.
Edit 1
ButtonHelper.rb
def simple_posts_delete_button(current_user, simple_post)
if(current_user.id == #user.id)
link_to svg('x-square'), [current_user, simple_post], remote: true, method: :delete, class:"btn"
end
end
Edit 2
show-action from simple_posts_controller.rb
def show
#user = User.find{params[:id]}
#simple_post = SimplePost.find(params[:id])
respond_to do |format|
format.js
format.html # show.html.erb
end
end
show.js.erb
$('#post-actions').html("<%= j render partial: 'simple_posts/shared/posts_actions', locals: {simple_post: #simple_post, user: #user} %>");
$('#show-post').html("<%= j render #simple_post %>");
There are several problems with your partials:
First, to remove the post from your navigation you just need to update the partial, the post is already destroyed/removed at this point.
After destroy, #simple_post in your button helper is not updated with ajax and still comes from the users_controller (where you set it the first time i presume)
You can put your action buttons inside a new partial and specify the locals.
You can also wrap the html inside a main div for the last update (when all the posts are deleted)
_posts_panel.html.erb:
<div id="panel">
<% if #user.simple_posts.empty? %>
<%= render 'simple_posts/shared/posts_empty' %>
<% else %>
<div id="post-navigation">
<%= render partial: 'simple_posts/shared/posts_navigation', locals: { simple_post: #simple_post } %>
</div>
<div id="post-actions"> #<-- new partial
<%= render partial: 'simple_posts/shared/posts_actions', locals: { simple_post: #simple_post } %>
</div>
<div id="show_post">
<%= render #simple_post %>
<%= yield %>
</div>
<% end %>
</div>
_posts_actions.html.erb:
<%= simple_posts_edit_button(current_user, simple_post)%>
<%= simple_posts_delete_button(current_user, simple_post)%>
Note that you are not using the instance variable #simple_post anymore but the local one.
Now you can write your js like this:
destroy.js.erb:
<% if #simple_post %> //<-- check if "#simple_post = current_user.posts.first" still exists after destroy
$('#post-navigation').html("<%= j render partial: 'simple_posts/shared/posts_navigation', locals: { simple_post: #simple_post } %>");
$('#post-actions').html("<%= j render partial: 'simple_posts/shared/posts_actions', locals: { simple_post: #simple_post } %>");
$('#show_post').html("<%= j render #simple_post %>");
<% else %>
$('#panel').html("<%= j render partial: 'simple_posts/shared/posts_empty' %>");
<% end %>

Resources