Render HTML file content and assign it to an Object - javascript

This is what my initial js looks like
// template.js
file: {
template : '<div><h2>Template</h2><p>My paragraph</p></div>';
}
But I need to create this as a separate html file.
<!-- template.html -->
<div>
<h2>Template</h2>
<p>My paragraph</p>
</div>
Now I need to import and assign it to an object (template) of a script. Any ideas how to do this?
// template.js
file: {
template : 'Content Here';
}

You will have to get it via AJAX. This will be a pseudo-promise (see: Promise and $.get()) that is resolved with the contents of the file:
$.get(templateUrl).then(...)
So a good example would be:
// assuming `file` is inside `options`
if (options.file.template == null && options.file.templateUrl != null) {
$.get(options.file.templateUrl).then(function(html) {
options.file.template = html
// continue doing stuff with the newly received html...
});
}
Notice how I separated them into template and templateUrl, but you don't have to do that.
Note: if you try to use to use the result outside the promise's callback, you will probably get undefined when you try to access it (unless some time has passed) so you might need to organize your code to accommodate to that.

Related

Use [hash:base64:5] in JavaScript / TypeScript file

I am using CSS Modules in an Angular Project with Webpack.
I had already transformed the class names in .css and .scss files with postcss-modules.
Then with posthtml-css-modules I had changed the values on the class property in html elements for his hash value defined by postcss-modules.
I can say that everything is working fine 💪.
Now, I have a new challenge to resolve.
Angular, has a feature that allows you to change dynamically the value of class in a html element depending on a condition:
https://angular.io/api/common/NgClass
For Example, I can do:
<div [className]="myVar ? 'myClass1' : 'myClass2' " >
If myVar = true, the html element will be:
<div class="myClass1" >
And if myVar = false, the html element will be:
<div class="myClass2" >
Like I do not know what is going to be the value of myVar during compilation time (because the value of myVar depends on user actions) I am not able to set the value for <div css-module="myClass1" > or <div css-module="myClass2" > in order to hash the class names of myClass1 or myClass2.
BUT (Here comes my solution)...
If I can invoke the same function that does [hash:base64:5] (https://github.com/css-modules/postcss-modules#generating-scoped-names)
I can create a function that receives a string as parameter and return the class name as a hash.
It would be something like this:
<div [className]="setMyClassNameInHash(myVar)">
Then in javascript:
function setMyClassNameInHash(condition) {
return condition ? doHash64('myClass1') : doHash64('myClass1');
}
doHash64() would be the function that takes a string and returns the hash using [hash:base64:5].
In conclusion, my question is:
¿How I can invoke the same function that does [hash:base64:5] in a javascript file?
Thanks!!!!
You'll need to integrate a templating step into your build process. The plugin exports the class names and their mapped names to a json file, so you can look up the hashed class name from the original.
Edit: Since the built in templating only works for a single class name and doesn't appear to support replacing class names in things like angular attributes, you could do the templating yourself using a templating library like lodash. If you're already using grunt or gulp, I'd recommend using their template tasks instead of this manual way because they do a lot of things for you, like supporting multiple files.
In your html, you would use lodash delimiters to get the hashed class name like:
<div [className]="myVar
? '<%= getHashedClass('myClass1') %>'
: '<%= getHashedClass('myClass2') %>' " >
Your node build script might look like this:
var fs = require('fs');
postcss([
require("postcss-modules")({
// Callback to get hashed class names
getJSON: function(cssFileName, classNames, outputFileName) {
var filePath = '/path/to/outputDir/file.html';
// Function to lookup hashed class names
var getHashedClass = function(unhashedClass) {
return classNames[unhashedClass];
}
// Read your html file as a string
var html = fs.readFileSync(path),
// Use lodash to template it, passing the class lookup function
var compiled = _.template(html);
var templated = compiled({
getHashedClass: getHashedClass
});
// Write the templated html
fs.writeFileSync(path, templated);
}
})
]);

NODE.JS accessing functions within an EJS template

I have a scripts.js file that includes a function inside that i want to access within an EJS templates.
in the templates i included a header, in the header I added a script rel to scripts.js
<script src="/scripts/scripts.js" type="text/javascript"></script>
I tested it though console.log("test") and when the template is being called I see "test" appears in the console.
when I try to call an actual function (verify_data) within the EJS template i get an error verify_data is not defined.
the error code within the EJS looks like this:
<p><em>Submited By <%= verify_data(results.user.username) %></em></p>
the function check if the argument passed is null/undefined, if yes the data returns if not "n/a" string returns instead.
How do i access JS functions directly within EJS template ?
Thanks,
Assuming you're not using a SPA framework, you need to add a tag which will be either replaced by the function or to contain the data you want to store.
Look at this code snippet
<p><em>Submited By <span id='userData'></span></em></p>
<script>
//This is the script.js
function verify_data(userName) {
return `My data with '${userName}'`;
}
</script>
<script>
let data = verify_data("EleFromStack"); // assuming <%=results.user.username%> = "EleFromStack"
document.querySelector("#userData").textContent = data;
</script>

Flask -- instantiating an object from external doesn't seem to do anything

I'm just starting with Flask, so I may be overlooking something very obvious. I have loaded my javscript file with this:
<script src="{{ url_for('static', filename='Page.js') }}"></script>
then I try to instantiate an object from that js file:
<script>
var page = new Page("index");
</script>
In Page.js, I have this:
var Page = function(page) {
alert("init");
<some other things>
}
<and then some object methods Page.prototype.init_standard = function() {} etc>
The alert isn't alerting, though I am expecting it to. Also, if I put an alert before the instantiation in the HTML page, I get the alert, if I put an alert on the line after the instantiation on the HTML page, I don't get the alert. I'm not sure if this is a Flask issue or javascript -- I'm quite new at both.
EDIT: To nip this possibility in the bud, the javascript file is getting loaded according to the development server, status 304
Page.js needs to just define a function that creates the object you want. So Page() should just be a function that ends with 'return this;'. Correction returning this is not strictly required for object creation.
function Page(page) {
alert("init");
... Your code and methods...
// Example Method:
this.foo = function(param) {
... function body ...
};
return this;
}
And then you can create an object with:
var page_object = new Page("index");
page_object.foo(some_data);
I figured it out after looking at the browser debugger. It said Page was not defined. After some trial and error, I realized I can't use {{ flask templating notation inside the javascript file, probably because it isn't loaded as a template so no parsing of it in that way occurs. Anyway, I ended up just moving the {{ }} link to the html template and passing it in as an argument to the javascript.

How to cache mustache templates?

I would like to cache mustache templates.
I know that I could include mustache templates directly, like this:
<script id="mustache-template" type="text/html">
<h1>{{title}}</h1>
</script>
And call those with javascript, like this:
var html, template, data;
data = {
title : "Some title"
};
template = document.getElementById('mustache-template').innerHTML;
html = Mustache.to_html(template, data);
This won't cache templates. Only way I could figure out is usage of link -tags, but how do I call template content via javascript without an ajax request?
This won't work (of course)...
HTML
<link type="text/html" href="/mustache/template.tpl" id="mustache-template" />
Javascript
document.getElementById('mustache-template').innerHTML;
This question is very interesting! I had the same problem several months ago when I started to use mustache for 'huge' front-end templating within a rails project.
I ended up with the following solution...
Mustache templates are inside a public folder :
/public/templates/_template_name.tpl
Whenever I need a template I have this helper getTemplate that does some stuff (there's some mootools, but there are comments too):
// namespace.templatesCache is an object ( {} ) defined inside the main app js file
var
needXHR = false, // for callback function
templateHTML = ""; //template html
if(!(templateHTML = namespace.templatesCache[template_name])){ //if template is not cached
templateHTML = (this.helpers.supportLocalStorage) ? localStorage.getItem(template_name) : ""; //if browser supports local storage, check if I can retrieve it
if(templateHTML === "" || templateHTML === null){ // if I don't have a template (usually, first time), retrieve it by ajax
needXHR = true;
new Request.HTML({ //or jQuery's $.get( url /*, etc */ )
url: namespace.URLS.BASE+"templates/_"+template_name+".tpl", // url of the template file
onSuccess : function(t, e, html, js){
namespace.templatesCache[template_name] = html; //cache it
if(_this.helpers.supportLocalStorage){ //and store it inside local storage, if available
localStorage.setItem(template_name,html);
}
//call callback
}
}).get();
}else{ //retrieved by localStorage, let's cache it
namespace.templatesCache[template_name] = templateHTML;
}
}
if(!needXHR){ // I retrieved template by cache/localstorage, not by Ajax
//call callback
}
and I call this helper in this way :
namespace.helpers.getTemplate('template_name', function( templateHTML ){
// the callback function
});
You can notice that first time user needs the template, there's an asynch request (you could make a sync request if u don't want to wrap some other code inside the callback)
I hope it could help and I'd love to receive feedbacks/suggestions concerning this stuff :)
You could try to load your template in an iframe that contains an HTML page(that will be cached) with all your script tags inside.
Then you can read them from the main page, or push them from the iframe to the parent window.
That is what I do when using pure.js templates
What you say it will not work, of course, because the innerHTML attribute of the liknek element will not give you the contents of the link.
You can use Chevron to load external templates from links like so:
You add in you template a link to your template file:
<link href="path/to/template.mustache" rel="template" id="templateName"/>
Then, in you JS you can render your template like so:
$("#templateName").Chevron("render", {name: "Slim Shady"}, function(result){
// do something with 'result'
// 'result' will contain the result of rendering the template
// (in this case 'result' will contain: My name is Slim Shady)
});
The docs of Chevron will give more examples

How do I pass argument to anonymous Javascript function?

I am writing a simple counter, and I would like to make installation of this counter very simple for users. One of the simplest counter code (for users who install it) I ever see was Google Analytics Code
So I would like to store main code in a file and user who will install my counter will need just to set websiteID like this:
<html><head><title></title></head><body>
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var websiteId = 'XXXXX';
</script>
</body></html>
Here is my code:
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var page = _gat.init('new');
</script>
and this is my JS file:
(function() {
var z = '_gat';
var aa = function init(data) { alert(data); alert(z);};
function na() {
return new z.aa();
}
na();
})();
I tried to understand Google Analytics javascript code but I failed to do this. Can anyone suggest how can I specify variable between tags and then read it in anonymous function which is located in a javascript file ?
Thanks.
In your example, websiteId is a global variable. So it is accessible everywhere including anonymous functions unless there is a local variable with the same name
<script> var websiteId = "something"; </script>
Later in the page or included js file...
(function() {
alert(websiteId); //this should work
})();
Can anyone suggest how can I specify variable between tags and then read it [...]
Not if your tag has both a SRC attribute and JS content.
<script type="text/javascript" src="http:/x.com/x.js"></script>
.. is different from,
<script type="text/javascript">
var x = 1;
</script>
One framework that optionally adds JS variables to SCRIPT tags is Dojo. So if you're using Dojo you can add variables to the global djConfig hash by writing,
<script type="text/javascript" src="mxclientsystem/dojo/dojo.js"
djConfig="
usePlainJson: true,
parseOnLoad: true
">
</script>
Dojo does this by running through the SCRIPT tags and evaluating the custom djConfig attribute.
This does not, however solve your problem.
You do really want two SCRIPT tags. One saying,
<script type="text/javascript">
var websiteId = '123456';
</script>
which will set a global variable websiteId and a second one,
<script type="text/javascript" src="http:/x.com/myreporter.js"></script>
which can load from anywhere and read out the websiteId variable and, I assume, report it back.
You can pass variables to an anonymous function like so:
(function(arg1, arg2, arg3) {
alert(arg1);
alert(arg2);
alert(arg3);
})("let's", "go", "redsox");
// will alert "let's", then "go", then "redsox" :)
I'm not entirely clear about what you're asking, but...
You can tag any HTML element with an id attribute, then use
document.getEntityById() to retrieve that specific element.
You can also give any HTML element user-defined attributes having names of your own choosing, then get and set them for that element within Javascript.
I think you've got a bit confused with how JS objects are called.
z is a String, '_gat'. You can't call aa() on it because a String has no member called aa. aa is a standalone function stored in a local variable. Even if you did call aa(), it doesn't return anything, so using the new operator on its results is meaningless. new can only be called on constructor-functions.
I guess you mean something like:
var _gat= function() {
// Private variable
//
var data= null;
// Object to put in window._gat
//
return {
// Set the private variable
//
init: function(d) {
data= d;
}
};
}();
Then calling _gat.init('foo') as in your second example would set the variable to website ID 'foo'. This works because the _gat object is the return {init: function() {...}} object defined inside the anonymous function, keeping a reference (a ‘closure’) on the hidden data variable.
If you specify a src attribute as part of a script element, any code within the script element tags themselves will not be executed. However, you can add this functionality with the following code. I got this technique from Crockford (I believe it was him), where he uses it in of his talks on the unrelated topic of rendering performance and asynchronously loading scripts into a page to that end.
JavaScript:
(function() {
// Using inner class example from bobince's answer
var _gat = (function() {
var data= null;
return {
init: function(d) {
console.info("Configuration data: ", d);
data = d;
}
}
})();
// Method 1: Extract configuration by ID (SEE FOOT NOTE)
var config = document.getElementById("my-counter-apps-unique-and-long-to-avoid-collision-id").innerHTML;
// Method 2: search all script tags for the script with the expected name
var scripts = document.getElementsByTagName("script");
for ( var i=0, l=scripts.length; i<l; ++i ) {
if ( scripts[i].src = "some-script.js" ) {
config = scripts[i].innerHTML;
break;
}
}
_gat.init( eval("(" +config+ ")") );
})();
HTML:
<script type="text/javascript" src="some-script.js" id="my-counter-apps-unique-and-long-to-avoid-collision-id">
{some: "foo", config: "bar", settings: 123}
</script>
Both methods have their draw backs:
Using a unique and non-colliding ID will make determining the proper script element more precise and faster; however, this is not valid HTML4/XHTML markup. In HTML5, you can define arbitrary attributes, so it wont be an issue at that time
This method is valid HTML markup; however, the simple comparison that I have shown can be easily broken if your url is subject to change (e.g.: http vs https) and a more robust comparison method may be in order
A note on eval
Both methods make use of eval. The typical mantra concerning this feature is that "eval is evil." However, that goes with say that using eval without knowing the dangers of eval is evil.
In this case, AFAIK, the data contained within the script tags is not subject to inject attack since the eval'ing script (the code shown) is executed as soon as that element is reached when parsing the HTML into the DOM. Scripts that may have been defined previously are unable to access the data contained within the counter's script tags as that node does not exist in the DOM tree at the point when they are executed.
It may be the case that a well timed setTimeout executed from a previously included script may be able to run at the time between the counter's script's inclusion and the time of the eval; however, this may or may not be the case, and if possible, may not be so consistently depending on CPU load, etc.
Moral of the story, if you're worried about it, include a non-eval'ing JSON parser and use that instead.

Categories