Handlebars not parsing data - javascript

I am trying to use handlebars to parse a javascript object but for some reason Handlebars is not loading the data properly...
Here is my template file:
{{> header}}
<h1>Handlebars JS Example</h1>
<script id="some-template" type="text/x-handlebars-template">
<table>
<thead>
<th>Name</th>
<th>Job Title</th>
<th>Twitter</th>
</thead>
<tbody>
{{#users}}
<tr>
<td>{{fullName person}}</td>
<td>{{jobTitle}}</td>
<td>#{{twitter}}</td>
</tr>
{{/users}}
</tbody>
</table>
</script>
<body>
<!-- Insertion point for handlebars template -->
<div id="main" style="margin-left:100px">
</div>
</body>
<script type="text/javascript" src="javascript/templating.js"></script>
Here is my .js file:
$(document).ready(function() {
var source = $("#some-template").html();
var template = Handlebars.compile(source);
var data = {
users: [ {
person: {
firstName: "Garry",
lastName: "Finch"
},
jobTitle: "Front End Technical Lead",
twitter: "gazraa"
}, {
person: {
firstName: "Garrasd",
lastName: "Finch"
},
jobTitle: "Photographer",
twitter: "photobasics"
}, {
person: {
firstName: "Garry",
lastName: "Finch"
},
jobTitle: "LEGO Geek",
twitter: "minifigures"
} ]
};
Handlebars.registerHelper('fullName', function(person) {
return person.firstName + " " + person.lastName;
});
console.log(template(data));
$("#main").append(template(data));
});
Note - when I console.log(template(data)), I get the following:
<table>
<thead>
<th>Name</th>
<th>Job Title</th>
<th>Twitter</th>
</thead>
<tbody>
</tbody>
Anyone have any idea what I'm doing wrong?? I'm using node.js + express.
Thank you!!

You didn't define any block helper for users but trying to use it in the template.
Change these lines
...
{{#users}}
...
{{/users}}
...
to these:
...
{{#each users}}
...
{{/each}}
...
Check out the documentation here: http://handlebarsjs.com/builtin_helpers.html#iteration
The each block helper
You can iterate over a list using the built-in each helper. Inside the
block, you can use this to reference the element being iterated over.

Related

Knockout JS Cannot bind to an array

I am trying to bind array to a table, so it shows all my array contents.
I tried first example, which works (purely in HTML):
<table>
<thead>
<tr><th>First name</th><th>Last name</th></tr>
</thead>
<tbody data-bind="foreach: people">
<tr>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
function Model() {
this.people = [
{ firstName: 'Bert', lastName: 'Bertington' },
{ firstName: 'Charles', lastName: 'Charlesforth' },
{ firstName: 'Denise', lastName: 'Dentiste' }
];
}
ko.applyBindings(new Model());
</script>
Then I got to the next level, and tried bigger example, which always shows error
Unable to process binding "foreach: function(){return interests }"
Message: Anonymous template defined, but no template content was provided
Below is faulty code:
// Activates knockout.js when document is loaded.
window.onload = (event) => {
ko.applyBindings(new AppViewModel());
}
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
this.fullName = ko.computed(() => this.firstName() + " " + this.lastName(), this);
this.interests = ko.observableArray([
{ name: "sport" },
{ name: "games" },
{ name: "books" },
{ name: "movies" }
]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody data-bind="foreach: interests"></tbody>
<tr>
<td data-bind="text: name"></td>
</tr>
</table>
I tired already with regular array, but with no luck.
You are closing the <tbody> before the inner template:
<tr>
<td data-bind="text: name"></td>
</tr>
So, the tr is now not in the context of the foreach binding.
Move the </tbody> to after the </tr> and before the </table> tags:
// Activates knockout.js when document is loaded.
window.onload = (event) => {
ko.applyBindings(new AppViewModel());
}
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
this.fullName = ko.computed(() => this.firstName() + " " + this.lastName(), this);
this.interests = ko.observableArray([
{ name: "sport" },
{ name: "games" },
{ name: "books" },
{ name: "movies" }
]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody data-bind="foreach: interests">
<tr>
<td data-bind="text: name"></td>
</tr>
</tbody> <!-- close here -->
</table>
Change your HTML to
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody>
<tr data-bind="foreach: interests">
<td data-bind="text: name"></td>
</tr></tbody>
</table>

knockout.js can I pass an an array of data to a template?

Working with Knockout templates, all of the examples seem to pass in single values to the template instead of array data. I have a template which will create some basic HTML, and then render a table using the data passed in to the template. Here is how it looks:
<script type="text/html" id="my-template">
<p>Here is the data</p>
[MORE HTML DATA HERE]
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Surname</th>
</tr>
</thead>
<tbody data-bind="foreach: [WHAT DO I PUT HERE???]">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: Surname"></td>
</tr>
</tbody>
</table>
</script>
<div data-bind="template: { name: 'my-template', data: PersonArray1 }"></div>
<div data-bind="template: { name: 'my-template', data: PersonArray2 }"></div>
<div data-bind="template: { name: 'my-template', data: PersonArray3 }"></div>
The templates do support a foreach binding, but I don't think I can use that as I don't want the template HTML heading data marked [MORE HTML DATA HERE] repeated for every item in the array.
I want the foreach binding within the template as in my rough example above. Is there a way I can make this work? I think the answer is in the placeholder i have marked with [WHAT DO I PUT HERE???] above, but I don't know if there is a variable which holds top-level template data.
What you are looking for is the $data parameter which will give you each entry from the supplied array:
var myViewModel = {
PersonArray1 : [
{ Id: 1, FirstName: "Alice", Surname: "Bloggs" },
{ Id: 2, FirstName: "Bob", Surname: "Bloggs" },
{ Id: 3, FirstName: "Claire", Surname: "Bloggs" }
]
};
ko.applyBindings(myViewModel)
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script type="text/html" id="my-template">
<p>Here is the data</p>
[MORE HTML DATA HERE]
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Surname</th>
</tr>
</thead>
<tbody data-bind="foreach: $data">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: Surname"></td>
</tr>
</tbody>
</table>
</script>
<div data-bind="template: { name: 'my-template', data: PersonArray1 }"></div>
I found the answer from this link, and the solution was posted by the creator himself.
https://github.com/knockout/knockout/issues/246
The solution is supplied in this fiddle:
http://jsfiddle.net/b9WWF/
The part of interest is where you call your template, instead of supplying an object directly to the data attribute, you can supply data in named values as follows:
<div data-bind="template: { name: 'my-template', data: { people: PersonArray1 } }"></div>
<div data-bind="template: { name: 'my-template', data: { people: PersonArray2, displayAdmins: false } }"></div>
You then access the array in your template as follows (I'm using my code above as an example):
<tbody data-bind="foreach: people">

Cannot read data "objectname" of undefined angular directive

Unable to read the data from parent scope to directive. Getting error like
TypeError: Cannot read property 'rowCollection' of undefined
Can you please help me out of this.
HTML
<div ng-controller="ctrl1 as one">
<ltcg-table options="one.rowCollection"></ltcg-table>
</div>
Grid HTML
<table st-table="rowCollection" class="table table-striped">
<thead>
<tr>
<th>first name</th>
<th>last name</th>
<th>birth date</th>
<th>balance</th>
<th>email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rowCollection">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
</tbody>
</table>
Javascript Controller
(function () {
var myApp = angular.module('myApp', ['smart-table']);
function one() {
this.song="Murali";
// alert("gg");
this.rowCollection = [
{firstName: 'Laurent', lastName: 'Renard', birthDate: new Date('1987-05-21'), balance: 102, email: 'whatever#gmail.com'},
{firstName: 'Blandine', lastName: 'Faivre', birthDate: new Date('1987-04-25'), balance: -2323.22, email: 'oufblandou#gmail.com'},
{firstName: 'Francoise', lastName: 'Frere', birthDate: new Date('1955-08-27'), balance: 42343, email: 'raymondef#gmail.com'}
];
//alert($scope.gridOptions.columnDefs[1].name);
//alert($scope.gridOptions);
};
myApp.directive('ltcgTable', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'options': '='
},
templateUrl: "ltcg-table.html",
link: function(scope, element, attr) {
alert(scope.$parent.options.rowCollection);
scope.rowCollection = scope.options.rowCollection;
}
}
});
myApp.controller('ctrl1', one)
})();
So, you have a directive with isolated scope. In this case scope parameter in link function referes to this scope, in your case this next object
{
'options': '='
}
So when you do in html options="one.rowCollection" value of one.rowCollection was binded to options property, so for access to it you should use scope.options in link function, on just options in view.
also $parent property set to parent scope, in your case - "ctrl1" controller scope. So you can directly go to controller and get what you want.
When use controller as syntax reference to controller saved in controller scope. So for access controller you should use it name.
Sample:
var myApp = angular.module('myApp', []);
function one() {
this.song = "Murali";
// alert("gg");
this.rowCollection = [{
firstName: 'Laurent',
lastName: 'Renard',
birthDate: new Date('1987-05-21'),
balance: 102,
email: 'whatever#gmail.com'
}, {
firstName: 'Blandine',
lastName: 'Faivre',
birthDate: new Date('1987-04-25'),
balance: -2323.22,
email: 'oufblandou#gmail.com'
}, {
firstName: 'Francoise',
lastName: 'Frere',
birthDate: new Date('1955-08-27'),
balance: 42343,
email: 'raymondef#gmail.com'
}];
//alert($scope.gridOptions.columnDefs[1].name);
//alert($scope.gridOptions);
};
myApp.directive('ltcgTable', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'options': '='
},
templateUrl: "ltcg-table.html",
link: function(scope, element, attr) {
//go to controller directly
scope.rowCollection = scope.$parent.one.rowCollection
}
}
});
myApp.controller('ctrl1', one)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="ctrl1 as one">
<ltcg-table options="one.rowCollection"></ltcg-table>
</div>
<script id="ltcg-table.html" type="text/ng-template">
<table st-table="rowCollection" class="table table-striped">
<thead>
<tr>
<th>first name</th>
<th>last name</th>
<th>birth date</th>
<th>balance</th>
<th>email</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5">
Get data from scope.options
</td>
</tr>
<tr ng-repeat="row in options">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
<tr>
<td colspan="5">
<hr/>
</td>
</tr>
<tr>
<td colspan="5">
Get data saved from controller directly in link function
</td>
</tr>
<tr ng-repeat="row in rowCollection">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
</tbody>
</table>
</script>
</div>
You added options to your directive scope, so you can directly access it throughscope.options. By the way, directives scopes are isolated (with the scope: {} notation), so you can't just go up and try to read parent scopes.

How to consume JSON array in AngularJS?

What i trying to do here is, i want to consume the JSON that i produce using Spring Restful WebService, which is looked like this :
[
{
"userid": 1,
"firstName": "kevin",
"lastName": "buruk",
"email": "pucuk#ubi.com"
},
{
"userid": 2,
"firstName": "helm",
"lastName": "krpuk",
"email": "helmkrupuk#gmail.com"
},
{
"userid": 3,
"firstName": "batok",
"lastName": "kelapa",
"email": "batokkelapa#gmail.com"
}
]
That JSON is produce by this function in my Java Spring MVC Controller, which is looked like this :
SpringController.java
#RestController
#RequestMapping("/service/")
public class SpringServiceController {
UserService userService = new UserService();
#RequestMapping(method = RequestMethod.GET,headers="Accept=application/json")
public List<User> getAllUsers() {
List<User> users=userService.getAllUsers();
return users;
}
}
And i am about to consume the JSON value into angular table, so i do store the JSON value in angular object in the scope, here is my Javascript code. i named this file app.js
app.js
function Hello($scope, $http) {
$http.get('http://localhost:8080/SpringServiceJsonSample/service/').
success(function(data) {
$scope.users = data;
});
}
and here is my html page. i named it index.html
index.html
<html ng-app>
<head>
<title>Angular Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="Hello">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.userid}}</td>
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
<td>{{user.email}}</td>
</tr>
</tbody>
</div>
</body>
</html>
What i missed here? i won't show anything. Before this i was trying to consume the JSON that i only have one record, it work properly. But this time i trying to consume the array, and i failed. What i missed here? is it the JSON or my javascript?
First of all, you have to use latest version of angular. Now, it's 1.4.5 with more performance optimizations and features.
About your question: error in html code. Here's the fixed version
function Hello($scope, $http) {
$scope.users = [
{
"userid": 1,
"firstName": "kevin",
"lastName": "buruk",
"email": "pucuk#ubi.com"
},
{
"userid": 2,
"firstName": "helm",
"lastName": "krpuk",
"email": "helmkrupuk#gmail.com"
},
{
"userid": 3,
"firstName": "batok",
"lastName": "kelapa",
"email": "batokkelapa#gmail.com"
}
]
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app>
<table ng-controller="Hello">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.userid}}</td>
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
<td>{{user.email}}</td>
</tr>
</tbody>
</table>
</body>
As you can see, I fixed it with adding only one html tag!

