Could you please explain me, how do templating engines in JavaScript work? Thank you.
JSON
{ "color" : "red"}
Template
<strong><%=color%></strong>
Result
<strong>Red</strong>
As a starting point I would recommend you to give a look to the String.prototype.replace method and specially using its callback function:
function replaceTokens(str, replacement) {
return str.replace(/<\%=([^%>]+)\%>/g, function (str, match) {
return replacement[match];
});
}
var input = "<strong><%=color%></strong>";
replaceTokens(input, { "color" : "Red"});
// returns <strong>Red</strong>
replaceTokens("<%=var1%> <%=var2%>", { "var1" : "Hello", "var2": "world!"});
// returns "Hello world!"
Give a look to these articles:
Search and Don't Replace
John Resig's Micro-Templating Engine
Better JavaScript Templates (JSP-like syntax)
jQuery Templates Proposal
They may vary by implementation, but the one you're talking about looks like it works by doing the following:
Parse the page looking for keys in <%= %> tags
Match the key to the key/value pair in the JSON
Replace the tags/key with the value.
It's not very different from other templating solutions (at the conceptual level).
{ "color" : "red"}
Specifies a color attribute with the value red.
<strong><%=color%></strong>
Means "Use the value of color wherever I have <%=color%>. Based on wahat you have, the templating-engine probably walks the DOM and finds nodes that have values that match <%=somestring%>. Then, it checks to see if there is an attribute that matches the somestring value. If there is one, it replaces the value of <%=somestring%> with the value defined in the JSON (which, in this case is red).
This finally gives you:
<strong>Red</strong>
Related
I'm trying to create a custom template engine in javascript but I'm having trouble getting started as I cannot extract tokens using regex.
Here are the requirements:
Variables are defined like this: $(variable)
Functions: $(name arg1 "this is arg2 but it contains whitespaces.")
Function arguments can contain other variables $(name $(variable) arg2)
Both variables and functions will be rendered async. For example: Get the value for $(variable) from db then replace it.
This is not for rendering an html page but to simply replace a string entered by a user on the backend.
Edit
More information:
Suppose a user enters the following string:
$(id $(lowercase John))
On the backend application must do:
Convert "John" to lowercase.
Get the id for "john" from db.
This is only a simple example to demonstrate how this should work.
Are there any libraries that can help me achieve this? If not, any idea how to implement this?
EDIT 2:
I tried using Mustache and I changed the delimiters to $(), however the function (section) tags do no satisfy the requirements. In Mustache, for functions I must do this: $(#name) $(variable) "this is arg2 but it contains whitespaces."$(/name) also it does not support async rendering.
If not, any idea how to implement this?
You should use an Abstract Syntax Tree, and write a compatible parser. While regex (as Pedro Lima stated) is good for simple templating, if you ever want to extend the parser, you'll need something a bit more robust.
As an example of an Abstract Syntax Tree parser, $(test1 $(test2) test3) lorem ipsum $(test4) would be turned into the following:
(Thanks to Mile Shang's Syntree for the tree generator.)
As for specifically how to write a parser, I think you can figure it out. Just iterate over the string and check for the template delimiter. Reading the source code for a templating library like Handlebars might help.
Here. This regex will identify the templates that can be replaced. Note that it only selects the innermost templates in nested templates.
/\$\((?<FirstTerm>\S+?)(?<OtherTerms>(?:\s+(?:\w+|".*?"))+)?\)/g
So just use a regex replace function with your templating logic recursively until there are no more matches. The inner templates will be replaced and you'll be left with the string with templates replaced.
Other answers on this post are correct, however, I want to share exactly how I managed to implement this:
Create a recursive match function. I used Steven Leviathan's article to implement this.
Create a render function and inside the function call the recursive match function to find and replace variable/function names with appropriate values.
Keep calling the render function recursively until all arguments inside a function have been replaced.
I am trying to solve a question.
I wrote the following code, and it works fine:
(function () {
if($('#target:contains("bold")')) {
$('#target span:first').css('font-weight','bold');
}
if($('#target:contains("strike")')) {
$('#target span').eq(1).css('text-decoration','line-through');
}
if($('#target:contains("blink")')) {
$('#target span:last').css('text-decoration','blink');
}
}) ();
Their solution is as follows:
(function () {
var target = $('#target');
var html = target.html();
html = html.replace('blink',"$&".blink());
html = html.replace('bold',"$&".bold());
html = html.replace('strike',"$&".strike());
target.html(html);
}) ();
I have the following questions:
I do not understand what is "$&" in "$&".blink(), "$&".bold() and "$&".strike(),
Are blink, bold and strike jQuery UI methods?
These are not jQuery methods. Everything in jQuery is either a function that of the form $.funcName() or a method that operates on a jQuery object, e.g. $(something).methodName().
These are regular Javascript methods of the String class: String.prototype.blink
, String.prototype.bold, String.prototype.strike. They just wrap their argument with the appropriate HTML element. However, they have been deprecated, because CSS is preferred these days rather than the old tags like <blink>, so you shouldn't use them.
"$&" is a feature of the .replace() method, it's automatically replaced with the original matching element. It's mostly useful when using a regular expression, since you don't know the exact string that will match the pattern.
Your solution to the problem doesn't look like it will work properly. It assumes that each word is in its own span, and also that bold is in the first span, strike is in the second span, and blink is in the third span. The objective of the exercise is to find the words wherever they are in the DIV, and put the appropriate style around just that word.
The JavaScript replace() method uses regular expressions. The $& syntax is shorthand for the matched expression; so it will represent blink, bold, and strike respectively.
As #Daniel A. White mentions in his answer, blink(), bold(), and strike() are deprecated methods from string.prototype for wrapping the source string in markup. The exact markup used will vary by browser; for instance, one may return <b>bold</b>, another <strong>bold</strong> and yet another <span style="font-weight: bold">bold</span>.
For what it's worth, while #Barmr's accepted answer is shorter, I find yours more elegant in that it's easy to understand, consistent in its results, and standards-based. That said, #Barmar raises valid concerns regarding the assumptions it makes and, therefore, its usefulness in a general context.
Those are deprecated methods for wrapping a string in a certain element.
String.prototype.blink reveals it to be a native method.
You can view those here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/blink
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/bold
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/strike
I use JQuery to get Json data, but the data it display has double quotes. It there a function to remove it?
$('div#ListingData').text(JSON.stringify(data.data.items[0].links[1].caption))
it returns:
"House"
How can I remove the double quote? Cheers.
Use replace:
var test = "\"House\"";
console.log(test);
console.log(test.replace(/\"/g, ""));
// "House"
// House
Note the g on the end means "global" (replace all).
For niche needs when you know your data like your example ... this works :
JSON.parse(this_is_double_quoted);
JSON.parse("House"); // for example
The stringfy method is not for parsing JSON, it's for turning an object into a JSON string.
The JSON is parsed by jQuery when you load it, you don't need to parse the data to use it. Just use the string in the data:
$('div#ListingData').text(data.data.items[0].links[1].caption);
Someone here suggested using eval() to remove the quotes from a string. Don't do that, that's just begging for code injection.
Another way to do this that I don't see listed here is using:
let message = JSON.stringify(your_json_here); // "Hello World"
console.log(JSON.parse(message)) // Hello World
I also had this question, but in my case I didn't want to use a regex, because my JSON value may contain quotation marks. Hopefully my answer will help others in the future.
I solved this issue by using a standard string slice to remove the first and last characters. This works for me, because I used JSON.stringify() on the textarea that produced it and as a result, I know that I'm always going to have the "s at each end of the string.
In this generalized example, response is the JSON object my AJAX returns, and key is the name of my JSON key.
response.key.slice(1, response.key.length-1)
I used it like this with a regex replace to preserve the line breaks and write the content of that key to a paragraph block in my HTML:
$('#description').html(studyData.description.slice(1, studyData.description.length-1).replace(/\\n/g, '<br/>'));
In this case, $('#description') is the paragraph tag I'm writing to. studyData is my JSON object, and description is my key with a multi-line value.
You can simple try String(); to remove the quotes.
Refer the first example here: https://www.w3schools.com/jsref/jsref_string.asp
Thank me later.
PS: TO MODs: don't mistaken me for digging the dead old question. I faced this issue today and I came across this post while searching for the answer and I'm just posting the answer.
What you are doing is making a JSON string in your example. Either don't use the JSON.stringify() or if you ever do have JSON data coming back and you don't want quotations, Simply use JSON.parse() to remove quotations around JSON responses! Don't use regex, there's no need to.
I dont think there is a need to replace any quotes, this is a perfectly formed JSON string, you just need to convert JSON string into object.This article perfectly explains the situation : Link
Example :
success: function (data) {
// assuming that everything is correct and there is no exception being thrown
// output string {"d":"{"username":"hi","email":"hi#gmail.com","password":"123"}"}
// now we need to remove the double quotes (as it will create problem and
// if double quotes aren't removed then this JSON string is useless)
// The output string : {"d":"{"username":"hi","email":"hi#gmail.com","password":"123"}"}
// The required string : {"d":{username:"hi",email:"hi#gmail.com",password:"123"}"}
// For security reasons the d is added (indicating the return "data")
// so actually we need to convert data.d into series of objects
// Inbuilt function "JSON.Parse" will return streams of objects
// JSON String : "{"username":"hi","email":"hi#gmail.com","password":"123"}"
console.log(data); // output : Object {d="{"username":"hi","email":"hi#gmail.com","password":"123"}"}
console.log(data.d); // output : {"username":"hi","email":"hi#gmail.com","password":"123"} (accessing what's stored in "d")
console.log(data.d[0]); // output : { (just accessing the first element of array of "strings")
var content = JSON.parse(data.d); // output : Object {username:"hi",email:"hi#gmail.com",password:"123"}" (correct)
console.log(content.username); // output : hi
var _name = content.username;
alert(_name); // hi
}
I had similar situation in a Promise just solved doing a cast as (String)
export async function getUserIdByRole(userRole: string): Promise<string> {
const getRole = userData.users.find((element) => element.role === userRole);
return String (getRole?.id);
}
http://jsfiddle.net/DerNalia/zrppg/8/
I have two lines of code that pretty much do the same thing
var doesntbreak = $j("hello");
var breaks = $j(" ");
The first one doesn't error, but the second one throws this
Syntax error, unrecognized expression:
should'nt they both behave the same?
any insight as to how to solve this?
in the actual method I'm using, ele is from the Dom, so it could eb a text node, or any other kind of node.
UPDATE:
the input to the function that I'm using that I noticed this takes selection from the dom.
updated example: http://jsfiddle.net/DerNalia/zrppg/11/ <- includes html markup.
So, I guess, my question is, how do I test if something is JUST a text node? and doesn't contain any markup?
In general, you cannot create standalone text nodes with the jQuery function. If a string isn't obviously HTML, it gets treated as a selector, and is not recognized by jQuery as a valid selector.
Assuming you want to parse arbitrary strings (which may have HTML tags or not), I suggest something like var result = $('<div></div>').html(' ').contents();. Place your your HTML or text string in a div to parse it and then immediately extract the parsed result as a jQuery object with the list of elements. You can append the resultant list of elements with $(parentElem).append(result);
try this:
function isTextNode(node){
div=document.createElement('div');
div.innerHTML=node;
return $(div).text()==$(div).html();
}
And " " is'nt a valid selector if you want to find a elements containing some text you must use the :contains selector http://api.jquery.com/contains-selector/
Internet Explorer (older versions at least) don't have built in "querySelector" functions, so the Sizzle engine has to do the work directly. Thus, the slightly different tolerances for bogus input can cause differences in error reporting.
Your selector expression " " is equally invalid in all browsers, however. The library is not obliged to quietly accept anything you pass it, so perhaps you should reconsider your application design.
If you want to check for entities, you could use a regular expression if you're confident that it's just a text node. Or you could get the contents with .text() instead of .html().
So, I have to thank Apsillers and Rolando for pointing me in the right direction. Their answers were very close, but gave me the information I needed.
This is what I ended up using:
TEXT_NODE = 3;
objectify = function(n) {
return $j("<div></div>").html(n).contents();
}
function textOnly(n) {
var o = objectify(n);
for (var i = 0; i < o.length; i++) {
if (objectify(o[i])[0].nodeType != TEXT_NODE) {
return false
}
}
return true;
}
And here is a jsFiddle with some test cases, that neither of the original code submissions passed.
to pass, it needed to handle this kind of input
"hello" // true
"hello<b>there</b>" // false
"<b>there</b>" // false
" " // false
Not actual answer, but may help someone with similar issue as mine and loosely related to this question. :)
I was getting same issue today, so fixed by removing
Changed:
var breaks = $j(" ");
to:
var breaks = $j(" ".replace(/&.*;/g, ""));
Here I am removing , < etc...
Note: value at is dynamic for me, so it can be anything.
var allRapidSpells = $$('input[value^=RSW]');
Can anyone tell me what that does?
I would venture to guess that you're using MooTools, a JavaScript framework. The $$() function is used to select an element (or multiple elements) in the DOM.
More specifically, the $$('input[value^=RSW]'); syntax is selecting all input elements whose value attribute starts with RSW.
Other attribute selectors include:
= : is equal to
*= : contains
^= : starts-with
$= : ends-with
!= : is not equal to
~= : contained in a space separated list
|= : contained in a '-' separated list
Edit: It looks as though Prototype, another JavaScript framework, uses the same syntax.
Return all inputs that hava value starting with RSW
It calls the function named '$$' with the parameter 'input[value...' and assigns the returnvalue of that function to the var allRapidSpells.
Javascript doesn't consider the '$' to be a reserved character, which jQuery makes excellent use of.
It looks like it uses some CSS selectors using some javascript library, the CSS selectors return all input tags where the value begins RSW.
calls a method on the windows object called $$ and passes a string argument to it, which appears to be an xpath expression.
which returns input tags that contain an attribute called value starting with RSW.