Appending SVG to DOM not working - Reload Asset Pipeline? - javascript

I have javascripts in the Asset Pipeline that interact with specific DOM elements on a specific view. For example:
# app/assets/javascripts/book.js
...
var svg = d3.select("#book").append("svg")
...
My view /pages/book looks like:
<% content_for :head do %>
<%= javascript_include_tag 'd3' %>
<% end %>
<h1>Book</h1>
<div id="book"></div>
<% content_for :body do %>
<%= javascript_include_tag 'book' %>
<% end %>
My assets.rb has:
Rails.application.config.assets.precompile += %w( d3.js )
Rails.application.config.assets.precompile += %w( book.js )
When I go to localhost:3000/pages/book it works perfectly, the SVG binds to the #book
When I start at root localhost:3000 and follow a link <%= link_to "See Book", pages_book_path %>, the DOM elements exist, the HTML renders, but the SVG does not bind to #book.
Looking in the terminal, when I go directly to localhost:3000/pages/book the Asset Pipeline is loaded and the javascript executes correctly. And when I start at root, the same thing happens, but since #book does not exist on the root view, nothing happens...and when I follow the link, the asset pipeline does not reload...
How can I get the asset pipeline to reload when the link is followed?

You need to make sure that your javascript asset code is wrapped in a jquery page ready and page:load like this:
$(document).on('ready page:load', function () {
var svg = d3.select("#book").append("svg")
});
I believe turbo-links are to blame here, and that code that appends to #book is being run before there is a #book, like you were saying. To get it to run when you visit that page, I'd say the above should work for you.
Update:
If you are trying to embed svg, there are gems and custom approaches out there. I found these four sources with 2 minutes of searching online, so there is likely even better stuff you can run across that will likely get you what you need. I'd start here though:
jquery's append not working with svg element?
https://github.com/jamesmartin/inline_svg
https://robots.thoughtbot.com/organized-workflow-for-svg
https://coderwall.com/p/d1vplg/embedding-and-styling-inline-svg-documents-with-css-in-rails

Related

How to show some stuff or a message beetween the main content in rails

