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 }); %>
<%
}); %>
Related
I have around 10 html documents that only differ due to one div. Is there any way to make a header and footer document, and link them together for each html document? I have used EJS in the past, so I would prefer using it. I am using github pages to host my site, so I cannot use any backend.
Yup! These are called partials in EJS, and you insert them with an include function:
<%- include('header') %>
<!-- Main content -->
<%- include('footer') %>
EJS will look for them in the local directory. Any parameters you passed into app.render in your Express route are passed down to partials too.
If you need to re-use partials on the same page with unique data, you can pass them a data object too:
<% msgs.forEach((msg) => { %>
<%- include('msgPartial', { msg: msg }) %>
<% }) %>
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}' %>
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
I am using Ruby on Rails 3.2.2 and the jquery-rails 2.0.2 gem (including the jQuery UI plugin). In order to DRY (Don't Repeat Yourself) my code I am planning to "extract" the jQuery Dialog Widget in a partial template that will be rendered in my application almost the same way; so, I am thinking to implement and render that template by passing some "complex" code (possibly, JavaScript code) throughout the :locals statement... but I have some trouble on how to properly pass that code.
Specifically, I would like to render the template by passing :locals with which "build" / that "populate" some jQuery Dialog Option, Event, Method (see the jQuery UI Official Documentation for more information) as-like, for example, the option title or the event close (note: it is intended that the "passed" / "builded" / "populated" content is JavaScript code or both JavaScript and Ruby code).
What / how could / should I make to accomplish what I aim to make? Is there some "common" approach or practice?
Here's a simple example. See if this is helpful, and YMMV.
A partial can contain JavaScript and Ruby code as ERB (or HAML, or whatever templating you want to use).
I use the convention of putting shared partials in the directory ./app/views/shared.
Example file ./app/views/shared/_dialog.html.erb:
<!-- typical jQuery dialog box creator -->
<script>
$(function() {
$( "#dialog" ).dialog();
});
</script>
<!-- typical dialog with Ruby ERB for title and message -->
<div id="dialog" title="<%= title %>">
<p><%= message %></p>
</div>
To call the partial from your view page:
<h1>My Page</h1>
<%= render :partial => "shared/dialog",
:locals => { :title => :"Hello", :message => "World" } %>
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.