How to render data with Handlebars and jQuery - javascript

I have this html where I need to render the data
default.hbs
<div class="chart-container" data-action="chartContainer">
<ul>
<li class="department">
<h3>Enterprise</h3>
<ul class="sections">
// HERE I NEED TO RENDER THE DATA IN AN <li> TAG
</ul>
</li>
</ul>
</div>
and here is the code
APP.chartContainer = (function () {
var Handlebars = window.Handlebars;
var bindEventsToUI = function () {
$.getJSON('maindata.json')
.done(function(data) {
localStorage.setItem('jsonData', JSON.stringify(data));
}).fail(function(err) {
console.log(err);
});
var chartContainerTemplate = $(".chart-container").html();
var theTemplate = Handlebars.compile(chartContainerTemplate);
var getData = localStorage.getItem('jsonData');
var iterateObj = $.each(JSON.parse(getData), function(key, val) {
return val;
});
var theCompiledHtml = theTemplate(iterateObj[0].enterprise);
$(".sections").append(theCompiledHtml);
};
var init = function (element) {
bindEventsToUI();
};
/**
* interfaces to public functions
*/
return {
init: init
};
}());
the function iterateObj returns this
[
{
"enterprise":[
{
"id":"10",
"name":"Hellen Quesada",
"role":"Principal Software Engineer"
},
{
"id":"11",
"name":"Jonathan Chavez",
"role":"Principal Creative Engineer"
}
]
},
{
"consumer":[
{
"id":"18",
"name":"Helga Martinez",
"role":"Production Manager"
},
{
"id":"19",
"name":"Leroy Bernard",
"role":"Sr. Software Engineer"
}
]
}
]
but all I need to render for now is the enterprise part of the data, that is why in my function I am doing iterateObj.[0].enterprise but I am not getting anything in the DOM yet, how do I iterate properly in the over the object in order to get the rendering of the data I need?
What am I missing ?

The template needs to be a script not html. The script can contain the needed html though.
<script id="foo" type="text/x-handlebars-template">
<div class="chart-container" data-action="chartContainer">
<ul>
<li class="department">
<h3>Enterprise</h3>
<ul class="sections">
//Not really sure what you want here
//But access the data like this
<li>{{enterprise.id}}</li>
</ul>
</li>
</ul>
</div>
</script>
Then you compile the template (ie the script):
//var chartContainerTemplate = $(".chart-container").html();
//rename the script to a better id
var chartContainerTemplate = $("#foo").html();
Lastly I would highly suggest reading the docs. There are ways of looping and accessing data. The above template is very basic.

Related

Sort a list of two different objects with one common property

I have two lists that are coming from an API that represent two different classes. I want to display the two lists according to their proprieties in one general list where the elements are sorted by their date.
I need to separate the two lists because the proprieties to display are different and they only share the Date.
I manage to display the two list separately but cannot merge the two... Does anyone have an idea on that? Thank you very much.
here is the .js and view:
.js:
function activityController($http) {
var vm = this;
vm.race= [];
vm.try= [];
vm.errorMessage = "";
vm.isBusy = true;
$http.get("/api/race")
.then(function (response) {
//Sucess
angular.copy(response.data, vm.race);
}, function (error) {
//Failure
vm.errorMessage = "Failed to load the data" + error;
})
$http.get("/api/try")
.then(function (response) {
//Sucess
angular.copy(response.data, vm.try);
}, function (error) {
//Failure
vm.errorMessage = "Failed to load the data" + error;
})
.finally(function () {
vm.isBusy = false;
});
}
View :
<div class="col-md-offset-7">
<div class="text-danger" ng-show="vm.errorMessage">{{ vm.errorMessage}}</div>
<wait-cursor ng-show="vm.isBusy"></wait-cursor>
<ul class="well" ng-repeat="activities in vm.race| orderBy: 'date':true">
<li> {{activities.title}}</li>
<li>Date : {{activities.date | date :'dd-MM-yyyy'}}</li>
<li>Temps : {{activities.time}}</li>
</ul>
<ul class="well" ng-repeat="activities in vm.try| orderBy: 'date':true">
<li> {{activities.person}}</li>
<li>Date : {{activities.date | date :'dd-MM-yyyy'}}</li>
<li>Temps : {{activities.type}}</li>
</ul>
The way I would do it is:
<ul class="well" ng-repeat="activities in vm.getRacesAndTrys()| orderBy: 'date':true">
<li> {{activities.title}}</li>
<li>Date : {{activities.date | date :'dd-MM-yyyy'}}</li>
<li>Temps : {{activities.time}}</li>
</ul>
With a function in your vm:
vm.getRacesAndTrys = function() {
var result = [];
for (var i = 0; i < vm.try.length; i++) {
var item = vm.try[i];
result.push({
title: item.person,
date: item.date,
time: item.type
});
}
for (var i = 0; i < vm.race.length; i++) {
result.push(vm.race[i]);
}
return result;
}
This will create a combined array and also convert all the try objects so that their fields match that of the race objects.

Sort/GroupBy files pre-uploaded using AngularJS