I have a rails app where users can Sign Up/Sign In and post stories, follow other authors, what i want to achieve is how to put some content like "Authors to follow" in between of the main content while the user is scrolling (Like what twitter does with "Who to follow".
with react i have created a User_Follow_suggestion_container:
<%= react_component('FollowSuggestionsContainer') %>
It works fine, now i want to know, where to place it, so it can come randomly or after scrolling down to 20 or 30 posts.
Assuming your react component is inside a loop, you'd do something like this:
<%- #tweets.each_with_index do |tweet, index| %>
<%= tweet %>
<%- if index == 20 ( or 30 or even rand(20..30)) %>
<%= react_component('FollowSuggestionsContainer') %>
Sorry if the syntax is wrong. I haven't used ERB in years..
Edit: If you want it after scroll, you'd probably do something with JavaScript instead of Ruby/Rails.

How can I add javascript to only a single view in Rails 5?

I have a single controller that has 2 actions/views and a channel all of which have been scaffolded and pretty much using a default project.
When I load either view I can see that the channel subscribes properly.
I need the subscription to happen only on one of the views. Currently the asset pipeline appears to be compiling everything into a single js file and then serving that js file to every page.
When I scaffolded my channel it created some javascript called channel.js. How can I include channel.js with only specific actions/views?
The asset pipeline indeed compiles everything into a single JS file, so there is no built-in way to limit the execution of certain JavaScript files to specific actions.
There is a way to solve this, however. First, add this helper method to application_helper.rb:
# application_helper.rb
def body_classes(*args)
return (#body_classes || []).join(" ") if args.empty?
#body_classes ||= []
#body_classes += args.map { |klass| klass.to_s.gsub("_", "-") }
#body_classes.uniq!
nil
end
And use it in your layout:
<!-- application.html.erb -->
<body class="<%= body_classes %>">
<!-- ... -->
</body>
With this, you can specify certain body classes in your templates, to be added to the <body> tag:
<!-- your_action.html.erb -->
<%= body_classes :my_custom, :action_class %>
<h1>Your action</h1>
<!-- ... -->
The code above will add the following classes to <body>:
<body class="my-custom action-class">
Finally, you can test for these body classes in your JS code:
// your_action.js
if($("body").hasClass("my-custom")) {
// run code specific to pages with the 'my-custom' class
}
Try going into your controller that holds the method to the view you want to have the javascript incorporated in and write this inside:
def 'the view you want to effect' # This could be "index" for your index.html.erb view
#java = "channel.js"
end
Then in the following file, locate your <%= javascript_include_tag %>
views > layout > application.html.erb
Include this into your tag to load a different javascript file for any view you want with the previous process. (Try it with CSS inside your CSS include tags too.)
<%= javascript_include_tag '#{#java}' %>

How to load partial into view when a link is clicked?

I have a list of user 'submissions' in my Rails app, and when a user submission is clicked, I would like the full submission to load into the view, without having to go to a new page.
Here's the code for the list of submissions:
<div id="submission-list-container">
<% current_user.submissions.each do |i| %>
<a href='#'>
<div id="post-container">
<%= i.title %>
</div>
</a>
<% end %>
</div>
The partial I have created, <%= render "show", :submission => i %>,, works fine, but I would like the full submission to be loaded into the view (index.html.erb), when that link above is clicked. Is there a good method for doing this? Should I just do something else like an AJAX call in JavaScript? I like these partials because it feels more clean and organized to seperate code.
My partial is pretty simple at the moment:
<%= submission.title %>
<%= dat_markdown(submission.content) %>
You can do this using a TitlePane in the Dojo Toolkit, and probably there's a similar widget with jQueryUI.
I can only speak to the Dojo Toolkit's version
You would do something like:
<div data-dojo-type="dijit/TitlePane" data-dojo-props="href: '/blah', title: '<%= submission.title %>', open: false">
You can include this in your application layout for lite usage of dojo, loaded from a CDN:
<script data-dojo-config="async: true, parseOnLoad: true"></script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js"></script>
The TitlePane is wired up so that when it is expanded, it will autoload the content that is provided to the href parameter.
While this isn't exactly a rails solution directly, it can be used as an alternative.
For a Rails Solution, you can simply use an AJAX call to a controller that renders the partial. You may even be able to hook it up using the
def blah
#submission = Submission.find(...)
respond_to do |format|
format.html # default render
format.js # js behavior
end
end
then a blah.html.erb
<%= render partial: 'submission/submission', object: #submission %>
with the partial
<div id='submission-<%= submission.id %>'>
<div id='submission-<%= submission.id %>-title'><%= link_to(submission.title, 'blah/blah', remote: true) %></div>
<div id='submission-<%= submission.id %>-content'></div>
</div>
and a blah.js.erb
$.get('/submission/content', function(data) { $('#submission-<%= #submission.id %>-content').html(data) } );
and an entry into the submissions_controller with route
def content
#submission = Submission.find(...)
render text: #submission.content
end
This probably isn't an exact solution, but hopefully it'll put you on the right path.

Underscore Templating - Partials (with RequireJS)

I am using the tpl! plugin for RequireJS to import and compile my templates into my application - similar to the text! plugin:
define([
"tpl!../templates/newsfeed.html",
"tpl!../templates/friends.html",
"tpl!../templates/tag.html",
"tpl!../templates/tags.html",
], function (renderNewsfeed, renderFriends, renderTag, renderTags) { … });
This all works great, but I have got to a stage where I would ideally like to use some form of partials.
Currently, if I want to use a template inside a template, I would have to pass the compiled partial to the template I am rendering, like so:
$('body').append(renderTags({
tags: tags,
renderTag: renderTag
}));
Then, In my template:
<section class="tags-list">
<h1 class="h4 hidden">Tags</h1>
<% _.each(tags, function (tag) { %>
<%= renderTag({ tag: tag }) %>
<% }); %>
</section>
If I do not pass the compiled partial onto the template, then it's not going to find it.
My question is, how could I do this better? If the templates I defined as dependencies in my RequireJS definition were available to the variable scope of the templates themselves (globally), then I probably wouldn't have to pass the compiled partial to the template?
Secondly, it would be really nice to have the same kind of dependency definitions that are available to use with RequireJS but for templates:
define([
'tpl!../templates/tag.html'
], function (renderTag) {
// Obviously this can't be straight HTML, but you get the idea
<section class="tags-list">
<h1 class="h4 hidden">Tags</h1>
<% _.each(tags, function (tag) { %>
<%= renderTag({ tag: tag }) %>
<% }); %>
</section>
});
I might be on a completely different planet here. If I am, would somebody please kindly explain how they use templates. Perhaps I need to switch templating engine?
The solution I come up with was to actually use require() inside the template, to fetch the required partials, for example:
<%
require([
"tpl!../templates/partials/tags.html",
"tpl!../templates/partials/spotify-search.html",
"tpl!../templates/partials/popup.html"
], function (renderTags, renderSpotifySearch, renderPopup) { %>
// Template code goes here
// Partial example:
<%= renderTags({ tags: tags }); %>
<%
}); %>

EJS gem in rails 3.1 won't compile JavaScript templates properly

I am trying to use the EJS gem for templating in rails 3.1. When I require my template in the application.js file
//= require_directory ./templates
The output I get on the client side wraps the template in an anonymous function and namespaces it, but... that's it. This is the generated output I get.
(function() {
this.JST || (this.JST = {});
this.JST["templates/index"] = <article class="item <%=type%>">
<% if (type === "stat") { %>
<h2>
<span>70%</span>
of teens have one or more social network profiles
</h2>
<% } else { %>
<header>
<a href="/posts/<%=id%>">
<h3><%=type%></h3>
<h2><span>- <%=type%></span></h2>
</a>
</header>
<% if (confidential) { %>
<span class="confidential">Confidential</span>
<% } %>
<% if (type === "video" || type === "music") { %>
play
<% } %>
<img src="<%=image%>" alt="" />
<% } %>
</article>;
}).call(this);
I would expect the template to be compiled into a string. That's the experience I've had with Jammit in the past. Do I need to do that manually? Am I missing something?
Thanks in advance,
A
Sprockets wasn't processing your template through EJS because it didn't end in 'ejs'. You need to use an extension ending in ".jst.ejs" with your template files to get them processed in the right order.
Hmm,
Interestingly, installing rails-backbone gem, rather than placing backbone in the app manually, seemed to solve the problem. I also moved the templates into the backbone directory structure. Maybe the ejs gem has some dependency on the backbone gem (unlikely I think)? Or is it something to do with directory nesting levels, or the way asset pipeline includes directories?
Either way, not sure why this is working but it is working none the less. If anyone could serve up an explanation, I'd appreciate it.

Categories

Resources