Selecting with jquery in my own js templating system/engine - javascript

For simplification and control, I have my own js templating engine, which works like this:
html:
<ul id="persons">
<li class="person-template hide">
<span>{name}</span>, <span class="placemark" data-lat="{latitude}" data-lng="{longitude}">{location}</span>
<li>
</ul>
js:
var persons = [
{"name":"hulk", "location": "usa", latitude: -33.4, longitude: -70.5},
{"name":"wolverine", "location": "mexico", latitude: -33.4, longitude: -70.5},
];
$.each(persons, function(i, person) {
var html = $(".person-template").clone().removeClass("person-template").addClass("person").show().outerHtml();
html = html.replaceAll("{name}", person.name);
html = html.replaceAll("{latitude}", person.latitude);
html = html.replaceAll("{longitude}", person.longitude);
html = html.replaceAll("{location}", person.location);
});
(replaceAll and outerHtml are two helper functions I've made, they are self-explained by their names)
My problem with this is that sometimes I want to select things with jQuery but my template notation interferes, for example:
$(".placemark").each(function(i, v) {
alert($(v).data("latitude")));
});
Will alert: "{latitude}", "-33.4", "-33.4"
Obviously I want to avoid the template value.
What is the most clean solution for this?
Start using a formal template engine?
Skip first in the $.each? (kind of ugly)
...?

You are cloning the template, so it still exists. You need to remove it after you loop through.
$(".person-template").remove();
or, you can just make sure not to grab the person-template when you alert. $(".location_but_not_location_template").each ...

Related

Use the same data object across multiple templates in Handlebars.js

