I'd like to load a handlebars partial, but only when the URL is '/foo', and load a different partial for '/bar'.
I know that this is always going to be a bit of a hack, and not really intended - but use to the project handlebars structure, it seems the most logical way of solving the problem I'm running into.
Thanks
assuming
jquery is included
your template files are in the same folder
your template files have the names "foo.bs" & "bar.hbs"
code
function loadTemplate(templateName) {
$.get(templateName + '.hbs', function (data) {
var template = Handlebars.compile(data);
$(body).html(template(jsonData));
}, 'html');
}
if (window.location.pathname.search('bar') !== -1) {
loadTemplate('bar');
}
if (window.location.pathname.search('foo !== -1) {
loadTemplate('foo');
}
Related
I'm trying to load images dynamically which may or may not exist.
In this case, crypto-currency icons using their 3 letter symbol. I have a few hundred in .svg format in my statics library and when I pull data from a price server I try to match up the icons I have with the symbols coming from the server and to serve a fallback image if I don't have the asset.
In my index.vue I can get away with this code and everything works fine:
<img :src="'statics/icons/svg/' + coin.symbol + '.svg'" v-img-fallback="'statics/icons/svg/fallback.svg'"/>
However in a subcomponent that opens if a user clicks a coin the same code will fail to load both the primary and fallback images. I've tried numerous ways but the only way to get an image to load from my subcomponent is to either hard code it like this:
<img src="statics/icons/svg/btc.svg"/>
Which is impossible for me as I need the modal to be dynamically generated for any possible coin...
Or using require() like this:
<img :src="imageSrc" v-img-fallback="require('../statics/icons/svg/fallback.svg')"/>
// Computed:
imageSrc () {
if (this.coinData.symbol) {
return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
}
}
However this crashes my app if require() looks for an asset that doesn't exist. I need a method that fails gracefully so that the v-img-fallback can detect it and supply the fallback.
I've tried doing something like return require(image1) || require(fallback) but it doesn't work.
This is a common request and latest WebPack, AFAIK (and I just searched for it again), does not expose an API for especifically testing the existence of a module.
In other words, you'd have to handle the uncertainty of the loading yourself. Example:
computed: {
imageSrc () {
if (this.coinData.symbol) {
try {
return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
} catch (e) {
if (e.name !== "ModuleNotFoundError") throw e; // handle false-positives
// in cordova, use the line below instead of the above
// if (!e.message.startsWith('Cannot find module')) throw e;
return require('../statics/icons/svg/fallback.svg');
}
}
return require('../statics/icons/svg/fallback.svg');
}
}
This way I'd argue you wouldn't even need a fallback src in the template. You could return it in the computed property itself.
The following code loads templates.
tpl = {
// Hash of preloaded templates for the app
templates: {},
// Recursively pre-load all the templates for the app.
// This implementation should be changed in a production environment. All the template files should be
// concatenated in a single file.
loadTemplates: function(names, callback) {
var that = this;
var loadTemplate = function(index) {
var name = names[index];
console.log('Loading template: ' + name);
$.get('tpl/' + name + '.html', function(data) {
that.templates[name] = data;
index++;
if (index < names.length) {
loadTemplate(index);
} else {
callback();
}
});
}
loadTemplate(0);
},
// Get template by name from hash of preloaded templates
get: function(name) {
return this.templates[name];
}
};
and u can load templates like
tpl.loadTemplates(['header', 'wine-details', 'wine-list-item'], function() {
});
But here the templates are defined in seperate html file.
Is it possible all my templates are defined in one html file and i load them all together.
Do i need to write some kind of parser. The reason is for my home page i have 40 templates and I dont want to create 40 Html template pages instead it would be i can keep them in one single html page and than load them all together.
Is this possible?
Yes you don't need to be doing it like this.
Check this: https://github.com/requirejs/text
define([
'some/module',
'text!some/module.html',
'text!some/module.css'
], function (SomeModule, HtmlFile, CssFile) {
'use strict';
// ....
}
This can load text resources. When you are ready to deploy your app you can compile all your js files and templates into a single minified file using r.js. You can do this via command line, or as a grunt or gulp task.
There are many template libraries which you can find requirejs plugins for (often extended off that text plugin ^^). For example this one for Handlebars https://github.com/jfparadis/requirejs-handlebars or this one for underscore https://github.com/jfparadis/requirejs-tpl. Just google search "requirejs «template engine name»".
If you want to have multiple templates in one html file you need a way to separate them. The nicest way to do this is using the new template tag.
You can, define templates single file, under different container with Ids. load single html the way you are loading here, create a jquery node with template html, run through children read id attribute and save html of each child saved as that.templates[id_you_read] = child.html();
But this is not the right way of doing stuff. use requirejs with text plugin as in the prev answer.
my first question here - please go easy. I'm using express, express3-handlebars and i18next-node with node.js
The plan is to work with a different translation namespace depending on which view (i.e. which handlebars file) is currently being served. So if we're looking at the page called ie(.hbs), i18next will look in the namespace called ie(.json) for the relevant language. This makes organisation and coordination of translations easier.
This is how I'm currently doing it: first I send the current page into the handlebars template for rendering (even this seems unnecessary - handlebars doesn't automatically expose which file it's rendering?):
res.render( url_base_path, { layout: ("sub"), title: title, currentpage: url_base_path } );
and then I access the variable "greeting" to be translated in the namespace of the current page like so {{t "greeting" page=currentpage }} - the annoying thing is that there are 10's of these variables on each page. Don't Repeat Yourself, anybody?
't' is defined in the express3-handlebars create() function, like so, helpers: { t: t }
and the translate function looks like this
var t = function (i18next_key, options) {
var page, result;
page = options.hash.page;
result = i18next.t(page + ":" + i18next_key);
return new hbs.handlebars.SafeString(result);
};
for the sake of full disclosure, this is what my (english) namespace file for the current page looks like
{
"greeting": "Hello, it appears you're using Internet Explorer, an outdated web browser."
}
this works, but it seems like there should be a much simpler solution.
what i really want is to be able to just type {{t "greeting"}} into the handlebars template to achieve the same result. is this possible without overriding core handlebars functionality?
here is the i18next docs page
http://i18next.com/pages/doc_features.html
I've answered my own question - it turned out easier than I expected.
You can access the handlebars instance in my Handlebars translate helper (duh?), so in the helper: renderer = this (for clarity), then you can just access renderer.currentpage to get the name of the required namespace. Note, you still have to send currentpage in the render function (on res.render()), but I'm ok with that side of it.
I've done it like this (it's probably pretty slow like this at the moment, but it works exactly how I want it to):
// Set namespace for translation
options.ns = options.ns || bestNamespaceFor( i18n_key );
result = i18n.t(i18n_key, options);
return new hbs.handlebars.SafeString(result);
function bestNamespaceFor( i18n_key ){
if ( i18n.exists(i18n_key, { ns: renderer.currentpage }) ) return renderer.currentpage;
if ( i18n.exists(i18n_key, { ns: renderer.layout }) ) return renderer.layout;
if ( i18n.exists(i18n_key, { ns: "common" }) ) return "common";
Works perfectly with {{t "my translation key" }} and gets the right namespace no matter where I use it.
I was wondering how to make a requirejs module with multiple files.
e.x. I require one file which then somehow gets multiple files.
I know I could do this:
define([all the other files], function () {
var mod = {},
files = [all the other files];
arguments.forEach(function (i) {
mod.[files[i]] = i;
});
return mod;
});
but I was wondering whether there was a better method?
You want a dynamic list of files(.txt,.tpl,.htm,.json), to avoid a change in JS source code to add more files.
I don't know if is there a way to do that, but you must take care about the time that will take to download all this files. I suggest you to create a json file with all files that you want to download, and iterate through this array of files using ajax to get them.
If you are try to get all module inside a directory, you need to create a js file: <package-all>.js that encapsulate an require([all files],function()); for example.
I believe that this is the only way to solve this.
first, write the modules like this,
define(function(){
return {
'mod-a' : require('modules/mod-a'),
'mod-b' : require('modules/mod-b'),
'mod-c' : require('modules/mod-c')
}
})
then use r.js to optimize the above js to blow one (r.js will combine all module file to one), or, you can write like this directly :
define('modules/mod-a', [], function(){});
define('modules/mod-b', [], function(){});
define('modules/mod-c', [], function(){});
define('mod-all', ['modules/mod-a','modules/mod-b','modules/mod-c'], function(){
return {
'mod-a' : require('modules/mod-a'),
'mod-b' : require('modules/mod-b'),
'mod-c' : require('modules/mod-c')
}
})
now, the modules 'mod-all' is the thing you wanted.
I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)