Reputation: 33755
So I have a partial called _node.html.erb
. That is rendered in different views (and from different controllers) throughout my app. On one particular view, what happens is I generate a list of events
that are linkable (and unique).
What I want to happen is, whenever someone clicks one of those links, it shows the partial and populates it with the correct info. The way I was initially trying to do it is via passing data-attributes
from the link to some JS that does the updating of the partial. This quickly runs into a wall when we reach the node.comments
, where the partial should load all of the comments associated with the node
being rendered.
For example, here is some sample code of what my _node.html.erb
may look like:
<!-- Card Meta -->
<div class="card__meta">
<aside>
<%= image_tag node.user.avatar.url, class: "card__author-avatar", alt: "" %>
</aside>
<section>
<h6 class="card__author-name"><%= node.user.name %></h6>
<time class="card__date"><%= node.created_at.strftime("%B %d, %Y") %></time>
<p class="card__description"><%= node.media.try(:description) %>
<textarea class="card-form__description" name="" id="" cols="30" rows="10">Aenean lacinia bibendum nulla sed consectetur.</textarea>
</section>
</div>
<!-- End Card Meta -->
<!-- Card Comments -->
<div class="card-comments-container">
<h4 class="card-comments__title"><%= pluralize(node.comments_count, "Comment") %></h4>
<a class="card-comments__button"><i class="icon-arrow-down"></i></a>
<div class="card-comments">
<%= render partial: "nodes/comment", collection: node.comments.includes(:user).order(created_at: :desc) %>
</div>
</div>
<!-- End Card Comments -->
Here is a sample of the link that a user can press, or rather the Rails code that generates the link:
<div class="item-wrapper">
<div class="item_comment">
<h5 class="item_comment-title"><%= event.action %> on <%= link_to event.eventable.node.name, "#", data: {toggle_card: "", card_id: event.eventable.node.id, card_title: event.eventable.node.media.title, card_description: event.eventable.node.media.description, card_time: event.eventable.node.media.created_at, card_comments: event.eventable.node.comments_count, card_favorites: event.eventable.node.cached_votes_total } %></h5>
<p class="item_comment-text"><%= event.eventable.message %></p>
</div>
</div>
This is an example of the type of jQuery I was using to do it originally:
$(document).ready(function() {
var overlay = $('.card-overlay'),
$card = overlay.find('.card'),
triggers = $('a[data-toggle-card]');
overlay.hide();
overlay.click(function(e) {
if( e.target != this ) {
} else {
overlay.hide();
}
});
triggers.click(function() {
var trigger = $(this);
var tr = trigger;
overlay.show();
var card = {
id: tr.data('card-id'),
title: tr.data('card-title'),
description: tr.data('card-description'),
time: tr.data('card-time'),
comments: tr.data('card-comments'),
favorites: tr.data('card-favorites'),
};
$card.attr('id', 'card-' + card.id);
$card.find('.card__content').attr('style', 'background-image: url(' + card.image + ')');
$card.find('.card__favorite-count').html('<i class="icon-heart"></i> ' + card.favorites);
$card.find('.card__comment-count').html('<i class="icon-speech-bubble-1"></i> ' + card.comments);
$card.find('.card__title').text(card.title);
$card.find('.card__date').text(card.time);
$card.find('.card__description').text(card.description);
$card.find('textarea').attr('id', 'card-input-field-' + card.id);
var player = videojs("example_video_" + card.id, {}, function() {});
player.src(
{
src: card.video.mp4, type: 'video/mp4'
},
{
src: card.video.webm, type: 'video/webm'
},
{
src: card.video.ogv, type: 'video/ogv'
}
);
player.load();
player.ready(function() {
console.log("READY")
});
return false;
});
});
How do I do this in a more "Railsy" way without relying on too much JS/jQuery?
Edit 1
After attempting Rich Peck's suggestion, I got this error:
05:46:31 web.1 | Unexpected error while processing request: JS, accepted HTTP methods are OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, VERSION-CONTROL, REPORT, CHECKOUT, CHECKIN, UNCHECKOUT, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY, ORDERPATCH, ACL, SEARCH, and PATCH
To be clear, this is what I did:
Modified my routes to have this:
resources :events do
get :node, action: :node
end
Then created an action in my EventsController
called node
like this:
def node
@node = current_user.nodes.find(params: [node_id])
respond_to do |format|
format.js
end
end
Then I created a app/views/events/node.js.erb
, that looks like so:
$overlay = $('.card-overlay');
$node = $('<%= j render "nodes/node", locals: {node: @node} %>');
$overlay.append($node);
Then I modified the link_to
tag like so:
<%= link_to event.eventable.node.name, event_node_path(event.eventable.node), remote: true, method: :js %></h5>
Upvotes: 1
Views: 368
Reputation: 76774
Okay there are two problems here:
You're trying to load & populate a partial without any extra data (IE node.comments
)
Secondly, you're heavily relying on JQuery
to update various attributes on the page. Although there is nothing wrong with this in itself, it does become an issue if you're looking to change page layouts etc.
A more "railsy" way to do this would be to remotely load the node directly from the server, much as Alex Lynham
recommended:
#config/routes.rb
resources :cards do
get :node_id, action: :node #-> url.com/cards/:card_id/:node_id
end
#app/controllers/cards_controller.rb
class CardsController < ApplicationController
def node
@card = Card.find params[:card_id]
@node = @card.nodes.find params[:node_id]
respond_to do |format|
format.js #-> app/views/cards/node.js.erb
end
end
end
#app/views/cards/node.js.erb
$node = $("<%=j render "cards/node", locals: {node: @node} %>")
$('body').append($node);
#app/views/cards/index.html.erb
<% @cards.each do |card| %>
<%= link_to card.name, card_node_path(@card), remote: true, format: :js %>
<% end %>
This will keep the code clear and concise.
You can put all your options directly in the _node.js.erb
partial. Although it still means there are some changes you have to make, it will give you the ability to change your partial & implementation without all the JQuery specialization you have now.
Upvotes: 1
Reputation: 1318
You need to make an AJAX request to the server, then use a js.erb
partial to return what you are looking for to the frontend, probably. That's the most 'Rails-y' way I can think of.
Personally, I'd ask for JSON and then use a bit of jQuery to wrangle the return values much as you have done in your example. By calling out to the backend is the only sane way you'll be able to get the associated records, the .comments
bit, which is I believe where you're stuck, if I've understood your question correctly.
EDIT:
This is quite a good tutorial on how AJAX-y stuff can be accomplished with Rails, in case you've not had much cause to do so in the past.
Upvotes: 1