I want to use the same data object across multiple templates in different contexts, how can I reuse it. By any chance, handlebars partials would be helpful in this situation.
$("document").ready(function(){
var source = $("#homePage-template").html();
var template = Handlebars.compile(source);
var dataPanel = {activites:[
{activity: "Events" , activityRedirect:"#", icon:"fa fa-camera-retro" , imageUrl:"xyz"},
{activity: "Ciriculars" , activityRedirect:"#", icon:"fa fa-paper-plane", imageUrl:"xyz"}
]};
$("#homePage-placeholder").html(template(dataPanel));
$("#another-placeholder").html(template(dataPanel));
});
And here is my template:
<script id="homePage-template" type="text/x-handlebars-template">
<ul>
{{#activites}}
<li><i class="{{icon}}" aria-hidden="true"></i>{{activity}}</li>
{{/activites}}
</ul>
</script>
Another Template
<script id="another-template" type="text/x-handlebars-template">
{{#activites}}
<div>
<img src="{{imageUrl}}"/>
<span>{{activity}}</span>
</div>
{{/activites}}
</script>
Now how can I reuse this data into "another-template", because I want an image and text in that but it renders like "homePage-template" only in form of a list, if anyone has an idea over this!
In order to have a second template you must fetch its HTML source from the DOM and compile it into a template method just as you are doing for the Home Page Template.
var homepage_template_source = $('#homePage-template').html();
var another_template_source = $('#another-template').html();
var homepage_template = Handlebars.compile(homepage_template_source);
var another_template = Handlebars.compile(another_template_source);
You must call the template method for the specific template you wish to render:
$("#homePage-placeholder").html(homepage_template(dataPanel));
$("#another-placeholder").html(another_template(dataPanel));
See this fiddle for an example.

AngularJS namespace for function call in view template

I have a div with controller like this
<div ng-controller= "merchandiseListCtrl as ml">
In the js file, I have a couple of function calls and render an object on return.
vm.getlaptopTotal = (productCatergory) => getTotal(productCatergory);
var getTotal = function (_) {
var res = {
count: 0,
total: 0,
products: []
};
_.map((item) =>{
if(item.purchased){
res.count += 1;
res.total += item.price;
res.products.push(item.product);
}
});
return res;
};
So, it is obvious that I'm returning obj res with properties: count, total, and products.
In the HTML view template, I want to have to shorter namespace, shortcut to reduce the pattern ml.fn(x).products of my function call to return the properties' values for some fields. In other words, I want to assign something like ng-bind ='myTotal = ml.fn(x)', but then I run into ton of errors about $rootScope. What should I do to have a better namespace for this scenario, and complexity may go this far: funcObj.subobj.property.childproperty.grandchildproperty ? So accessibility and readibility can be accomplished?
Here is my HTML code
<div>{{ml.myTotal = ml.getlaptopTotal(ml.laptops)}}>
<!-- display the selection -->
Laptop count: {{ ml.myTotal.count }} units
<br>
<div ng-if="ml.myTotal.products">
<ul ng-repeat="product in ml.myTotal.products">
<li>{{$index+1}}) {{product}}</li>
</ul>
</div>
<br>
</div>
EDIT 1: Ran into this error because now the products array, funcObj().products is dynamic. Don't know how to fix yet. But previously, I use funcObj() as an ng-click event handler.
Thanks,

JsRender - How to Translate templates? (Best Practices)

Im rokiee using Jsrender, I would like to know which is the best way to translate my templates
I have templates like this:
_welcome.tmpl.html:
<div> Hello, {{:name}}</div>
<div> welcome to {{:place}}</div>
and i read daat from file like this:
welcome.json:
{
"name": "David"
"place": "wien"
}
Until here, works fine.
So, now i would like to translate the words "hello" and "welcome to" in diferrents languages. But my system its really ugly and inefficient.
I have different files that i load depends on "lang" attribute. For expample
lang="EN" im going to load:
english_vars.js
var t_hello = "Hello";
var t_msg = "Welcome to";
if lang="es" im going to load:
spanish_vars.js
var t_hello = "Hola";
var t_msg = "Bienvenido a";
Then my templates looks like this:
var wellcomeTemplate = `
<div>`+t_hello+`, {{:name}}</div>
<div>`+t_msg+` {{:place}}</div>`
There are any way to improve this templates engine translations?
Note: Translations MUST NOT come in the same .json that DATA
If you have your localized dictionary as JSON or a JavaScript object (hash):
var terms = {
hello: "Hola",
welcome: "Bienvenido a"
};
then you can pass in the terms separately from the data, as helper variables:
<script id="tmpl" type="text/x-jsrender">
<div>{{:~hello}}, {{:name}}</div>
<div>{{:~welcome}} {{:place}}</div>
</script>
like this:
var data = {
name: "John",
place: "Madrid"
};
var html = $.templates("#tmpl").render(data, terms);
When you change language, pass in the appropriate localized terms.
See http://www.jsviews.com/#helpers
An alternative possibility is to translate the template for each language, using JsRender itself to translate. Simply change the delimiters for the translation step, so you only translate the terms, without changing the other tags:
<script id="baseTmpl" type="text/x-jsrender">
<div><%:hello%>, {{:name}}</div>
<div><%:welcome%> {{:place}}</div>
</script>
then:
$.views.settings.delimiters("<%", "%>");
var localizedTemplate = $.templates("#baseTmpl").render(terms);
$.views.settings.delimiters("{{", "}}");
var html = $.templates(localizedTemplate).render(data);
See http://www.jsviews.com/#settings/delimiters
Both approaches are shown in this jsfiddle

Create HTML tag from Javascript object

What is the best method to change this object
{
src: 'img.jpg',
title: 'foo'
}
into a valid HTML tag string like this
<img src="img.jpg" title="foo" />
Solution 1
With jQuery this is easy; but complicated:
$('<img/>').attr(obj).wrap('<div/>').parent().html();
Any better ideas?
Why not:
$('<img/>', obj).get(0).outerHTML;
Fiddle
You do not need to wrap it in a div using multiple functions and get the html, just use get(0) to get the DOM element and outerHTML to get the element's html representation.
Unless you are using browsers really old you can rely on outerHTML
Here is a JSPerf to compare the performance diff between the approaches.
Perhaps slightly more concise than PSL's?
$('<img />',object)[0].outerHTML;
Simple with jquery
$("<div>").append($('<img />',object)).html();
If you are only doing one element, then this solution is overkill, but I thought I would post it anyway as I don't know what your project is.
Have you considered a JavaScript template engine? I've been playing around with Swig lately, as it is quite lightweight, but there are many options. Basically, you create a template, pass a JavaScript object, and the compiled template is executed, returning a string of HTML.
Example from Swig Documentation
Template
<h1>{{ pagename|title }}</h1>
<ul>
{% for author in authors %}
<li{% if loop.first%} class="first"{% endif %}>
{{ author }}
</li>
{% else %}
<li>There are no authors.</li>
{% endfor %}
</ul>
JavaScript to Render Template
var template = require('swig');
var tmpl = template.compileFile('/path/to/template.html');
tmpl.render({ // The return value of this function is your output HTML
pagename: 'awesome people',
authors: ['Paul', 'Jim', 'Jane']
});
Output
<h1>Awesome People</h1>
<ul>
<li class="first">Paul</li>
<li>Jim</li>
<li>Jane</li>
</ul>
Making html elements based out of objects containing attribute-attribute values such as
{
src: 'img.jpg',
title: 'foo'
}
almost completely falls into the paradigm of cook.js.
The command which you would issue with cook would be:
img ({
src: 'img.jpg',
title: 'foo'
})
If the attribute details are stored as given in your example,
in a variable obj then:
img(obj)
For more details check it out at cook.relfor.co.
Here's how you make it as a string:
var img = '<img ',
obj = { src : 'img.jpg', title: 'foo' };
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
img += prop + '=' + '"' + obj[prop] + '" ';
}
}
img += '/>';
http://jsfiddle.net/5dx6e/
EDIT: Note that the code answers the precise question. Of course it's unsafe to create HTML this way. But that's not what the question asked. If security was OP's concern, obviously he/she would use document.createElement('img') instead of a string.
EDIT 2: For the sake of completeness, here is a much safer way of creating HTML from the object:
var img = document.createElement('img'),
obj = { src : 'img.jpg', title: 'foo' };
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
img.setAttribute(prop, obj[prop]);
}
}
http://jsfiddle.net/8yn6Y/

