I'm trying to learn about new usages of javascript as a serverside language and as a functional language. Few days ago I heard about node.js and express framework. Then I saw about underscore.js as a set of utility functions. I saw this question on stackoverflow
. It says we can use underscore.js as a template engine. anybody know good tutorials about how to use underscore.js for templating, especially for biginners who have less experience with advanced javascript. Thanks
Everything you need to know about underscore template is here. Only 3 things to keep in mind:
<% %> - to execute some code
<%= %> - to print some value in template
<%- %> - to print some values HTML escaped
That's all about it.
Simple example:
var tpl = _.template("<h1>Some text: <%= foo %></h1>");
then tpl({foo: "blahblah"}) would be rendered to the string <h1>Some text: blahblah</h1>
<!-- Install jQuery and underscore -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<!-- Create your template -->
<script type="foo/bar" id='usageList'>
<table cellspacing='0' cellpadding='0' border='1' >
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<%
// repeat items
_.each(items,function(item,key,list){
// create variables
var f = item.name.split("").shift().toLowerCase();
%>
<tr>
<!-- use variables -->
<td><%= key %></td>
<td class="<%= f %>">
<!-- use %- to inject un-sanitized user input (see 'Demo of XSS hack') -->
<h3><%- item.name %></h3>
<p><%- item.interests %></p>
</td>
</tr>
<%
});
%>
</tbody>
</table>
</script>
<!-- Create your target -->
<div id="target"></div>
<!-- Write some code to fetch the data and apply template -->
<script type="text/javascript">
var items = [
{name:"Alexander", interests:"creating large empires"},
{name:"Edward", interests:"ha.ckers.org <\nBGSOUND SRC=\"javascript:alert('XSS');\">"},
{name:"..."},
{name:"Yolando", interests:"working out"},
{name:"Zachary", interests:"picking flowers for Angela"}
];
var template = $("#usageList").html();
$("#target").html(_.template(template,{items:items}));
</script>
JsFiddle Thanks #PHearst!
JsFiddle (latest)
JsFiddle List grouped by first letter (complex example w/ images, function calls, sub-templates) fork it! have a blast...
JsFiddle Demo of XSS hack noted by #tarun_telang below
JsFiddle One non-standard method to do sub-templates
In it's simplest form you would use it like:
var html = _.template('<li><%= name %></li>', { name: 'John Smith' });
//html is now '<li>John Smith</li>'
If you're going to be using a template a few times you'll want to compile it so it's faster:
var template = _.template('<li><%= name %></li>');
var html = [];
for (var key in names) {
html += template({ name: names[i] });
}
console.log(html.join('')); //Outputs a string of <li> items
I personally prefer the Mustache style syntax. You can adjust the template token markers to use double curly braces:
_.templateSettings.interpolate = /\{\{(.+?)\}\}/g;
var template = _.template('<li>{{ name }}</li>');
The documentation for templating is partial, I watched the source.
The _.template function has 3 arguments:
String text : the template string
Object data : the evaluation data
Object settings : local settings, the _.templateSettings is the global settings object
If no data (or null) given, than a render function will be returned. It has 1 argument:
Object data : same as the data above
There are 3 regex patterns and 1 static parameter in the settings:
RegExp evaluate : "<%code%>" in template string
RegExp interpolate : "<%=code%>" in template string
RegExp escape : "<%-code%>"
String variable : optional, the name of the data parameter in the template string
The code in an evaluate section will be simply evaluated. You can add string from this section with the __p+="mystring" command to the evaluated template, but this is not recommended (not part of the templating interface), use the interpolate section instead of that. This type of section is for adding blocks like if or for to the template.
The result of the code in the interpolate section will added to the evaluated template. If null given back, then empty string will added.
The escape section escapes html with _.escape on the return value of the given code. So its similar than an _.escape(code) in an interpolate section, but it escapes with \ the whitespace characters like \n before it passes the code to the _.escape. I don't know why is that important, it's in the code, but it works well with the interpolate and _.escape - which doesn't escape the white-space characters - too.
By default the data parameter is passed by a with(data){...} statement, but this kind of evaluating is much slower than the evaluating with named variable. So naming the data with the variable parameter is something good...
For example:
var html = _.template(
"<pre>The \"<% __p+=_.escape(o.text) %>\" is the same<br />" +
"as the \"<%= _.escape(o.text) %>\" and the same<br />" +
"as the \"<%- o.text %>\"</pre>",
{
text: "<b>some text</b> and \n it's a line break"
},
{
variable: "o"
}
);
$("body").html(html);
results
The "<b>some text</b> and
it's a line break" is the same
as the "<b>some text</b> and
it's a line break" and the same
as the "<b>some text</b> and
it's a line break"
You can find here more examples how to use the template and override the default settings:
http://underscorejs.org/#template
By template loading you have many options, but at the end you always have to convert the template into string. You can give it as normal string like the example above, or you can load it from a script tag, and use the .html() function of jquery, or you can load it from a separate file with the tpl plugin of require.js.
Another option to build the dom tree with laconic instead of templating.
I am giving a very simple example
1)
var data = {site:"mysite",name:"john",age:25};
var template = "Welcome you are at <%=site %>.This has been created by <%=name %> whose age is <%=age%>";
var parsedTemplate = _.template(template,data);
console.log(parsedTemplate);
The result would be
Welcome you are at mysite.This has been created by john whose age is 25.
2) This is a template
<script type="text/template" id="template_1">
<% _.each(items,function(item,key,arr) { %>
<li>
<span><%= key %></span>
<span><%= item.name %></span>
<span><%= item.type %></span>
</li>
<% }); %>
</script>
This is html
<div>
<ul id="list_2"></ul>
</div>
This is the javascript code which contains json object and putting template into html
var items = [
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
}
];
$(document).ready(function(){
var template = $("#template_1").html();
$("#list_2").html(_.template(template,{items:items}));
});
with express it's so easy. all what you need is to use the consolidate module on node so you need to install it :
npm install consolidate --save
then you should change the default engine to html template by this:
app.set('view engine', 'html');
register the underscore template engine for the html extension:
app.engine('html', require('consolidate').underscore);
it's done !
Now for load for example an template called 'index.html':
res.render('index', { title : 'my first page'});
maybe you will need to install the underscore module.
npm install underscore --save
I hope this helped you!
I wanted to share one more important finding.
use of <%= variable => would result in cross-site scripting vulnerability. So its more safe to use <%- variable -> instead.
We had to replace <%= with <%- to prevent cross-site scripting attacks. Not sure, whether this will it have any impact on the performance
Lodash is also the same
First write a script as follows:
<script type="text/template" id="genTable">
<table cellspacing='0' cellpadding='0' border='1'>
<tr>
<% for(var prop in users[0]){%>
<th><%= prop %> </th>
<% }%>
</tr>
<%_.forEach(users, function(user) { %>
<tr>
<% for(var prop in user){%>
<td><%= user[prop] %> </td>
<% }%>
</tr>
<%})%>
</table>
Now write some simple JS as follows:
var arrOfObjects = [];
for (var s = 0; s < 10; s++) {
var simpleObject = {};
simpleObject.Name = "Name_" + s;
simpleObject.Address = "Address_" + s;
arrOfObjects[s] = simpleObject;
}
var theObject = { 'users': arrOfObjects }
var compiled = _.template($("#genTable").text());
var sigma = compiled({ 'users': myArr });
$(sigma).appendTo("#popup");
Where popoup is a div where you want to generate the table
Related
Could someone please tell me how to change the default delimiter for mustache.js? The default delimiter is {{var}} and I would like to change it to {|var|}
I have the following code:
$('body').append(Mustache.render(this.template, data));
Many thanks
As per the documentation:
Set Delimiter tags start with an equals sign and change the tag delimiters from {{ and }} to custom strings.
Consider the following contrived example:
* {{ default_tags }}
{{=<% %>=}}
* <% erb_style_tags %>
<%={{ }}=%>
* {{ default_tags_again }}
So for your case where you want to use {|var|} you could probably use:
{{={| |}=}}
Note, here is another example that changes the default delimiter to triple-mustaches.
I think a javascript example will be helpful too.
var template = $('#template').html();
var parseTags = new Array();
parseTags.push("[[");
parseTags.push("]]");
Mustache.parse(template,parseTags); // optional, speeds up future uses
var rendered = Mustache.render(template, {name: "<%Luke%>"});
In the app.js, use the last line in the following snippet. The first 2 are for reference:
// view engine setup
app.set('views', path.join(__dirname, 'views/templates'));
app.set('view engine', 'hjs');
app.locals.delimiters = '{| |}';
There's a much neater way to do this now and it's documented well.
https://github.com/janl/mustache.js/
var customTags = [ '<%', '%>' ];
Mustache.render(template, view, {}, customTags);
//Either tell mustache to use them each time
Mustache.render(template, view, {}, customTags);
//Or override the tags property and Mustache knows what to do until you tell it otherwise
Mustache.tags = customTags;
var updateTemplate = '<small class="text-muted">Last updated <% updated_at %> by <% updated_by %></small>';
var html = Mustache.render(updateTemplate, {updated_at:'2019-11-18 14:54:20',updated_by:'abc'});
Is it possible to:
1
When I do:
$('#NContrato' ).val('${personList[0].NContrato}');
It works. But if I do
$('#NContrato' ).val('${personList[' + ind + '].NContrato}');
$('#NContrato' ).val('${personList[ind].NContrato}');
$('#NContrato' ).val('${personList[${ind}].NContrato}');
It doesn't and this is request.setAttribute(personList, ...) in a controller (I'm using spring MVC)
$('#NContrato' + ind) // JQuery $() ...It works but
${'${personList[' + ind ...} //Request ${} doesn't work concatenating string's
${personList[0].NContrato} // Works
Is there a way to do it?
2
I'd like to change
newPerson to have the clone of
${personList[0]}
I tried this ...
<script type="text/javascript">
function setNewPerson(ind) {
${newPerson=personList[0]};
}
</script>
but .... it gives this error:
org.apache.jasper.JasperException: /WEB-INF/pages/t4imovelZMAguaEdpGass.jsp(153,3) PWC6038:
"${newPerson=personList[0]}" contains invalid expression(s): javax.el.ELException:
Error Parsing: "${newPerson=personList[0]}"
EDIT:
JSP
<c:forEach items="${personList}" var="item" varStatus="status">
.....
<button type="button" onclick="change(${item.id},${status.count})"></button>
.....
</c:forEach>
.....
<form:form action="save()" method="post" modelAttribute="newPerson" id="personId" >
....
<form:input path="NContrato" id="NContrato" />
....
</form>
<script type="text/javascript">
function change(id, ind) {
$('#NContrato' ).val('${personList[0].NContrato}'); //Works
var ind=0;
$('#NContrato' ).val("${'personList[' + ind + '].NContrato'}"); // <- 1 Question
....
${newPerson=personList[0]}; // <- 2 Question
}
</script>
Controller (Servlet)
#RequestMapping(value="/saves*", method = RequestMethod.POST )
public #ResponseBody ModelAndView save(#ModelAttribute("newPerson") Person person,
BindingResult errors, HttpServletRequest request) throws Exception {
...
request.setAttribute("newPerson", subForm);
...
request.setAttribute("personList", personManager.getPersonList())
....
return new ModelAndView( "personJSP");
}
If you start coding JSP using EL without first learning the basics, the original old way of doing things, you will be forever confused. EL's ${} looks kind of like jquery's $() operator, but it is not.
The EL expression
${personList[0].NContrato}
is equivalent to
<%= personList[0].NContrato %>
or
<% out.print( personList[0].NContrato ); %>
It runs on the server side and prints the value of personList[0].NContrato into the HTML source you are building.
The expression
${personList[' + ind + '].NContrato}
does nothing. Why? Because ${} runs on the server and only prints variables. There is no variable named "personList[' + ind + '].NContrato". That exact text "${personList[' + ind + '].NContrato}" will be printed into your HTML source. Hence the importance of viewing the source in the browser when troubleshooting.
As for
${newPerson=personList[0]};
you can't do an assignment like that in EL. Ask yourself if the following would make any sense at all?
<% out.print( newPerson=personList[0] ); %>
It doesn't make sense to do an assignment inside a print command.
I'd like to know if a better approach exists to store data in html content.
At the moment I got some values stored in my html file using hidden field. These values are generated by code behind.
Html:
<input type="hidden" id="hid1" value="generatedValue1" />
<input type="hidden" id="hid2" value="generatedValue2" />
And therefore I get those values on client side using jquery, in order to pass them to an ajax request.
JQuery
$.ajax({
data:{
var1 : $('#hid1').val(),
var2 : $('#hid2').val()
}
);
So is this the correct way to do this, or does it exist a smoother solution to achieve the same result? Since I don't need these values to be posted on page submit the input hiddenis probably gross.
What I usually do is adding the values as data- attributes to the html form:
<form data-field1="generatedValue1" data-field2="generatedValue2">
...
</form>
And then, retrieve them with jQuery:
...
$form = $( my_selector_to_take_the_form );
data:{
var1 : $('form').attr('data-field1'),
var2 : $('form').attr('data-field1')
}
With this, you won't post any hidden field
If you don't need those in a form, then just make them variables in your JavaScript. To output them, encode them via the JavaScriptSerializer class:
<%
// Presumably somewhere in your C# code...
JavaScriptSerializer serializer = new JavaScriptSerializer();
%>
<script>
var hid1 = <%= serializer.Serialize(valueForHid1) %>;
var hid2 = <%= serializer.Serialize(valueForHid2) %>;
</script>
(See note below about globals.)
Using them later:
$.ajax({
data:{
var1 : hid1,
var2 : hid2
}
);
Globals: As shown there, hid1 and hid2 end up as globals (on most browsers, they do when you use hidden fields as well). I recommend not using globals, but instead wrapping everything in scoping functions:
(function() {
var hid1 = <%= serializer.Serialize(valueForHid1) %>;
var hid2 = <%= serializer.Serialize(valueForHid2) %>;
// ....
$.ajax({
data:{
var1 : hid1,
var2 : hid2
}
);
})();
If for some reason you have to use a global, use just one:
var myOneGlobal = {
hid1: <%= serializer.Serialize(valueForHid1) %>,
hid2: <%= serializer.Serialize(valueForHid2) %>
};
Using that later:
$.ajax({
data:{
var1 : myOneGlobal.hid1,
var2 : myOneGlobal.hid2
}
);
You can output an entire object graph to one variable (perhaps myOneGlobal) with the serializer:
<script>
var myOneGlobal = <%= serializer.Serialize(objectWithData) %>;
</script>
You can use the new HTML5 "data" attributes. (http://html5doctor.com/html5-custom-data-attributes/)
Your codebehind section would do something like this:
<ul data-listname="{put name here}">
<li data-key="{put key here}>
Item1
</li>
</ul>
And then in your jQuery you can do:
var firstId = $('li').first().data('id');
var list = $('ul').data('listname');
Make sure to only use lowercase after the data-
I have found, that it will not work correctly otherwise.
You can also set the data like this:
$('#something').data('smthgnelse', 'Hi there');
You should use the HTML5 data attribute.
i.e My Link
You can easy access this attributes i.e with jQuery
$(".mylink").attr("data-YOURKEY");
John Resig explained it well:
http://ejohn.org/blog/html-5-data-attributes/
Please also read the specs from HTML5-Doctor
http://html5doctor.com/html5-custom-data-attributes/
..and if you like to go a bit deeper:
http://www.w3.org/html/wg/drafts/html/master/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
I need a bit of help with some Ruby array jujitsu.
I have the following array called #tasks:
[#<PivotalTracker::Story:0x007f9d6b8 #id=314, #url="http://www.pivotaltracker.com/", #created_at=#<DateTime: 2012-06-18T20:23:42+00:00 ((2456097j,73422s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test ", #description="This is the description for \"Test\"", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>, #<PivotalTracker::Story:0x007f9d6b8 #id=315, #url="http://www.pivotaltracker.com/", #created_at=#<DateTime: 2012-06-18T20:25:20+00:00 ((2456097j,73520s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test 2", #description="This is the description for \"Test 2\"", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>, #<PivotalTracker::Story:0x007f9d6b8 #id=316, #url="http://www.pivotaltracker.com/story/", #created_at=#<DateTime: 2012-06-18T20:25:26+00:00 ((2456097j,73526s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test 3", #description="Description for Test 3 ", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>]
My end goal is to create a JavaScript array in my .erb view with just the ID values in the above array.
I was thinking of trying something like:
var myJSArray = [<%= #tasks.each { |t| print t.id.to_s+", " } %>];
However, that obviously appends a "," to the end of the string, which is not desirable (i.e. it returns "314, 315, 316,". It also seems like a bit of a hack and not the right way to do it.
Any ideas on how to do this properly?
Thank you!
UPDATE: After some more research on SO, it seems I can do this in two steps:
#ids = #tasks.map { |t| t.id }
and then use that in the view with:
var myJSArray = [<%= #ids.map(&:to_s).join(", ") %>];
Not sure if this is ideal or if it could be done in a single step, though.
var myJSArray = [<%= j #tasks.map(&:id).join(',') %>];
Or you may prefer something like this:
var myJSArray = <%= #tasks.map(&:id).to_json %>;
Use Array#join :
<%=print #task.map {|t| t.id.to_s}.join(', ') %>
I am making a css grid system that relies on the concept of blocks. So I have a base file like:
$max-columns: 4;
$block-width: 220px;
$block-height: 150px;
$block-margin: 10px;
And it is used by a mixin:
#mixin block ($rows, $columns, $max-columns) {
display: block;
float: left;
margin: $block-margin 0 0 $block-margin;
box-sizing: border-box;
width: ($block-width * $columns) - $block-margin;
}
But I'd also like javascript to have access to the variables in the base file. I was thinking that I could make an invisible div, and give it the $block-width, $block-height, and $block-margin attributes and pull the values from there. But max-columns, doesn't map to anything directly, so I'd have to come up with a hacky way to render it in a div. Is there a cleaner way to share values from sass/css to javascript or vice versa?
If you use webpack you can use sass-loader to exportvariables like:
$animation-length-ms: $animation-length + 0ms;
:export {
animationMillis: $animation-length-ms;
}
and import them like
import styles from '../styles/animation.scss'
const millis = parseInt(styles.animationMillis)
https://blog.bluematador.com/posts/how-to-share-variables-between-js-and-sass/
I consider my solution to be quite hokey; but it does work...
In my _base.scss I have some variables defined:
$menu_bg: rgb(45, 45, 45);
$menu_hover: rgb(0, 0, 0);
In a menu.scss I have:
#import "base";
#jquery_vars {
.menu_bg {
background-color: $menu_bg;
}
.menu_hover {
background-color: $menu_hover;
}
}
And in a handy page template:
<span class="is_hidden" id="jquery_vars">
<span class="is_hidden menu_bg"></span>
<span class="is_hidden menu_hover"></span>
</span>
Finally this allows in a nearby jQuery script:
var menu_bg = $('#jquery_vars .menu_bg').css("background-color");
var menu_hover = $('#jquery_vars .menu_hover').css("background-color");
This is so ugly my dad is wearing a bag on his head.
jQuery can pull arbitrary CSS values from page elements; but those elements have to exist. I did try pulling some of these values from raw CSS without creating the spans in the HTML and jQuery came up with undefined. Obviously, if these variables are assigned to "real" objects on your page, you don't really need the arbitrary #jquery_vars element. At the same time, one might forget that .sidebar-left nice-menu li is the vital element being use to feed variables to jQuery.
If someone has anything else, it's got to be cleaner than this...
sass-ffi should do the trick, but the opposite way (from JS to SASS/SCSS). It will define a function called ffi-require, which allows you to require .js files from SASS:
config.js:
module.exports = {
maxColumns: 4,
};
style.scss:
$max-columns: ffi-require('./config', 'maxColumns');
Works with sass-loader (webpack) and node-sass.
You can read the sass file with a server side script, "parse" it and echo the values you need to javascript.
I would like to add that there are now several ways to share data between Sass and JavaScript using JSON. Here are some links to articles detailing various techniques:
Making Sass talk to JavaScript with JSON
SassyJSON: Talk to the browser
Sharing Data Between Sass and JavaScript with JSON
It's probably just a matter of time until JSON importing becomes supported natively in Sass.
I would recommend looking at sass-extract which uses native sass features in order to extract the computed variable values into JSON.
Also if you are using webpack the sass-extract-loader will make it very easy to just require/import the sass files as in const variables = require('sass-extract-loader!./variables.scss'); and have your sass variables in a nice JSON object.
Since it also supports #import statements you can still separate your variables in different files, and no need to add additional preprocessing or separate json files with variables.
There are many alternative ways of accomplishing this as mentioned in other answers, and which one you choose will depend on your use case and environment.
Disclaimer, I am the author of both mentioned libraries.
Another way could be to use gulp-template so you can generate any structure you want for your JavaScript.
Sharing Variables between Javascript and Sass using Gulp with gulp-template
https://youtu.be/UVeUq8gMYco
It's created from scratch so people could see it from the ground up and there is a git repo with the end result:
https://github.com/PocketNinjaCoUk/shared-js-sass-vars-using-gulp/tree/master/dev
You basically have your config object
saved at ./dev/config.js
module.exports = {
defaults: {
colours: {
primary: '#fc0'
},
sizes: {
small: '100px',
medium: '500px',
large: '1000px'
},
zIndex: {
model: 100,
dropdown: 50,
header: 10
}
}
}
Then you have both of your templates for Sass and Javascript, or less or whatever you want.
Sass underscore template
saved at ./dev/templates/sass-config.txt
<% _.each(defaults, function(category, key) { %>
// Var <%= key %>
<% _.each(category, function(value, key) { %>
$<%= key %>: <%= value %>;
<% }) %>
<% }) %>
Javascript underscore template
saved at ./dev/templates/js-config.txt
namespace.config = {};
<% _.each(defaults, function(monkey, key) { %>
namespace.config.<%= key %> = {
<% i = 1 %>
<% _.each(monkey, function(value, key) { %>
<% comma = (Object.keys(monkey).length === i) ? '': ',' %>
<% if(typeof value === 'string') {%>
<%= key %>: '<%= value %>'<%= comma %>
<%} else { %>
<%= key %> : <%= value %><%= comma %>
<% } %>
<% i++ %>
<% }); %>
};
<% }) %>
Then the gulp to compile it
var gulp = require('gulp');
var template = require('gulp-template');
var rename = require('gulp-rename');
var removeEmptyLines = require('gulp-remove-empty-lines');
var sharedVars = require('./dev/config');
gulp.task('compile', function() {
gulp.src('./dev/templates/sass-config.txt')
.pipe(template(sharedVars))
.pipe(rename('_sass-config.scss'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/sass'));
gulp.src('./dev/templates/js-config.txt')
.pipe(template(sharedVars))
.pipe(rename('js-config.js'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/js'));
});
This can be done using gulp-sass-vars-to-js. It generates a .js file from your .scss file. The .js file contains all variables declared in your .scss file. You can then 'require' this generated js into your .js