Individual JavaScript for multiple records in Rails - javascript

I've been trying to wrap my head around this for days now, but I can't seem to find a solution for this problem.
Basically what I want to do is a feed similar to Facebook. A feed with multiple posts, of which each has comments/replies.
Now what I can't get to work is an AJAX "Load more" button for each post. I've only got it working for all posts at once, but not for individual ones.
What I have so far:
The feed:
<% #posts.each do |post| %>
<%= render "posts/thread", post: post %>
<% end %>
The threads:
<%= render post %>
<% replies = post.replies.paginate(:page => params["replies#{post.id.to_s}"], :per_page => 2) %>
<%= render replies %>
<%= will_paginate replies, :param_name => 'replies'+post.id.to_s %>
<%= link_to "Load More", "#", class: "load-more", id: post.id, :remote => true %>
The posts:
<div class="post" data-id="<%= post.id %>">
## content ##
</div>
pagination.coffee:
jQuery ->
$('.load-more').on 'click', ->
postId = $(this).attr('id')
more_posts_url = $('#post-'+postId+' .pagination .next_page').attr('href')
if more_posts_url
$('.load-more').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />')
$.getScript more_posts_url
return
return
index.js.erb:
<%= #posts.each do |post| %>
$('#post-<%= post.id %>').append('<%= j render post.replies %>');
<% end %>
But this does nothing.
I really don't understand how JS works for multiple records on one page.

