jQuery: Templates. Must be contained in script block? - javascript

As a JS developer, I always keep my design layer separate from my business layer. Meaning, HTML is always alone, CSS and JavaScript files are external and included.
Now, in the case of jQuery Templates, a declared template must apparently live within a script block of the page. How in the world are you supposed to keep all of your business separated? I don't want messy HTML. I want clean HTML that never needs to be touched because it's been designed that way...
Are there solid, proven methods for doing this?

You can call $.templ(yourTemplateString, data) if you want. That returns the built-up elements which you can then stick in your document with "append" or whatever.
I agree with you that doing templates as <script> tags is not a super cool idea for everyone.

There is a sacrifice, but what looks more maintainable and clean:
Original:
for(var i=0; i<client.name.length; i++) {
clRec += "<li><a href='clients/"+client.id[i]+"'>" + client.name[i] + "</a></li>";
}
Templates
<script id="clientTemplate" type="text/html">
<li>${name}</li>
</script>
Code from http://blog.reybango.com/2010/07/09/not-using-jquery-javascript-templates-youre-really-missing-out/

You can use a different $.tmpl() syntax and $.ajax() to use jQuery Templates definitions stored in external files.

I suppose that if you're looking to templatize your application, try out the jQuery Template. But I've come to realize that, like many frameworks, ends up complicating the code even further.

Related

AngularJS - inject trusted code from loaded content

I've hooked up a lazy loader in Angular. It pulls in full templates and extracts key information from that full template in order to populate a partial. This full page template has script tags which load in and then register with the existing app. All of this works fine. My problem is that I'd like to remove the only use of jQuery in this approach.
The root issue is that the JS inside of something.js doesn't execute when using $element.html(), but it does execute when using $.html(), despite the script tag being placed in the DOM in both approaches.
Working code, including lazy loader and post-bootstrap registration of lazy-loaded JS:
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
// i'd like the following to not rely on full jQuery.
$("#stage").html(partial);
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
I've tried what seems like the logical translation:
$element.html($compile(partial)($scope));
and the DOM is created properly, but the JS inside of the loaded <script> tag doesn't actually execute. My research suggested this was an $sce issue, so I tried:
$element.html($compile($sce.trustAsHtml(partial)($scope));
but i get the same result. the DOM is fine, but the JS doesn't actually execute and so I get undefined controller issues.
I've tried playing with $sce.JS and $sce.RESOURCE_URL but the docs didnt elaborate much so I'm not sure I know whether or not what I'm trying is even right.
I've also tried $element[0].innerHTML but I get the same result as $element.html().
Preemptive disclaimer: I can trust the incoming HTML/JS. I know it's inadvisable. This isn't my baby and it is much more complicated than I explained so please try to stay on topic so other people in this position may not have as hard of a time as I am :)
The $http.get happens in a provider, and the $element.html happens in a directive. I consolidated them to remove noise from the problem.
Jquery will find any script tags and evaluate them (either a direct eval or appending them to the head for linked scripts) when calling html(), see this answer. I'm assuming angular's jquery lite doesn't do this. You would need to effectively replicate what jquery is doing and look for script tags in the html you are appending.
Something like this (although I haven't tested it):
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
var d = document.createElement('div');
d.innerHTML = partial;
var scripts = d.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
document.head.appendChild(scripts[0]);
}
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
This is far from an ideal solution as it gives you no guarantee of when things are loaded and doesn't really handle dependencies across scripts. If you can control the templates it would be simpler to remove the scripts from them and load them independently.

how to avoid fetching a part of html page which is being called inside another page?

I am calling a .html page(say A.html, which is dynamically created by another software each time a request is made) inside another webpage (say B.html). I am doing this by using the .load() function. Everything works fine but the problem is I donot want the so many "br" tags (empty tags) present at the end of A.html into B.html. Is there any way to avoid fetching those "br" tags into B.html? Any suggestion would be of great help. Thank you in advance.
You can't avoid loading part of a file when you are just accessing it.
The best option would be to simply remove the extra <br> tags from the document to begin with. There is probably a better way to accomplish whatever they are attempting to accomplish.
With some server-side scripting, it could be possible to strip them automatically when you load it, but would probably be pretty bothersome to do.
Instead, if you can't remove the <br> elements for some reason, what might be easier, if you are just dealing with a handful of <br> tags would be to simply strip them out.
Since you mention using the load() function, I'm guessing you are using jQuery.
If that's the case, something like this would cleanly strip out any extra <br> tags from the end of the document.
Here is a JSfiddle which will do it: http://jsfiddle.net/dMJ2F/
var html = "<p>A</p><br><p>B</p><br><p>C</p><br><br /><br/>";
var $html = $('<div>').append(html);
var $br;
while (($br = $html.find('br:last-child')).length > 0) {
$br.remove();
}
$('p').text($html.html());
Basically, throw the loaded stuff in to a div (in memory), then loop through and remove each <br> at the end until there aren't any. You could use regex to do this as well, but it runs a few risks that this jQuery method doesn't.
You shout delete the br-tags in your A.html.
Substitute them by changing the class .sequence with marging-top:30px
And have an other value in your B.html-file.
You also can run this:
$('br', '.sequence').remove();​
in the load-function. It will strip all br-tags.
You can't avoid fetching a part of your page, but you CAN fetch only a part of it.
According to the jQuery docs, you can call load like this:
$("#result").load("urlorpage #form-id");
That way, you only load the form html inside the result element.

How to optimize the creation of long html code to be used in javascript string