How to bind click handlers to templates in knockoutjs without having a global viewModel?

I'm very new to KnockoutJs so I'm hoping that there is a well known best practice for this kind of situation that I just haven't been able to find.
I have a view model that contains an array of items. I want to display these items using a template. I also want each item to to be able to toggle between view and edit modes in place. I think what fits best with Knockout is to create the relevant function on either the main view model or (probably better) on each item in the array and then bind this function in the template. So I have created this code on my page:
<ul data-bind="template: {name: testTemplate, foreach: items}"></ul>
<script id="testTemplate" type="text/x-jquery-tmpl">
<li>
<img src="icon.png" data-bind="click: displayEditView" />
<span data-bind="text: GBPAmount"></span>
<input type="text" data-bind="value: GBPAmount" />
</li>
</script>
<script>
(function() {
var viewModel = new TestViewModel(myItems);
ko.applyBindings(viewModel);
})();
</script>
And this in a separate file:
function TestViewModel(itemsJson) {
this.items = ko.mapping.fromJS(itemsJson);
for(i = 0; i < this.items.length; ++i) {
this.items[i].displayEditView = function () {
alert("payment function called");
}
}
this.displayEditView = function () {
alert("viewmodel function called");
}
};
Due to the environment my JS is running in I can't add anything to the global namespace, hence the annonymous function to create and set up the view model. (There is a namespace that I can add things to if it is necessary.) This restriction seems to break all the examples I've found, which seem to rely on a global viewModel variable.
P.S. If there's an approach that fits better with knockoutJS than what I am trying to do please feel free to suggest it!
When your viewModel is not accessible globally, there are a couple of options.
First, you can pass any relevant methods using the templateOptions parameter to the template binding.
It would look like (also note that a static template name should be in quotes):
data-bind="template: {name: 'testTemplate', foreach: items, templateOptions: { vmMethod: methodFromMainViewModel } }"
Then, inside of the template vmMethod would be available as $item.vmMethod. If you are using templateOptions as the last parameter, then make sure that there is a space between your braces { { or jQuery templates tries to parse it as its own.
So, you can bind to it like:
<img src="icon.png" data-bind="click: $item.vmMethod" />
The other option is to put a method and a reference to anything relevant from the view model on each item. It looks like you were exploring that option.
Finally, in KO 1.3 (hopefully out in September and in beta soon) there will be a nice way to use something like jQuery's live/delegate functionality and connect it with your viewModel (like in this sample: http://jsfiddle.net/rniemeyer/5wAYY/)
Also, the "Avoiding anonymous functions in event bindings" section of this post might be helpful to you as well. If you are looking for a sample of editing in place using a dynamically chosen template, then this post might help.
This is for those asking how to pass variable methods (functions) to Knockout Template. One of the core features of Templating is the consuming of variable data, which can be String or function. In KO these variables can be embedded in data or foreach properties for the Template to render. Objects embedded in data or foreach, be it String, function etc, can be accessed at this context using $data.
You can look at this code and see if it can help you to pass functions to Knockout Template.
function ViewModel() {
this.employees = [
{ fullName: 'Franklin Obi', url: 'employee_Franklin_Obi', action: methodOne },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo }
],
this.methodOne = function(){ alert('I can see you'); },
this.methodTwo = function(){ alert('I can touch you'); }
}
ko.applyBindings(new ViewModel());
<ul data-bind="template: { name: employeeTemplate, foreach: employees }" ></ul>
<script type="text/html" id="employeeTemplate">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
If you want to serve multiple Template constructs you can introduce a switch method to your ViewModel like this, and use as property to introduce alias for each item (employee). Make sure you add the switch key, linkable, to the item object.
...
this.employees = [
{ fullName: 'Franklin Obi', linkable : false },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo, linkable : true }
],
this.methodLinkTemplate = function(employee){return employee.linkable ? "link" : "noLink"; } //this is a two way switch, many way switch is applicable.
...
Then the id of the Template forms will be named thus;
<ul data-bind="template: { name: employeeTemplate, foreach: employees, as: 'employee' }" ></ul>
<script type="text/html" id="noLink">
<li data-bind="text: fullName"></li>
</script>
<script type="text/html" id="link">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
I have not ran this codes but I believe the idea can save someones time.

Categories