I suppose this could help: https://jsfiddle.net/se708oou/2/
Main points of difference:
You should loop through all of the .load-more elements
$('.load-more').each(function() {
Change reference to use this
$(this).on('click', function() {
(Miscellaneous) This is how you take data-* in jQuery:
postId = $(this).data('id');
Cheers! :)

Your posts div
<div class="post" data-id="<%= post.id %>">
...
</div>
Has a class of post and a data attribute id, but in index.js.erb you are trying to select #post-<%= post.id %>
That selector isn't in the dom, You need to change your selector to
$('.post[data-id=<%= post.id %>').append('<%= j render post.replies %>');

Related

Rails - pass a parameter to controller from form select dropdown

I have 2 models: Team and Quest. When creating a new team, I have a drop-down of all the quests. When a quest is selected, I want to display information about the quest.
My understanding is that everything in the form is on the client side and AJAX is required to pass the selected quest to the server side. My code is based on this Stack Overflow answer.
Here is how I constructed my form:
app/views/teams_form.html.erb
<%= form_for(#team) do |f| %>
<fieldset>
<ol>
<li>
<%= f.label :name %>
<%= f.text_field :name %>
</li>
<li>
<%= f.label :quest_id %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
</li>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
</ol>
<p>
<%= f.submit %>
</p>
</fieldset>
<% end %>
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
respond_to do |format|
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
format.html #new.html.erb
format.json
format.js
end
end
My goal was to pass the :quest_id parameter from the form to the #x variable and use that in the form.
This has produced nothing. I'm not getting the parameter in the controller and I'm not sure what I'm missing.
As per the description shared it seems like the you are unable to get the value of the selected item from the dropdown.
Below mentioned code is used for selecting value from dropdown also you can inspect the same using developer tools of the browser.
quest = $("#quest_id").val();
Note: Assuming the selector id is quest_id, change it according to your form.
Now, you can call the ajax using the below mentioned code.
$.ajax({
type: "GET",
url: "/teams/new",
data:{ quest_id: quest },
dataType: "json",
success:function(data){
# logic for using ajax result
}
Hope it helps!!
Finally got this working, wanted to post if anyone sees this and is having the same problem:
I went with a separate AJAX request since that was being suggested
app/views/teams_form.html.erb
<script>
$(document).ready(function() {
$('#team_quest_id').change(function() {
$.ajax({
url: "/teams/new",
data: {quest_id: $("#team_quest_id option:selected").val()},
dataType: "script",
method: "get",
success: function(r){}
});
});
});
</script>
I moved the location of the parameter assignment
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
respond_to do |format|
format.html #new.html.erb
format.json
format.js
end
end
And most importantly - I created a js file to render my form
app/views/new.js.erb
$('#new_team').html("<%= j (render 'form') %>");
This video was extremely helpful
The code in your question is almost correct, you forgot to nest the attributes in data.
<% # app/views/teams_form.html.erb %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
<% # should be: %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: '/teams/new', method: 'get'}} %>
<% # or even better, use the path helper instead of the hard coded path %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: new_team_path, method: :get}} %>
Having set the attributes correctly, we still need to fix the form further. On page request the browser will request the form, but #x will never be set. Since ERB will not be send to the client we'll need to add a handle to find our quest container element back.
<% # app/views/teams_form.html.erb %>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
<% # should be something like %>
<li id="quest-info-container"></li>
Now in the controller split of the HTML request from the JS request.
# app/controllers/teams_controller.rb
def new
respond_to do |format|
format.html do
#team = Team.new
#quests = Quest.all
end
format.js do
#quest = Quest.find(params.dig(:team, :quest_id))
end
end
end
You could simplify the above by sending the select data-path to another url that handles the quest preview.
Now we need to render the preview in our container we need 2 files for this, first of how the resulting structure should look. Keep in mind that this will be rendered inside the container.
<% # app/views/teams/_quest_preview.html.erb %>
<% # Here comes what you want to display about the quest. You can give this %>
<% # file another name if you like. You have #quest to your disposal here. %>
<%= #quest.id %> <strong><%= #quest.name %></strong>
Now we only need a JavaScript file that loads the above structure into our created handle.
<% # app/views/teams/new.js.erb %>
handle = document.getElementById('quest-info-container');
handle.innerHTML = '<%= j render('quest_preview') %>';
The j is an alias for escape_javascript. If the partial is not in the same directory use <%= j render('other_dir/some_partial') %> instead.

Rails will_paginate endless scroll with an array that drops the first 3 items

I have a partial where I'd like to drop or not show the first three articles in the array because they are in a featured articles section. I also want the partial to use will_paginate w/ endless scrolling to load the next page of articles. The issue I'm facing is that when using #articles.drop(3).each do |a| and the next page goes to load, the array drops the next three articles again.
What's the best way to solve for this? My initial thought was an array within an array, where the first array drops the first 3 then the nested array returns all articles but I'm not sure how to do that?
Array code in partial:
<% #articles.drop(3).each do |a| %>
<%= link_to a.source_url, :class => "flexRow" do %>
<%= a.source %>
<h3><%= a.title %></h3>
<% end %>
<% end %>
Index.js.erb
$('#article-index').append(' <%= j render("articles") %>');
<% if #articles .next_page %>
$('.pagination').replaceWith('<%= j will_paginate(#articles, :previous_label => '', :next_label => '', :page_links => false) %>');
<% else %>
$('.pagination').remove();
<% end %>
Index.html.erb
<div id="article-index">
<%= render 'articles' %>
</div>
UPDATE
This solution seems to work but doesn't feel elegant?
<% (#articles.current_page == 1 ? #articles.drop(3) : #articles).each do |a| %>
Try
#articles[3..#articles.count]
This will drop the records held at index 0, 1 and 2, and return the remaining.
You may do the following in your controller:
EXAMPLE
#articles = Article.where(...).paginate(page: params[:page], per_page: 10)
# Works only for the first HTML request
unless request.xhr?
#articles.shift(3)
end
.
.
.
respond_to do |format|
format.html
format.js
end
Now, when you iterate over #articles, it would start from index 3, only for first time.
EDIT
<% (request.xhr? ? #articles : #articles[3..10]).each do |a| %>
<%= link_to a.source_url, :class => "flexRow" do %>
<%= a.source %>
<h3><%= a.title %></h3>
<% end %>
<% end %>
Assuming the page size is 10.

what is the best way to implement a cancel button that sends an order id to the controller and updates the order status attribute to "cancelled"?

what is the best way to implement a cancel button on an orders show page. The cancel button simply updates the order's status attributes to "cancelled" in a controller. I would like to carry over the order.id to the controller as each user has many orders.I am currently getting an undefined method 'id' for nil:Nilclass which makes me think the #order.id is not being passed into the hidden-field. Not sure what am doing is the best way to pass the order.id into the controller&welcome any ideas for a better solution
<div>
<% #orders.each do |order| %>
<%= order.id %>
<%= order.total %>
<%= order.user.name %>
//lots of boring stuff then at the bottom of the page
<%= form_tag guest_cancel_path, method: :post do |f| %>
<input type="hidden" name="order_id" value="<% order.id %>" >
<%= submit_tag "Cancel ",class: "cancel-button btn wide" %>
<% end %>
In my controller, I have:
def guest_cancel
#user = current_user
#order = Order.find(params[:order_id])
#order.update(status: 'cancelled')
redirect_to guest_requests_path, notice: " the order: #{#order} by user -> #{#user} has been cancelled, "
end
then in my routes:
post 'guest_cancel' => 'orders#guest_cancel'
It seems you missed to output it "<%= order.id %>"
you would need to add this the controller too: #order.update(status: 'cancalled')
If you have relation between listening and orders then you should write
<% listing.orders.each do |order| %>
so, orders instead of order.

rails crud using jquery popover via bootstrap

I tried to add jquery popover to rails default cruds instead of redireting to views and what I am doing is rendering the form in the jquery popover:
<%= content_tag_for :tr, #order.order_items do |i| %>
<a class='btn btn-mini label-with-popover' id=<%=i.id%>><%= t('helpers.links.edit_html') %> </a>
<% end %>
<script type="text/javascript">
$(function () {
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item') %>" ,
placement: 'top'
} );
});
</script>
and here is my form_item:
<%= simple_form_for :order_item, url: admin_shop_order_path, :html => { :class => 'form-horizontal' } do |f| %>
<div class='form-inputs'>
<%= f.input :item_id , :collection => #shop.products.collect{|b| b.variants}.flatten.map{|variant| ["#{variant_full_title(variant)}", variant.id]}%>
<%= f.input :quantity %>
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t("helpers.links.cancel"), admin_shop_orders_path, :class => 'btn' %>
</div>
<% end %>
but the problem appears on the edit button. to render the edit form we need the object that we want to edit(:order_item I mean) and a way to get that is by using the id and this is why I have set the anchor tag id. now we have to get that anchor id within the popover function but $(this).id doesn't work. any suggestion?
In jQuery, you need to use attr('id') to get the id of an element. Try to replace $(this).id with:
$(this).attr('id')
See jQuery's documentation for more details: http://api.jquery.com/attr/
But a prefered way to achieve this is usually to use data attributes. It's a clean way to pass data from your view to some JS code. Your link would look like this:
<a class='btn btn-mini label-with-popover' data-item-id=<%=i.id%>>...</a>
And in your JS file, you would get this value using:
$(this).data('item-id')
jQuery's documentation: http://api.jquery.com/data/
You are rendering wrongly, I think, you should try to render your partial with locals. You can also pass local variables into partials, making them even more powerful and flexible.
In your case, while rendering your partial form_item in script tag, so you can write it like this:
<script type="text/javascript">
$(function () {
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item', :locals => {order_item: #order_item}) %>" ,
placement: 'top'
} );
});
</script>
and in your form you will be able to access it like :
<%= form_for(order_item) do %>
# write your form stuff %>
<% end %>
This way you can handle your form for both create or edit operations.
First I suggest you to pass id with some text added to it(I replaced the id with "link_<%= i.id%>").
Second call a onclick function on your a tag:
<%= content_tag_for :tr, #order.order_items do |i| %>
<a class='btn btn-mini label-with-popover' id="link_<%=i.id%>" ><%= t('helpers.links.edit_html') onclick="javascript:openPopup(this.id)" %> </a>
<% end %>
and Last but not the least, get id in your function, and pass it through your partial.
<script type="text/javascript">
function openPopup(a_id) {
var id = a_id.split("link_")[1];
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item', locals => {:id => id}) %>" ,
placement: 'top'
} );
});
</script>
I am not good at javascript, but from my answer, you will get, what I wanted to explain you. and I am sure that you will find a more improved way to do this. And If you do, please post it here as well. Hope it will help. Thanks

Rails 3 javascript: How to render a partial with parameters

I'm still getting the hang of Rails. Here I'm using Rails 3 and the goal basically is to have an AJAX call triggered when I click the subscribe button the post_form partial is rendered beneath for the topic I have just subscribed to. The button then becomes an unsubscibe button and the post_form partial is removed. The toggling of the button alone works (i.e: by removing the second line in the two immediately following snippets), but the rendering of the *post_form* partial does not.
The problem is I can't seem to get the right syntax and/or passing of parameters in the two following partials. The topic object is just not passed and I get an invalid model_name for NilClass error when clicking on the subscribe or unsubscribe button. If I refresh the page manually, the partial is rendered or hidden the correct way, so it's really just the AJAX part that isn't working right.
views/subscription/create.js.erb
$("#subscription_form").html("<%= escape_javascript(render('users/unsubscribe')) %>");
$("#post_form").html("<%= escape_javascript(render('shared/post_form', :topic => #topic)) %>");
views/subscription/destroy.js.erb
$("#subscription_form").html("<%= escape_javascript(render('users/subscribe')) %>");
$("#post_form").html("<%= escape_javascript(render('shared/post_form', :topic => #topic)) %>");
views/users/_subscription_form.html.erb
<% unless current_user?(#user) %>
<div id="subscription_form">
<% if current_user.subscribed?(#topic) %>
<%= render 'users/unsubscribe', :topic => #topic %>
<% else %>
<%= render 'users/subscribe', :topic => #topic %>
<% end %>
</div>
<% end %>
controllers/subscriptions_controller.rb
class SubscriptionsController < ApplicationController
before_filter :signed_in_user
respond_to :html, :js
def create
#topic = Topic.find(params[:subscription][:topic_id])
current_user.subscribe!(#topic)
respond_with #topic
end
def destroy
#topic = Subscription.find(params[:id]).topic
current_user.unsubscribe!(#topic)
respond_with #topic
end
end
views/shared/_post_form.html.erb
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.hidden_field :topic_id, :value => #topic.id %>
<%= f.text_area :content, placeholder: "Tell us about it ..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
If it is of any help, the relationships are:
post -> belongs_to -> topic and topic -> has_many -> posts
Looks like you're using the variable "#post" in the "views/_post_form.html.erb" file.
<%= form_for(#post) do |f| %>
Since you aren't setting that variable anywhere in your actions you would get a null reference error.
You would need to do something like this:
def create
#post = Post.find(the_post_id)
#topic = Topic.find(params[:subscription][:topic_id])
current_user.subscribe!(#topic)
respond_with #topic
end
Also you are passing in the "topic" variable as a local but accessing it as an instance variable. You should change the your _post_form.html.erb file to look like this:
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.hidden_field :topic_id, :value => topic.id %>
<%= f.text_area :content, placeholder: "Tell us about it ..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
I don't have my ruby environment readily available so I can't verify that this will solve your problem but I think it should move you in the right direction.

Categories