I need to define javascript variables containing very long html code.
Here is a short example:
var text = "Select one item:<br>";
text += "<ul class='thumbnails'><li class='span3'><a href='#' class='thumbnail'><img src='http://placehold.it/260x180' alt=''></a></li></ul>";
Since the html is going to be much much longer, I would rather work in pure html rather than append text to a javascript string.
I thought of creating a separate html file, but I guess that would require an Ajax call to fetch its content.
What is the best way to deal with this?
As N.Zakas said on "maintainable javascript" book, you should «keep html out of javascript» to promote high mantainability of the code through loose coupling of UI layers.
Beside the ajax solution you could also place the markup as a comment in the html file and read it via javascript (as a regular DOM node) or you could use some kind of microtemplating system (e.g. handlebars) and place your markup in a script block (the idea is to put markup where is expected to be found and not into javascript logic)
One possible solution is to use templates. There are a few JavaScript libraries that provide templating, underscore.js is one: http://underscorejs.org/#template, or more details on how to use it for templating http://www.headspring.com/blog/developer-deep-dive/an-underscore-templates-primer/
Plus underscore is great for a number of other things.
You could break up the text into actual HTML objects.
var thumbnailsUL = document.createElement('ul');
for (index in {your-thumbnails-list}) {
var thumbnail = document.createElement('li');
thumbnail.innerHTML = {whatever you need, more objects or html as text};
thumbnailsUL.appendChild(thumbnail);
}
Ideally though, there is no reason to build this IN JavaScript - can you not emit it from the server?
Instead of constructing HTML in Javascript as a string, I would rather suggest you to emit those html elements in the page itself and hide while loading. Then in Javascript, you could select that container and display it.

HTML generation in JS code

I'd like to know your thoughts about HTML code generation in my JS code.
I just think that the html.push("<tag>" + something + "</tag>") style pretty annoying.
I've already tried something with templates inside my HTML file (and put some placeholders therein), and then used its content to a replace the placeholders to my real values.
But maybe you guys have other ideas, possibly one using jQuery.
jQuery is the way to go. You can do things like this:
// create some HTML
var mySpan = $("<span/>").append("Something").addClass("highlight");
it is cross-browser compatible,
it is an easy to use syntax
There is also a templating plugin.
You can use createelement, appendchild, and innerHTML to do this.
Here's an article from A List Apart that uses these functions in the process of generating a dropdown menu.
jQuery has javascript template plugins like jBind and jTemplate. I haven't used them myself but I do recommend jQuery whenever possible.
A note on html generation, it is not searchable by search engines in most cases.
I'm a big fan of how PrototypeJS handles templates.
First you create an instance of the Template class and pass it a string containing the template HTML.
var tpl = new Template('Here is a link to #{sitename}');
Then you pass it data containing the values to replace within the template.
$('someDiv').innerHTML = tpl.evaluate( {link: 'http://www.stackoverflow.com', sitename: 'StackOverflow'} );
In the above example, I have a div with id="someDiv" and I'm replacing the contents of the div with the result of the template evaluation.
Resig has a little blog entry on creating a very lightweight template system.
There are a bunch of JQuery function that support this. In particular, you should look at append(content), appendTo(content) prepend(content) prependTo(content), replaceWith(content), after(content), before(content), insertAfter(content), and insertBefore(content).

How to handle localization in JavaScript files?

I want JavaScript code to be separated from views.
I got the requirement to implement localization for a simple image button generated by JavaScript:
<img src="..." onclick="..." title="Close" />
What's the best technique to localize the title of it?
PS: I found a solution by Ayende. This is the right direction.
Edit:
I got Localization helper class which provides the Controller.Resource('foo') extension method.
I am thinking about to extend it (helper) so it could return all JavaScript resources (from "ClientSideResources" subfolder in App_LocalResources) for the specified controller by its name. Then - call it in BaseController, add it to ViewData and render it in Layout.
Would that be a good idea?
EDIT
Consider writing the necessary localized resources to a JavaScript object (hash) and then using it for lookup for your dynamically created objects. I think this is better than going back to the server for translations. This is similar to adding it via viewdata, but may be a little more flexible. FWIW, I could consider the localization resources to be part of the View, not part of the controller.
In the View:
<script type="text/javascript"
src='<%= Url.Content( "~/Resources/Load?translate=Close,Open" %>'></script>
which would output something like:
var local = {};
local.Close = "Close";
local.Open = "Open";
Without arguments it would output the entire translation hash. Using arguments gives you the ability to customize it per view.
You would then use it in your JavaScript files like:
$(function(){
$('#button').click( function() {
$("<img src=... title='" + local.Close + "' />")
.appendTo("#someDiv")
.click( function() { ... } );
});
});
Actually, I'm not too fussed about keeping my JavaScript code out of my views as long as the JavaScript code is localized in a container. Typically I'll set my master page up with 4 content area: title, header, main, and scripts. Title, header, and main go where you would expect and the scripts area goes at the bottom of the body.
I put all my JavaScript includes, including any for viewusercontrols, into the scripts container. View-specific JavaScript code comes after the includes. I refactor shared code back to scripts as needed. I've thought about using a controller method to collate script includes, that is, include multiple scripts using a single request, but haven't gotten around to that, yet.
This has the advantage of keeping the JavaScript code separate for readability, but also allows me to easily inject model or view data into the JavaScript code as needed.
Actually ASP.NET Ajax has a built-in localization mechanism: Understanding ASP.NET AJAX Localization
If you insist on keeping it separate, you could do something like:
//keep all of your localised vars somewhere
var title = '{title_from_server}';
document.getElementById('someImage').title = title;
Remember, if you use JavaScript code to initialize any text of elements, your site will degrade horribly where JavaScript isn't available.

Categories

Resources