I know in PHP we can do something like this:
$hello = "foo";
$my_string = "I pity the $hello";
Output: "I pity the foo"
I was wondering if this same thing is possible in JavaScript as well. Using variables inside strings without using concatenation — it looks more concise and elegant to write.
You can take advantage of Template Literals and use this syntax:
`String text ${expression}`
Template literals are enclosed by the back-tick (` `) (grave accent) instead of double or single quotes.
This feature has been introduced in ES2015 (ES6).
Example
var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b}.`);
// "Fifteen is 15.
How neat is that?
Bonus:
It also allows for multi-line strings in javascript without escaping, which is great for templates:
return `
<div class="${foo}">
...
</div>
`;
Browser support:
As this syntax is not supported by older browsers (mostly Internet Explorer), you may want to use Babel/Webpack to transpile your code into ES5 to ensure it will run everywhere.
Side note:
Starting from IE8+ you can use basic string formatting inside console.log:
console.log('%s is %d.', 'Fifteen', 15);
// Fifteen is 15.
Prior to Firefox 34 / Chrome 41 / Safari 9 / Microsoft Edge, nope, that was not possible in javascript. You would have to resort to:
var hello = "foo";
var my_string = "I pity the " + hello;
Prior to Firefox 34 / Chrome 41 / Safari 9 / Microsoft Edge, no. Although you could try sprintf for JavaScript to get halfway there:
var hello = "foo";
var my_string = sprintf("I pity the %s", hello);
well you could do this, but it's not esp general
'I pity the $fool'.replace('$fool', 'fool')
You could easily write a function that does this intelligently if you really needed to
Complete and ready to be used answer for <ES6:
var Strings = {
create : (function() {
var regexp = /{([^{]+)}/g;
return function(str, o) {
return str.replace(regexp, function(ignore, key){
return (key = o[key]) == null ? '' : key;
});
}
})()
};
Call as
Strings.create("My firstname is {first}, my last name is {last}", {first:'Neo', last:'Andersson'});
To attach it to String.prototype:
String.prototype.create = function(o) {
return Strings.create(this, o);
}
Then use as :
"My firstname is ${first}".create({first:'Neo'});
If you are on >ES6 then you can also do:
let first = 'Neo';
`My firstname is ${first}`;
2022 update: Just use the ES6 Template Literals feature. It's fully supported in practically every browser you'll encounter these days. If you are still targeting browsers like IE11 and lower .. well, my heart goes out to you. The below solution I came up with 5 years ago will still work for you. Also, email me if you want a job that doesn't involve catering to old browsers 👍.
You can use this javascript function to do this sort of templating. No need to include an entire library.
function createStringFromTemplate(template, variables) {
return template.replace(new RegExp("\{([^\{]+)\}", "g"), function(_unused, varName){
return variables[varName];
});
}
createStringFromTemplate(
"I would like to receive email updates from {list_name} {var1} {var2} {var3}.",
{
list_name : "this store",
var1 : "FOO",
var2 : "BAR",
var3 : "BAZ"
}
);
Output: "I would like to receive email updates from this store FOO BAR BAZ."
Using a function as an argument to the String.replace() function was part of the ECMAScript v3 spec. See this SO answer for more details.
If you like to write CoffeeScript you could do:
hello = "foo"
my_string = "I pity the #{hello}"
CoffeeScript actually IS javascript, but with a much better syntax.
For an overview of CoffeeScript check this beginner's guide.
I would use the back-tick ``.
let name1 = 'Geoffrey';
let msg1 = `Hello ${name1}`;
console.log(msg1); // 'Hello Geoffrey'
But if you don't know name1 when you create msg1.
For exemple if msg1 came from an API.
You can use :
let name2 = 'Geoffrey';
let msg2 = 'Hello ${name2}';
console.log(msg2); // 'Hello ${name2}'
const regexp = /\${([^{]+)}/g;
let result = msg2.replace(regexp, function(ignore, key){
return eval(key);
});
console.log(result); // 'Hello Geoffrey'
It will replace ${name2} with his value.
I wrote this npm package stringinject https://www.npmjs.com/package/stringinject which allows you to do the following
var string = stringInject("this is a {0} string for {1}", ["test", "stringInject"]);
which will replace the {0} and {1} with the array items and return the following string
"this is a test string for stringInject"
or you could replace placeholders with object keys and values like so:
var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });
"My username is tjcafferkey on Github"
If you're trying to do interpolation for microtemplating, I like Mustache.js for that purpose.
Don't see any external libraries mentioned here, but Lodash has _.template(),
https://lodash.com/docs/4.17.10#template
If you're already making use of the library it's worth checking out, and if you're not making use of Lodash you can always cherry pick methods from npm npm install lodash.template so you can cut down overhead.
Simplest form -
var compiled = _.template('hello <%= user %>!');
compiled({ 'user': 'fred' });
// => 'hello fred!'
There are a bunch of configuration options also -
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
var compiled = _.template('hello {{ user }}!');
compiled({ 'user': 'mustache' });
// => 'hello mustache!'
I found custom delimiters most interesting.
Simply use:
var util = require('util');
var value = 15;
var s = util.format("The variable value is: %s", value)
Create a method similar to String.format() of Java
StringJoin=(s, r=[])=>{
r.map((v,i)=>{
s = s.replace('%'+(i+1),v)
})
return s
}
use
console.log(StringJoin('I can %1 a %2',['create','method'])) //output: 'I can create a method'
Peace quote of 2020:
Console.WriteLine("I {0} JavaScript!", ">:D<");
console.log(`I ${'>:D<'} C#`)
Maybe
wrt=(s, arr=[])=>{
arr.map((v,i)=>{s = s.replace(/\?/,v);});
return s;
};
a='first var';
b='secondvar';
tr=wrt('<tr><td>?<td></td>?</td><tr>',[a,b]);
console.log(tr);
//or use tr in html(tr), append(tr) so on and so forth
// Use ? with care in s
String.prototype.interpole = function () {
var c=0, txt=this;
while (txt.search(/{var}/g) > 0){
txt = txt.replace(/{var}/, arguments[c]);
c++;
}
return txt;
}
Uso:
var hello = "foo";
var my_string = "I pity the {var}".interpole(hello);
//resultado "I pity the foo"
var hello = "foo";
var my_string ="I pity the";
console.log(my_string, hello)
I have a comma separated value list with several lines, and want to create a table with four columns using jquery. I have created a jquery function, but I am unable to reproduce the
$(function(){
$('div').prepend('<table><thead><tr><th>Author</th><th>Title</th><th>Year</th><th>Status</th></tr></thead><tbody>');
$('div').html(function(){
var text = $(this).text();
var array = text.split(',');
var array2 = array[0].split('-');
var html = '<tr><td>'+ array2[0] + '</td><td>'+ array2[1] + '</td><td>' + array[1] + '</td><td>' + array[2] + '</td></tr>';
$(this).html(html);
})
$('div').append('</tbody></table>');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
author - book,2010,good
author2 - book2,2011,good
author3 - book3,2011,bad
author4 - book4,2012,average
author5 - book5,2009,bad
author6 - book6,2008,good
</div>
The expected output:
<table><thead><tr><th>Author</th><th>Title</th><th>Year</th><th>Status</th></tr></thead>
<tbody>
<tr><td>author</td><td>book</td><td>2010</td><td>good</td></tr>
<tr><td>author2</td><td>book2</td><td>2011</td><td>good</td></tr>
<tr><td>author3</td><td>book3</td><td>2011</td><td>bad</td></tr>
<tr><td>author4</td><td>book4</td><td>2012</td><td>average</td></tr>
<tr><td>author5</td><td>book5</td><td>2009</td><td>bad</td></tr>
<tr><td>author6</td><td>book6</td><td>2008</td><td>good</td></tr>
</tbody>
</table>
The Jquery code doesn't work as I expect, I am stuck with this.
Thank you!
Break this down into smaller tasks by first getting the data processed and then once you have that figured out build the html from there.
You are missing a key step in splitting the line breaks to get your initial rows strings using text.split('\n').
Once you get those rows then map each row to split the other characters to get a sub array of strings for each row. Since none of this processing requires using elements you can do it in a sandbox until you get to where you can take string input and log a final array to console.
Once you have a data array then build the table rows. Note in your append() process you can't append closing tags as you would in a code editor. When you append html into the dom it creates full elements so include all closing tags in that html string
Working example:
const $div = $('#myDiv')
// include the closing tags in the html string
const $table = $('<table><thead><tr><th>Author</th><th>Title</th><th>Year</th><th>Status</th></tr></thead><tbody></tbody></table>');
let txt = $div.text().trim();// trim to remove whitespace at each end
// split line breaks for rows array
let rows = txt.split('\n').map(s => {
// split the two parts
const [author, content] = s.split(' - ');
// return final sub array
return [author, ...content.split(',')]
});
// inspect your work so far
console.log('rows array',JSON.stringify(rows))
// now that dat works, insert into table
rows.forEach(row =>{
var $row = $('<tr>');
row.forEach(s=> $row.append($('<td>',{text:s})));
$table.find('tbody').append($row)
})
// finally insert the table
$('div').html($table)
td, th{ border: 1px solid #ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myDiv">
author - book,2010,good
author2 - book2,2011,good
author3 - book3,2011,bad
author4 - book4,2012,average
author5 - book5,2009,bad
author6 - book6,2008,good
</div>
Still learning Angular however I need a quick fix for something and I can't figure it out. I have a site that gives you the USPS shipping options and need to tell the website to use my text instead of the vendor's. The vendor's says '2-Day Ship™' but I want to replace that with just 'Standard Ground'.
This is what I have for my filter:
app.filter('shipFilter',function() {
return function(string) {
if (string) {
return string.replace('2-Day Ship™', 'Standard Ground');
}
}
And in my html:
<select class="form-control" ng-change="updateShipper()" name="shipMethod"
ng-model="currentOrder.LineItems[0].ShipperName"
ng-show="user.ShipMethod.ShipperSelectionType == 'UserDropDown'"
ng-options="shipper.Name | shipFilter as (shipper.Name + ' ' + (shipper.ShippingRate.Price | currency | xlat)) for shipper in shippers"
ng-required="!currentOrder.IsMultipleShip() && user.ShipMethod != null" />
<option value=""></option></select>
It seems like it would be an easy patch however I'm running into walls and out of time.
Cheers
It's been a while but I wanted to post what I did to fix. I had to do this:
filter.js
//For changing USPS shipping option in dropdown
four51.app.filter('USPSFilter',function() {
debugger;
return function(object) {
if (object) {
return object.replace(/USPS Priority Mail 2-Day™/g, 'US Postal Ground');
}
}
});
Added this to html to display change in dropdown:
ng-options="shipper.Name as ((shipper.Name | USPSFilter) + ' ' + (shipper.ShippingRate.Price | currency | xlat)) for shipper in shippers"
Added this to my controller.js:
$scope.items = [{name: 'Ground' },{ name: 'Rush' }]
I'm on the same page at #MatthewGreen here. You have an array in your scope, so you can just modify the array in scope. However if that absolutely isn't possible I wrote a snippet that correctly changes the string name.
strs = [
"2-Day Ship™",
"Next Day"
]
var filter = function(string) {
if (string) {
return string.replace(/2-Day Ship™/g, 'Standard Ground');
}
}
console.log(strs)
strs = strs.map(filter)
console.log(strs)
I am trying to prepare sentence using html tags, but it fails to render HTML, instead it display with escape characters.
<p>
{{ activity | prepareSentence }}
</p>
Created custom filter to use in polymer template.
prepareSentence: function(activity) {
var sentence = [];
if (1) {
sentence.push('<a href="/user/'
+ activity.from_user.entity_id + '/'
+ activity.from_user.name + '">You</a>');
sentence.push(' are following ');
sentence.push('<a href="/user/'
+ activity.to_entity.entity_id + '/'
+ activity.to_entity.name + '">'
+ activity.to_entity.name + '</a>');
}
return sentence.join(' ');
}
Current Output:
name1 are following name2
Expected Output:
[You][1] are following [name2][1]
The TemplateBinding subsystem contains an HTML filter to protect developers against XSS attacks. Therefore, inserting HTML (as opposed to plain text) into DOM has to be done manually.
For example:
<p id="sentence"></p>
...
activityChanged: function(old, activity) {
// build html
this.$.sentence.innerHTML = html;
}
You now have a vulnerability, so make sure you screen the source data.
I'm working on an HTML page highlighter project but ran into problems when a search term is a name of an HTML tag metadata or a class/ID name; eg if search terms are "media OR class OR content" then my find and replace would do this:
<link href="/css/DocHighlighter.css" <span style='background-color:yellow;font-weight:bold;'>media</span>="all" rel="stylesheet" type="text/css">
<div <span style='background-color:yellow;font-weight:bold;'>class</span>="container">
I'm using Lucene for highlighting and my current code (sort of):
InputStreamReader xmlReader = new INputStreamReader(xmlConn.getInputStream(), "UTF-8");
if (searchTerms!=null && searchTerms!="") {
QueryScorer qryScore = new QueryScorer(qp.parse(searchTerms));
Highlighter hl = new Highlighter(new SimpleHTMLFormatter(hlStart, hlEnd), qryScore);
}
if (xmlReader!=null) {
BufferedReader br = new BufferedReader(xmlReader);
String inputLine;
while((inputLine = br.readLine())!=null) {
String tmp = inputLine.trim();
StringReader strReader = new stringReader(tmp);
HTMLStripCharFilter htm = HTMLStripCharFilter(strReader.markSupported() ? strReader : new BufferedReader(strReader));
String tHL = hl.getBestFragment(analyzer, "", htm);
tmp = (tHL==null ? tmp : tHL);
}
xmlDoc+=tmp;
}
bufferedReader.close()
As you can see (if you understand Lucene highlighting) this does an indiscriminate find/replace. Since my document will be HTML and the search terms are dictated by users there is no way for me to parse on certain elements or tags. Also, since the find/replace basically loops and appends the HTML to a string (the return type of the method) I have to keep all HTML tags and values in place and order. I've tried using Jsoup to loop through the page but handles the HTML tag as one big result. I also tried tag soup to remove the broken HTML caused by the problem but it doesn't work correctly. Does anyone know how to basically loop though the elements and node (data value) of html?
I've been having the most luck with this
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" enconding=\"UTF-8\"?><!DOCTYPE html>");
Document doc = Jsoup.parse(txt.getResult());
Element elements = doc.getAllElements();
for (Element e : elements) {
if (!(e.tagName().equalsIgnoreCase("#root"))) {
sb.append("<" + e.tagName() + e.attributes() + ">" + e.ownText() + "\n");
}// end if
}// end for
return sb;
The one snag I still get is the nesting isn't always "repaired" properly but still semi close. I'm working more on this.