How do you interpolate a dynamic {{property}} in Handlebars / Ember.js?

Say I have a User model in JavaScript that looks something like this:
var User = function(attributes) {
this.attributes = attributes;
}
User.fields = [
{name: 'firstName'},
{name: 'lastName'},
{name: 'email'}
]
User.prototype.get = function(key) {
return this.attributes[key];
}
User.all = [new User({firstName: 'Foo'})];
And I want to run it through a Handlebars template that goes through each field on the User class, creates a header for it, and then for each user renders the values:
<table>
<thead>
<tr>
{{#each User.fields}}
<th>{{name}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each User.all}}
<tr>
{{#each User.fields}}
<td>{{content.get(name)}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
My question is, how do I accomplish that internal part:
{{#each User.fields}}
<td>{{content.get(name)}}</td>
{{/each}}
That's basically doing user.get(field.name). How can I do that in Handlebars, given I don't know the fields before hand and want this to be dynamic?
Thanks for your help.
<body>
<div id='displayArea'></div>
<script id="template" type="text/x-handlebars-template">
<table border="2">
<thead>
<tr>
{{#each Fields}}
<th>{{name}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each users}}
<tr>
{{#each ../Fields}}
<td>{{getName name ../this}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</script>
<script type="text/javascript">
var User = function(attributes) {
this.attributes = attributes;
}
User.fields = [
{name: 'firstName'},
{name: 'lastName'},
{name: 'email'}
]
User.prototype.get = function(key) {
return this.attributes[key];
}
User.all = [new User({firstName: 'Foo',lastName :'ooF',email : 'foo#gmail.com'}) , new User({firstName: 'Foo2'})]; //array of user
//handle bar functions to display
$(function(){
var template = Handlebars.compile($('#template').html());
Handlebars.registerHelper('getName',function(name,context){
return context.get(name);
});
$('#displayArea').html(template({Fields :User.fields,users:User.all}));
});
</script>
</body>
This will solve ur problem using helpers in handlebar JS
You can write a Handlebars helper to do this for you.

Categories

Resources