I want to group my files before to upload them by extension(The extension can not be defined in the mime type attribute defined by the upload infos. So I used groupBy defined by angular-filter and instead to put an attribute('file.name' for example) to the filter Im using a function to get the extension.
So I want my pre-loaded files appears like this:
Extension1:
file3.Extension1
file1.Extension1
Extension2:
file4.Extension2
file2.Extension2
This is my EXAMPLE
Also my code:
<ul>
<li ng-repeat="f in files | groupBy: fileExtension" style="font:smaller">
{{f.name}}
</li>
</ul>
$scope.fileExtension = function(file) {
return file.name.split('.').pop();
};
Any suggestion is appreciated!
I would transform your list of file names into a list of file groups inside of an ngController or service. Binding to this transformed collection becomes trivial in the view.
$scope.groups = groupByExt(filenames);
function groupByExt(filenames) {
var extensions = [];
var groups = [];
angular.forEach(filenames, function(item) {
var extension = item.substring(item.lastIndexOf(".")+1);
if (!extensions[extension]) {
var group = { name: extension, files: [] };
extensions[extension] = group;
groups.push(group);
group.files.push({ name: item });
}
else {
var group = extensions[extension];
group.files.push({ name: item});
}
});
return groups;
}
HTML
<ul>
<li ng-repeat="group in groups">
{{ group.name }}
<ul>
<li ng-repeat="file in group.files">
{{ file.name }}
</li>
</ul>
</li>
</ul>
Demo
You can also set up $watchers so that when the original filenames list changes, it updates the file groups:
$scope.$watchCollection('filenames', function(newVal, oldVal) {
if(newVal !== oldVal) {
$scope.groups = groupByExt(newVal);
}
});
Demo
I would avoid filters because filters should not change the references of the underlying items (infinite digest issue)

dust js dynamic #eq condition

This seems simple, but I just can't figure how to dynamically change the #eq condition. The example shows a default render, i.e., curly, but what I need to do is accept user input, e.g., click a button, and change it to larry or moe.
See my jsfiddle http://jsfiddle.net/bodyrock/a7nmurnr/4/
From what I've read it appears I have to use one of the following techniques:
- base.push onto context
- makebase
- partials
- inline parameter
- variable lookup
<script type="javascript">
$(document).ready(function () {
var data = {
"title": "Famous People",
"names" : [{ "name": "Larry", "props":[{"name":"height","value":"5.8"},{"name":"weight","value":"160"}] },{ "name": "Curly", "props":[{"name":"height","value":"5.9"},{"name":"weight","value":"200"}]},{ "name": "Moe", "props":[{"name":"height","value":"5.8"},{"name":"weight","value":"160"}]}]
}
var source = $("#entry-template").html();
var compiled = dust.compile(source, "intro");
dust.loadSource(compiled);
dust.render("intro", data, function(err, out) {
$("#output").html(out);
});
});
</script>
<script id="entry-template">
{title}
<ul>
{#names}
{#eq key=name value="Curly"}
<li>
{name}
<ul><li>Weight: {#props}{#eq key=name value="weight"}{value}{/eq} {/props}</li></ul>
</li>
{/eq}
{/names}
</ul>
</script>
<input type="button" value="moe" onclick="alert('change to moe');"><input type="button" value="larry" onclick="alert('change to larry');"><input type="button" value="curly" onclick="alert('change to curly');">
<div id="output"></div>
You just need to rerender the template with a new context that contains information about what person you want to show.
JSFiddle
$('input').on('click', function() {
var ctx = dust.makeBase({ currentName: $(this).val() }).push(data);
dust.render("intro", ctx, function(err, out) {
$("#output").html(out);
});
}).first().click();
and
{#names}
{#eq key=name value=currentName}
<li>
{name}
<ul><li>Weight: {#props}{#eq key=name value="weight"}{value}{/eq}{/props}</li></ul>
</li>
{/eq}
{/names}

Knockout.js - Data binding outputting function text when not using parens

I am new to Knockout and have been trying to follow code examples and the documentation, but keep running into an issue. My data bindings printing the Knockout observable function, not the actual values held by my observable fields. I can get the value if I evaluate the field using (), but if you do this you do not get any live data-binding / updates.
Below are some code snippets from my project that are directly related to the issue I am describing:
HTML
<div class="col-xs-6">
<div data-bind="foreach: leftColSocialAPIs">
<div class="social-metric">
<img data-bind="attr: { src: iconPath }" />
<strong data-bind="text: name"></strong>:
<span data-bind="text: totalCount"></span>
</div>
</div>
</div>
Note: leftColSocialAPIs contains an array of SocialAPIs. I can show that code too if needed.
Initializing the totalcount attribute
var SocialAPI = (function (_super) {
__extends(SocialAPI, _super);
function SocialAPI(json) {
_super.call(this, json);
this.totalCount = ko.observable(0);
this.templateName = "social-template";
}
SocialAPI.prototype.querySuccess = function () {
this.isLoaded(true);
appManager.increaseBadgeCount(this.totalCount());
ga('send', 'event', 'API Load', 'API Load - ' + this.name, appManager.getRedactedURL());
};
SocialAPI.prototype.toJSON = function () {
var self = this;
return {
name: self.name,
isActive: self.isActive(),
type: "social"
};
};
return SocialAPI;
})(API);
Updating totalcount attribute for LinkedIn
var LinkedIn = (function (_super) {
__extends(LinkedIn, _super);
function LinkedIn(json) {
json.name = "LinkedIn";
json.iconPath = "/images/icons/linkedin-16x16.png";
_super.call(this, json);
}
LinkedIn.prototype.queryData = function () {
this.isLoaded(false);
this.totalCount(0);
$.get("http://www.linkedin.com/countserv/count/share", { "url": appManager.getURL(), "format": "json" }, this.queryCallback.bind(this), "json").fail(this.queryFail.bind(this));
};
LinkedIn.prototype.queryCallback = function (results) {
if (results != undefined) {
results.count = parseInt(results.count);
this.totalCount(isNaN(results.count) ? 0 : results.count);
}
this.querySuccess();
};
return LinkedIn;
})(SocialAPI);
In the <span data-bind="text: totalCount"></span>, I expect to see a number ranging from 0-Integer.MAX. Instead I see the following:
As you can see, its outputting the knockout function itself, not the value of the function. Every code example I've seen, including those in the official documentation, says that I should be seeing the value, not the function. What am I doing wrong here? I can provide the full application code if needed.
Not sure, but KO view models obviously tend to bind own (not inherited through prototypes) observable properties only. So you should rewrite your code to supply totalCount observable for every social network separately.

linking data from different json objects in jsrender

I am trying to link the data from foos and selectedFoos. I wish to list the selectedFoos and show the name from the foos object. The fooid in the selectedFoos would be linked to the foos id.
EDIT: I dont want to alter the structure of foos or selectedFoos.
fiddle is here
Html, Template
<div id="content"></div>
<script id="content_gen" type="x-jsrender">
<ul> {^{for sf}}
<li > {{: fooid}} - {{: code}} {{foo.name}} </li>
{{/for}}
</ul>
</script>
JS
var foos = [{
"id": 1,
"name": "a"
}, {
"id": 2,
"name": "b"
}, {
"id": 3,
"name": "c"
}];
var selectedFoos = [{
"fooid": 1,
"code": "z"
}, {
"fooid": 3,
"code": "w"
}];
var app = {
sf: selectedFoos,
f: foos
};
var templ = $.templates("#content_gen");
templ.link("#content", app);
You could add a view converter to lookup the name by id.
Like this - http://jsfiddle.net/Fz4Kd/11/
<div id="content"></div>
<script id="content_gen" type="x-jsrender">
<ul> {^{for sf}}
<li>{{id2name:fooid ~root.f }} - {{: code}} </li>
{{/for}}
</ul>
</script>
js
var app = {
sf: selectedFoos,
f: foos
};
$.views.converters("id2name", function (id, foos) {
var r = $.grep(foos, function (o) {
return o.id == id;
})
return (r.length > 0) ? r[0].name : '';
});
var templ = $.templates("#content_gen");
templ.link("#content", app);
Scott's answer is nice. But since you are using JsViews - you may want to data-link so you bind to the name and code values. Interesting case here, where you want to bind while in effect traversing a lookup...
So there are several possible approaches. Here is a jsfiddle: http://jsfiddle.net/BorisMoore/7Jwrd/2/ that takes a modified version of Scott's fiddle, with a slightly simplified converter approach, but in addition shows using nested {{for}} loops, as well as two different examples of using helper functions.
You can modify the name or the code, and see how the update works. You'll see that code updates in all cases, but to get the name to update is more tricky given the lookup.
You'll see that in the following two approaches, even the data-binding to the name works too.
Nested for loops
Template:
{^{for sf }}
{^{for ~root.f ~fooid=fooid ~sf=#data}}
{{if id === ~fooid}}
<li>{^{:name}} - {^{:~sf.code}} </li>
{{/if}}
{{/for}}
{{/for}}
Helper returning the lookup object
Helper:
function getFoo(fooid) {
var r = $.grep(foos, function (o) {
return o.id == fooid;
})
return r[0] || {name: ""};
}
Template:
{^{for sf}}
<li>{^{:~getFoo(fooid).name}} - {^{:code}} </li>
{{/for}}
See the many topics and samples here
http://www.jsviews.com
such as the following:
http://www.jsviews.com/#converters
http://www.jsviews.com/#helpers
http://www.jsviews.com/#fortag
http://www.jsviews.com/#iftag
http://www.jsviews.com/#samples/data-link/for-and-if
You should iterate over selectedFoos and lookup the name with fooid by iterating over foos. Then combine that data before rendering.
function getNameById(id) {
for (var i = 0; i < foos.length; i++)
if (foos[i].id == id)
return foos[i].name;
return '';
}
This function will return the name when given the id.
Usage:
alert(getNameById(2)); // alerts "b"

Categories