Can HTML from an ejs template be injected via JQuery?
I have an Index file that has a navbar and a content area, as well as a sample TestButton template that I am trying to render.
Index.ejs
<ul>
.
.
<li id="listItem">Nav Bar Item</li>
</ul>
<div id="display">
</div>
<script>
$('#listItem').click( function(){
// Note - testButtonTemplate is being properly passed in from my routes
$('#display').html( <%- render( testButtonTemplate, {} ) %> );
});
</script>
TestButton.ejs:
<a href="#" class="btn btn-primary" id="test-button">
Click Me!
</a>
I did my best to simplify my code, but basically, when I click on a link from the nav bar, I want to dynamically load a page in the display div.
The nav bar works.
The click functionality works.
If I manuallay display the ejs template in the div, it works.
EX:
<div id="display">
<%- render( testButtonTemplate, {} ) %>
</div>
Otherwise, when I try to load the page with the sample code above, this is the error I get:
"Uncaught SyntaxError: Unexpected token <" and the raw html looks like:
.
.
<script>
$('#listItem').click( function(){
$('#my-test').html( <a href="#" class="btn btn-primary" id="test-button">
Click Me!
</a>
);
});
</script>
So you can see that the html has been properly retrieved from the ejs template class, but JQuery does not like how I am formatting that data. It looks like the data needs to be surrounded in quotes, but simply adding beginning and end quotes does not solve the problem.
I have tried seemingly every combination of quotes, escaped and unescaped html, storing the data in a variable first then trying to inject it, but none are working for me.
Is there an obvious mistake I'm making? I would prefer to solve the problem given my current tool set.
Try loading your template like this:
<script>
var template = new EJS({url: '/TestButton.ejs'});
$('#listItem').click( function(){
$('#display').html(template.render());
});
</script>
Related
I want to use handlebars to display a series of data in an unordered list, split between "title" and "articles." However, some of the articles will contain different HTML tags, be it <a> tags for link. If you take a look at my code below, when the data is appended to the DOM, rather than showing a link like 'You can read more about it here', it says the actual HTML anchor tags. Does anyone know a way around this?
<div id="myDiv"></div>
Consider the following template:
<script id="my-template" type="text/x-handlebars-template">
<div style='margin: 20px'>
<ul style='list-style-type: none; width:500px'>
<h4 style='padding:10px; overflow: auto'>General Credit</h4>
{{#each this}}
<li style='padding:10px; overflow: auto'>
<h3>{{title}}</h3>
<p>{{article}}</p>
</li>
{{/each}}
</ul>
</div>
</script>
My javascript looks like this:
//Data for Articles
var articleData = [
{
title: "My title here",
article: "You can learn more about it <a href='https://www.google.com'>here</a>"
}
];
//Get Template From Script Tag
var source1 = $("#my-template").html();
//Compile Template
var template1 = Handlebars.compile(source1);
$("#myDiv").append(template1(articleData));
You should use triple curly braces:
<li style='padding:10px; overflow: auto'>
<h3>{{{title}}}</h3>
<p>{{{article}}}</p>
</li>
So that handlebars doesn't escape the HTML code within your string.
Here the reference.
Cheers
You can use triple brackets to escape html.
Try {{{article}}}
Simple you {} {} helper call is a simple identifier, followed by zero or more parameters (separated by space). Each parameter is a Handlebars expression.
{{{link story}}}
In this case, link is the name of a Handlebars helper, and story is a parameter to the helper. Handlebars evaluates parameters in exactly the same way described above in "Basic Usage".
Reread the documentation more carefully. Simply put {{{}}} instead of {{}} around the article in my template, and the HTML tags render fine now.
Handlebars documentation: http://handlebarsjs.com/expressions.html
I'm trying to make a dynamic text a link with javascript:
<div class="container">
<div class="form-current">
<form>
<h2 style="margin-left:10px" ><a href='#' onclick="homePage()" id="h2Current" ></a></h2>
<div id="node-current"></div>
</form>
</div>
This is the html and the text is loaded from a DB through a javascript function:
var h2Current = document.getElementById('h2Current');
h2Current.innerHTML = 'Day '+data[0]['day'];
the parameter "data" is a simple Json. The text appear underlined correctly but is impossibile to click.
Thanks in advance
You need to check your JSON. Check JSON i have written is a correct one. check your JSON format. You can use jsonlint.com to validate JSON you are trying to use.
<html>
<head>
</head>
<body>
<div class="container">
<div class="form-current">
<form>
<h2 style="margin-left:10px" ><a href='#' onclick="homePage()" id="h2Current" ></a></h2>
<div id="node-current"></div>
</form>
</div>
</div>
<script>
var data = [{ 'day':'monday'},{ 'day':'tuesday'}]
var h2Current = document.getElementById('h2Current');
h2Current.innerHTML = data[0]['day'];
</script>
</body>
The problem is not the JSON since you say that it appears and is underlined properly. The issue is that you are either not actually calling your 'homePage' function during the 'onclick' event, or there is an unhandled exception in the code of your 'homePage' function. Here is how you go about debugging this issue.
You need to open your site in Chrome, and open the developer tools, 'F12'. Click the 'Sources' tab, and find your .JS file, or the page file, if it contains the function, 'homePage' inline. You may need to open the 'File Navigator'. If this is the case, click the "arrow in the box" directly beneath the 'Elements' tab.
Once the file is opened, find the function declaration 'homePage' and breakpoint the first non var declarative line. Now, simply click the link and step through the function. You may find that you are not even calling the function at all and you may even see JS exceptions listed. Address any exceptions which appear inline in your code. If you are actually reaching your function, step through every line, 'F11' including any nested functions, until you find the exception.
What is the best practise for creating a view page in Rails with different view-options, such as a basic view with mostly text and an advanced view with graphical features? More specifically, I would like to have a view page where the the user can toggle between a basic/advanced show view. Clicking the toggle button renders one set of divs corresponding to selected view. Should this be done though a form_for/if-else statement in html markup or is it better do to do it in javascript? I guess turning the <div id="id"> on/off could be done in javascript through:
$("#id").show()
$("#id").hide()
I have a problem understanding how a rails implementation is done, where do I put the if-else statement (i.e. if user basic view is toggled render <div id="basic">, else <div id="advanced">)?
<%= form_for ??? do |f| %>
...
<%= f.submit ??? %>
<% end %>
Edit 2:
<div class="row">
<div class="span6">
<div class="btn-group" id="basic-advanced" data-toggle="buttons-radio">
<a class="btn btn-small" href="#" id="basicbutton">Basic</a>
<a class="btn btn-small" href="#" id="advancedbutton">Advanced</a>
</div>
</div>
</div>
<div class="container" id="basic">
This is the basic view
</div>
<div class="container" id="advanced">
This is the advanced view
</div>
Now in javascript I have the following:
$("#basic").toggle();
$("#basic-advanced").click(function (){
$("#basic").toggle();
$("#advanced").toggle();
});
I have added the above code, but how do I keep track of which viewing mode that the page is in? From the answers it seems like one could set an instance variable, #viewing_mode, to a value corresponding to the mode, but how should this be done? Through a form?
Update: I managed to achieve a toggle using a session variable, and an if/else statement, the procedure is described here: Session variable not persisting after switch.
If you are using jQuery.show() and jQuery.hide() that means you need both the basic and advanced div tags rendered. Rails needs to render them both. You can just have toggle display:hidden css, here I did that with variable #use_advanced.
<div id="advanced" <%= (#use_advanced) ? '' : ' style="display:hidden"' %>>...
<div id="basic" <%= (#use_advanced) ? ' style="display:hidden"' : '' %>>...
To toggle between the two you could have a button: <button>toggle</button>
$('button').click(function () {
$('#advanced').toggle();
$('#basic').toggle();
});
There are some different cases:
1) if you are able to change your view using CSS only - then you should enclose all the page inside classified div:
<div class='my-style'>
...
</div>
2) if first solution is not applicable then you may use if-else statement:
<% if #my_style_selected %>
<div>My style</div>
<% else %>
<div>Default</div>
<% end %>
3) finally, if neither solution suits you. You may write some JS to dynamically replace your divs. The best solution is to replace some parts of the page using AJAX. If you will render both variants and hide one of them - that will almost double your page load time.
Given that yiou are using rails I would look into using layout for different views rather than having conditional logic.
For more info see rails 3, how add a view that does not use same layout as rest of app?
Here's a JavaScript object (written in CoffeScript) that will allow you to switch between as many modes as you like using a convention:
class ModeSwitcher
constructor: ($area, modes) ->
$area = $ $area
me = this
bindToggle = (mode) ->
$area.find('a.toggle.' + mode).click ->
me.toggle mode
$ ->
bindToggle(mode) for mode in modes
toggle: (mode) ->
$('.mode').hide()
$('.' + mode + '.mode').show()
Here's an example of how you could construct the object (JavaScript/jQuery):
var switcher = new ModeSwitcher('body', ['basic', 'advanced']);
$(function() {
switcher.switch("<%= #mode %>");
});
The #mode instance variable is set in the Rails controller and says which mode the view should start out in:
#mode = params[:mode] || 'basic'
As for how it works by convention, you would have sections like this:
<div class="basic mode">...</div>
<div class="advanced mode">...</div>
And you would have links like this:
<a class="toggle advanced">Switch to advanced mode</a>
<a class="toggle basic">Switch to basic mode</a>
When constructed, the object will iterate through all the declared modes and look for the toggle links. It will then bind the toggling function to those links.
I just started using Mustache and I like it so far, but this has me perplexed.
I am using the GitHub gist API to pull down my gists, and part of what I want to do is include the embedding functionality into my page. The problem is Mustache seems to not want to have anything to do with my dynamic script tag.
For example, this works fine:
<div class="gist-detail">
{{id}} <!-- This produces a valid Gist ID -->
</div>
Additionally, this works perfect:
<div class="gist-detail">
<script src='http://gist.github.com/1.js'></script> <!-- Produces the correct embed markup with Gist ID #1 -->
</div>
If I try to pull these together, something goes terribly wrong:
<div class="gist-detail">
<script src='http://gist.github.com/{{id}}.js'></script> <!-- Blows up! -->
</div>
Chrome Inspector shows this:
GET https://gist.github.com/%7B%7Bid%7D%7D.js 404 (Not Found)
... which looks like to me something is weird with escapes or whatnot, so I switch over to the raw syntax:
<div class="gist-detail">
<script src='http://gist.github.com/{{{id}}}.js'></script> <!-- Blows again! -->
</div>
And I get the same result in Inspector:
GET https://gist.github.com/%7B%7B%7Bid%7D%7D%7D.js 404 (Not Found)
How do I get the correct values to embed in the script tag?
EDIT
I am injecting the template as follows (in document.ready:
function LoadGists() {
var gistApi = "https://api.github.com/users/<myuser>/gists";
$.getJSON(gistApi, function (data) {
var html, template;
template = $('#mustache_gist').html();
html = Mustache.to_html(template, {gists: data}).replace(/^\s*/mg, '');
$('.gist').html(html);
});
}
The actually template is inside of a ruby partial, but it is wrapped in a div (not a script tag, is that a problem?) (that's hidden):
<div id="mustache_gist" style="display: none;">
{{#gists}}
<!-- see above -->
{{/gists}}
</div>
I assume a div is ok rather than a script because in either case, I'm pulling the .html(). Is this a bad assumption?
To avoid automatic escaping in Mustache use {{{token}}} instead of {{token}}.
It seems like your template is in HTML and trying to retrieve the template using html() results in a pre-URL-escaped template to be returned. Try placing your template inside a <script type="text/html"> tag instead.
When you embed your template inside an HTML element that excepts more HTML elements as children, it may get processed by the browser as HTML. Escaping may occur. By using a <script> tag with a non-script content type, you're basically telling the browser not to touch your template.
It looks like your script is getting requested before Mustache has a chance to update the src property. What you want to do is define the template in a way that it's not parsed as part of the DOM. A common approach is to define your template inside of a <textarea> tag. This will preserve formatting and prevent character escaping.
<textarea id="gist-detail-template" style="display:none">
<script src='http://gist.github.com/{{id}}.js'></script>
</textarea>
Now, to instantiate the template:
var template = $('#gist-detail-template').val();
var html = Mustache.to_html(template, yourTemplateData);
Here's an official example: http://mustache.github.com/#demo
I'm trying to make a cross platform mobile application using jQuery Mobile.
I have a JSON string that displays information about a specific object (check it out here: http://app.calvaryccm.com/mobile/web/teachings/json?callback=?) and I want it to turn it into a list view that connects to a single item view. The problem is, the listview isn't displaying like a list at all. It is almost like HTML without CSS. Check it out here: http://mbeta.calvaryccm.com/#teachings
This is the Javascript for parsing the JSON string:
<!-- Getting Teaching Data -->
<script type="text/javascript">
$(document).ready(function () {
$.getJSON("http://app.calvaryccm.com/mobile/web/teachings/json?callback=?",
function (data) {
//remove any characters from the query that might be unsafe to use as an ID for a page
//data.pageId = data.MessageNumber.replace(/[^\w]/, "");
//Feed the data to the template and add the new page to the body.
var res = $("#teachingTemplate").tmpl(data); //.appendTo(document.body);
$("#teachings").append(res);
//Grab a reference to that shiny new page
//var newpage = $("#" + data.pageId);
});
});
//Makes date readable
function GetDate(jsonDate) {
var value = new Date(parseInt(jsonDate.substr(6)));
return value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
}
This is supposed to be the list view:
<script id="teachingTemplate" type="text/x-jquery-tmpl">
<div id="${MessageNumber}">
<div data-role="header">
<a data-icon="arrow-l" href="#" data-rel="back">Back</a>
<h1>${Title}</h1>
</div>
<div data-role="content">
<div class="teachingsForm">
<ul data-role="listview">
<li><a href="singleTeachingView" class="tableImage">
<img src="" alt=""/>
<h3>${Title}</h3>
<p>${Speaker} - ${GetDate(MessageDate)} - ${MessageNumber} {{if Book != null}} - ${Book.BookName} ${ChapterVerse}{{/if}}</p>
</a>
</li>
</ul>
</div>
</div>
<div data-role="footer">
<h4>2011 Calvary Chapel Melbourne</h4>
</div>
I cannot figure out why my listview isn't displaying right. If you want to see it in action look here: http://mbeta.calvaryccm.com/#teachings . I need help getting my listview to display right and direct to the right page.
It looks exactly like what your template says it should look like. Note that each of your <ul> elements only has one <li> element in it.
Your template starts off with an outer <div>, and then there's a "header" <div> with the <h1> title. Then there's the "content" <div>, and ultimately the <ul>, and finally the "footer". That's what your result page looks like. If you want to do some sort of iteration, well, you'll have to explicitly do that in your template, because otherwise the template code will assume you just want it to re-apply the template to each object in the array you pass it.
There's nothing wrong with the "JSON parsing". Note that in your JSON, there's no need to quote "/" characters with "\" and in fact (though it doesn't matter for JSONP) it's not valid JSON.
edit — OK so now that I've pulled my head out from wherever it was I think I see what you're doing. The problem may be that you just need to call
$.mobile.changePage();
at the end of your JSONP callback function. However I note that your "$.mobile" doesn't have a "changePage()" function ... I don't know what that means. You're using a pretty old version of jQuery too.