I'm using the underscore library to populate my template.
It works like a charm when my variable day is inside a tag. like this snippet from my code:
<template>
<label>Schedule</label>
<select class="form-control" onchange="set_from_hour('<%=day%>')" name="some_name" id="<%=day%>from_hour" >
<option value=0 selected='selected'>00</option>
<option value=1>01</option>
...
</template>
to populate this template I use this function:
days_schedule = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']
function show_schedule_week(){
_.each(days_schedule,function(day){
_.templateSettings.variable = 'day'
var template = _.template($('template').html())
console.log(day+'_schedule')
$('#'+day+'_schedule').prepend(template(day))
})
}
Now the problem comes when i want my HTML code to display the variables, say in a header tag the variable day like below:
<template>
<h1> <%- day%> </h1>
</template>
The browser displays
<%- day%>
instead of the value of the variable.
Do you know what I'm doing wrong?
The problem is with the string that you pass to _.template(): the HTML that you read from the DOM does not have <%- day%>, but <%- day>. This is because < has a special meaning in HTML, and as here it does not represent a tag, it is encoded with an HTML entity. The same goes for >.
There are several solutions to this. One is to not use a template element in your HTML, but a string literal in your code. If however you prefer to stick with the template element (which is reasonable), then you could turn the template content into an HTML comment.
Here is an example:
days_schedule = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']
function show_schedule_week() {
_.each(days_schedule,function(day){
_.templateSettings.variable = 'day'
// unwrap the template content by removing the HTML comment tag start/end
var template = _.template($('template').html().slice(4, -3));
$('#'+day+'_schedule').prepend(template(day))
})
}
show_schedule_week();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.11.0/underscore-min.js" integrity="sha512-wBiNJt1JXeA/ra9F8K2jyO4Bnxr0dRPsy7JaMqSlxqTjUGHe1Z+Fm5HMjCWqkIYvp/oCbdJEivZ5pLvAtK0csQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.js"></script>
<template><!--
<h3> <%- day%></h3>
--></template>
<div id="monday_schedule"></div>
Related
I'm trying to reuse the same HTML code in both PHP and Javascript. In PHP I'm using Laravel's component mechanism to load the file passing the necessary parameters.
#foreach($files as $file)
<x-my-file name="my_name[][image]" imageId="{{ $file->id }}" imageUrl="{{ $file->url }}" />
#endforeach
It's working just fine.
At the bottom of the page, I'm using jQuery like below:
#push('scripts')
<script>
var imageId = null;
var imageUrl = '';
jQuery(function($) {
$('body').on('click', '#btn-add', function() {
var imageId = $(this).data('id');
var imageUrl = $(this).data('url');
$('#target').append(`#include('components.my-file', ['name' => 'my_name[][image]', 'imageId' => 15, 'imageUrl' => '/path/to/an-image.jpg'])`);
});
});
</script>
#endpush
Everything just works fine. With every click, the same component is getting appended with the same data that I've passed.
But, if I replace the static values of the following line:
$('#target').append(`#include('components.my-file', ['name' => 'my_name[][image]', 'imageId' => 15, 'imageUrl' => '/path/to/an-image.jpg'])`);
with the dynamic values I have, using the Javascript template literals:
$('#target').append(`#include('components.my-file', ['name' => 'my_name[][image]', 'imageId' => ${imageId}, 'imageUrl' => ${imageUrl}])`);
The page stopped rendering with a Parse error from Laravel:
ErrorException
Use of undefined constant imageId - assumed 'imageId' (this will throw an Error in a future version of PHP)
Alternative?
I've seen some AJAX methods on loading blade in Javascript that I personally did not like.
Debugging
If I comment out the line, it still throws the same error until I remove the line completely. Hence I'm suspecting that the curly braces of the JS template literals (${}) are getting parsed as a Laravel blade syntax ({{}}).
Please note that instead of the backticks (`) I tried with the single quote and double quotes (as can be seen here), but getting errors as they are conflicting with the HTML code.
I've gone through the Laravel Documentation on Blade and JS Frameworks, but IMO, both the # syntax and #verbatim are for the exact opposite purpose, and won't serve me.
Update
I eventually came up with an alternative to have blade in JavaScript:
#push('scripts')
<script type="text/template" id="my-template">
#include('components.my-file', ['name' => 'my_name[][image]'])
</script>
<script>
var template = document.getElementById('my-template').innerHTML;
console.log(template); // Successful grabbing the HTML.
$(template).find('.image-placeholder').attr('src', imageUrl); // Replaced the HTML with the modified img tag only.
console.log(template.prop('outerHTML'));
</script>
#endpush
With this code, I can have the HTML perfectly without the dynamic data. The first console.log is showing the HTML perfectly:
<div>
<img src="" alt="" class="image-placeholder">
<input type="hidden" name="my_name[][image]" value="">
</div>
Now, the template is not a Laravel blade/component so far, so I have to put my dynamic data onto it. When I'm trying to put my dynamic data onto the HTML, the HTML is being replaced by the modified <img> string completely. The second console.log output is:
<img src="/dynamic-path/to/the-image.jpg" alt="" class="image-placeholder">
Blade components are served by laravel.
After response returned from server blade component won't work anymore.
You can use some kind of placeholder and replace them dynamically as you did last time.
As example,
var template = document.getElementById('my-template').innerHTML;
template = template
.replace('{src}', 'https://example.com/image.jpg')
.replace('{id}', 'ImageID')
.replace('{name}', 'my_name[][image]')
console.log(template)
<!--
<script type="text/template" id="my-template">
<x-my-file name="{name}" imageId="{id}" imageUrl="{src}" />
</script>
I am using a template below
-->
<script type="text/template" id="my-template">
<div>
<img src="{src}" id="{id}" class="image-placeholder">
<input type="hidden" name="{name}" value="">
</div>
</script>
I'm trying to achieve something similar to what JSRender does, but I'm not sure how to go about it. Consider the HTML "template" below:
<!DOCTYPE html>
<body>
<div class="content">
<div class="notifications">{{:notifications}} notifications</div>
<div class="something else">this is {{:something_else}} to show</div>
</div>
</body>
</html>
Supposed I have JSON data like so:
{"notifications": "3", "something_else": "some arbitrary data"}
How do I populated this data into the HTML page? The way JSRender does it seems to involve creating a separate template in a <script> tag, then populating the data into the template and finally copying the template into an empty container. Is there a way to avoid this template redefinition? I believe my HTML page can already act like a template as demonstrated above.
The Question: is it possible to display JSON data into a ready HTML page (such as above) with defined "data positions"? As part of the challenge, using $('.notifications').html()-related methods should be avoided since this would be cumbersome when handling large extensive data.
You can do that using top-level JsViews top-level data-linking - with an element such as a <span> for each insertion point.
<div class="content">
<div >this is <span data-link="something_else></span> to show</div>
...
Code:
$.link(true, ".content", data);
In addition, the data is data-bound to the HTML.
Here is a sample which shows the data-binding by letting you actually change a data property dynamically:
It also shows data-linking to the src and title attributes of an <img> tag. See here for more information about different data-link targets.
var data = {notifications: "3", something_else: "some arbitrary data",
imgData: {img1: {src: "http://www.jsviews.com//icons/android-chrome-36x36.png",
desc: "some image"}}};
$.link(true, ".content", data, {replace: true});
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.js"></script>
<div class="content">
<div ><span data-link="notifications"></span> notifications</div>
<div >this is <span data-link="something_else"></span> to show</div>
<img data-link="src{:imgData.img1.src} title{:imgData.img1.desc}"/>
<br/>Edit: <input data-link="something_else"/>
</div>
While BorisMoore's answer addresses the question adequately, I crafted a "hack" that also appears to work with the ability to support attributes on almost all elements, though I don't know to what extent it is reliable.
However, this requires one to change the data structure to also indicate the type of element and even the part of it (attribute) where the data is to be inserted. The data would need to look like so:
{"notifications": "span:|3", "something_else": "span:|some arbitrary data", "avatar":"img.alt:|A"}
Then in JQuery, one could do something like so:
$.each(data, function(key, value) {
value = value.split(":|");
var element = value[0];
value = value[1];
if(element.indexOf('.') == -1){
var content = $(element + ':contains("{{:'+key+'}}")').last().html().replace("{{:"+key+"}}", value);
$(element + ':contains("{{:'+key+'}}")').html(content);
}else{
element = element.split('.');
var attribute = element[1];
element = element[0];
$(element + '['+attribute+'="{{:'+key+'}}"]').last().attr(attribute, value);
}
});
EDIT: The main drawback of this method is that it unbinds all attached events when an elements property is modifed this way.
I am currently working on a component that looks like this:
<pre v-highlightjs>
<code>
<slot></slot>
</code>
</pre>
So the problem I have is that when I am writing html inside the slot, this html is rendered and not shown as a string or code-sample in my case. I have tested it with escaped < and > and it works. How can I access the html inside the slot and escape it automatically?
Thank you
EDIT:
I use highlight.js for that component to highlight the code inside my component. highlight.js can highlight html aswell. When I put e.g.
<html><head></head><body></body></html>
inside my slot, the box is shown, but the input is rendered as html. So I want to escape the html-Tags (and other code-snippets) so that it is shown and ofc highlighted. Hope that specifies my problem a bit more.
An important limitation to be aware of is that your HTML is not HTML, it is input to Vue's templating engine, so what a component sees in the slot may not be exactly what is in the HTML file.
Writing a directive to take HTML content and replace it with a text version of that content is pretty straightforward. Putting that in a component is also pretty straightforward. You can see these together in the snippet below.
What you will also see is that the Vue templating engine strips out tags that shouldn't be inside the body: the html, head, and body tags don't make it to the component. Their contents, if any, remain.
If it is important to be able to use those tags (or, for that matter, possibly invalid HTML), you will not be able to do it in a slot. You will need to have the HTML as a data string, which you can easily interpolate using the normal curlies.
new Vue({
el: '#app',
data: {
headContent: 'Something in the head'
},
components: {
htmlEscaper: {
template: '#html-escaper',
directives: {
escapeContent: {
bind(el) {
const html = el.innerHTML;
el.textContent = html;
}
}
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<html-escaper>
<html>
<head>
{{ headContent }}
</head>
<some-junk>
Custom tags are ok
</some-junk>
<body>
<div>This part is legit</div>
</body>
</html>
</html-escaper>
</div>
<template id="html-escaper">
<code v-escape-content>
<slot>
</slot>
</code>
</template>
I am trying to work with handlebar templates, but it's not going so well.
In *.html.twig I have:
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
<span id="demo">
</span>
<script>
var source = document.getElementById("entry-template").innerHTML;
var template = Handlebars.compile(source);
var context = {title: "My New Post", body: "This is my first post!"};
var html = template(context);
document.getElementById("demo").innerHTML = html;
</script>
And there is an error:
Variable "title" does not exist.
When I put this code in simple index.html w/o Symfony etc. it works. Any ideas how to fix it?
This is happening because Twig is also an HTML templating language which also uses {{ ... }} syntax to denote variables, just like Mustache is, so Twig is trying to parse it before outputting the HTML.
If you want to use Twig and Mustache together, then you'll need to escape/double-encode the curly braces that are intended for Mustache so that Twig doesn't try to parse them first.
There is a repo on Github which lets you define your own custom delimiters for Handlebar templates.
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