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.
Related
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">
I have got issue when I work out on the ng-repeat directive, since I am a beginner , unable to resolve it on my own.
var myModule = angular.module("myFirst",[]);
myModule.controller("cont", function($scope){
var employees = [
{name: "Manju", age :"23", rank:"2"},
{name: "SivaSankari", age :"23", rank:"1"},
{name: "Gayathri", age :"23", rank:"1"},
];
$scope.employees = employees;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="myFirst">
<head>
<title> My workout|ngRepeat
</head>
<body>
<div ng-controller="cont">
<table>
<thead>
<th>Name</th>
<th>Age</th>
<th>Postions</th>
</thead>
<tbody>
<tr ng-repeat="x in employees">
<td>{{employees.name}}</td>
<td>{{employees.age}}</td>
<td>{{employees.position}}</td>
</tr>
</tbody>
</table>
</div>
</body>
<html>
Also I have doubt on key that is x. Is ok to give any key value ?
Can any one please help me to resolve and understand ?
Thanks.
First
You have to use x instead of employees because employees is your array and x the current item
Second
You were trying to access the property position that is not existing inside your object. You should call rank
var myModule = angular.module("myFirst", []);
myModule.controller("cont", function($scope) {
var employees = [{
name: "Manju",
age: "23",
rank: "2"
}, {
name: "SivaSankari",
age: "23",
rank: "1"
}, {
name: "Gayathri",
age: "23",
rank: "1"
}, ];
$scope.employees = employees;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="myFirst">
<body>
<div ng-controller="cont">
<table>
<thead>
<th>Name</th>
<th>Age</th>
<th>Postions</th>
</thead>
<tbody>
<tr ng-repeat="x in employees">
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.rank}}</td>
</tr>
</tbody>
</table>
</div>
</body>
<html>
It should be
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.position}}</td>
You should access the properties of x not the employee array
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.position}}</td>
You're defining x as the employee you're iterating over. So the repeat part should be:
<tr ng-repeat="x in employees">
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.position}}</td>
</tr>
When you used ng-repeat="x in employees" it means you're saying on each iterate assign the item to x, that's why you can't use any other variable other than x.
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.position}}</td>
I didn't understand your ngRepeat in the title tag, but your table declaration makes little sense.
<tr ng-repeat="x in employees">
means that for every element (henceforth to be referred to as x) in the array employees, repeat this element viz. table row. (Make sure that's what you really want to do)
Therefore, inside your td, you gotta write -
<td>{{x.name}}</td>
Also I have doubt on key that is x. Is ok to give any key value ?
If you mean to say whether you can call x anything else then yes, you can.
E.g.
<tr ng-repeat="singleEmployee in employees">
<td>{{singleEmployee.name}}</td>
You have issue in ng-repeat, it should be x.name not employess.name
<tr ng-repeat="x in employees">
<td>{{x.name}}</td>
<td>{{x.age}}</td>
<td>{{x.position}}</td>
</tr>
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.
I want to populate the route's model using auto numbering 1,2,3 in my handlebar template and not using autogenerated db id as in the picture, as those are mess up when record is deleted, the problem is #index is no longer supported in handlebar.
my Route:
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
);
my controller:
App.PostsController = Ember.ObjectController.extend({
actions: {
delete: function(post) {
post.deleteRecord();
post.get('isDeleted'); // => true
post.save(); // => DELETE to /posts/1
},
adNewPost: function() {
this.transitionToRoute('new');
}
}
});
My model and RESTAdapter
App.Post = DS.Model.extend({
postId: DS.attr('string'),
title: DS.attr('string'),
author: DS.attr('string'),
body: DS.attr('string')
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'emberpostsrest/api'
});
My template:
<script type="text/x-handlebars" id="posts">
<h1>Posts List</h1>
<button {{action 'addNewPost' }}>Add New Posts</button>
<p></p>
<table border="1">
<tr>
<th scope="col">Index</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">Body</th>
<th scope="col">Delete</th>
</tr>
{{#each model}}
<tr>
<td>
{{#link-to 'post' this}}
{{id}} //<---- HOW TO ADD AN AUTO INDEX HELPER HERE (1,2,3, ETC
// AND NOT USING MODEL'S ID
{{/link-to}}
</td>
<td>{{title}}</td>
<td>{{author}}</td>
<td>{{body}}</td>
<td><a href="" {{action 'delete' this}}>X</a></td>
</tr>
{{/each}}
</table>
<div>
{{outlet}}
</div>
UPDATED WITH ANSWER AS SUGGESTED :D
The CSS
table {
counter-reset: id;
}
.id-column:before {
counter-increment: id;
content: counter(id);
}
The template
<script type="text/x-handlebars" id="posts">
<h1>Posts List</h1>
<button {{action 'addNewPost' }}>Add New Posts</button>
<p></p>
<table border="1">
<tr>
<th scope="col">Index</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">Body</th>
<th scope="col">Delete</th>
</tr>
{{#each}}
<tr>
<td class="id-column">
</td>
<td>
{{#link-to 'post' this}}
{{title}}
{{/link-to}}
</td>
<td>{{author}}</td>
<td>{{body}}</td>
<td><a href="" {{action 'delete' this}}>X</a></td>
</tr>
{{/each}}
</table>
<div>
{{outlet}}
</div>
The updated browser output
Use CSS counters instead of mucking around with Ember things. It appears that there are some problems with the old _view.contentIndex approach that may be due to recent changes in Ember. Basic approach would be something like:
<table>
{{! headers }}
{{#each model}}
<tr>
<td class="id-column">
{{#link-to 'post' this}}
{{id}} //<---- HOW TO ADD AN AUTO INDEX HELPER HERE (1,2,3, ETC
// AND NOT USING MODEL'S ID
{{/link-to}}
</td>
table {
counter-reset: id;
}
.id-column:before {
counter-increment: id;
content: counter(id);
}
You can use _view.contentIndex to get the current index in the loop. It is however zero-based so it would count 0, 1, 2. That could easily be fixed using a handlebar helper that simply adds 1 to the value though.
i am very new to ember js.
i am trying to form a data grid in my page.
modal
{
"tableView":{
"columns":[{"name":"EmpName","datatype":"string"},{"name":"Age","datatype":"numeric"}],
"records":[{"EmpName":"Ramesh","Age":12},{"EmpName":"Mukesh","Age":31},{"EmpName":"Mahesh","Age":22}]
}
}
template
<script type="text/x-handlebars" data-template-name="grid">
<table>
<thead>
<tr>
{{#each tableView.columns}}
<th> {{name}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each record in tableView.records}}
<tr>
{{#each column in tableView.columns}}
<td>{{record[column.name]}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</script>
In result, the table header with the columns is showing correctly.
but the data is not generated in the table and no error.
am i doing anything wrong in the inner {{#each}}??
To render the table with records you try something like,
http://emberjs.jsbin.com/nahanoje/1/edit
hbs
<table>
<thead>
<tr>
{{#each model.tableView.columns}}
<th> {{name}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each record in model.tableView.records}}
<tr>
<td>{{record.EmpName}}</td>
<td>{{record.Age}}</td>
</tr>
{{/each}}
</tbody>
</table>
In the original code posted the {{record[column.name]}} part will not work.
If you need to have a nested {{each}} helper to get dynamically the values of an arbitrary number of columns, then a registered helper for this specific task will really assist.
http://emberjs.jsbin.com/foqohibe/1/edit
js
App.IndexRoute = Ember.Route.extend({
model: function() {
var data = {
"tableView":{
"columns":[{"name":"EmpName","datatype":"string"},{"name":"Age","datatype":"numeric"}],
"records":[{"EmpName":"Ramesh","Age":12},{"EmpName":"Mukesh","Age":31},{"EmpName":"Mahesh","Age":22}]
}
};
return data;
}
});
Ember.Handlebars.helper('getRecord', function(value, options) {
var record = arguments[0];
var columnName = arguments[1];
return new Handlebars.SafeString(record[columnName]);
});
hbs
<table>
<thead>
<tr>
{{#each model.tableView.columns}}
<th> {{name}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each record in model.tableView.records}}
<tr>
{{#each column in model.tableView.columns}}
{{#with column}}
<td>{{getRecord record name}}</td>
{{/with}}
{{/each}}
</tr>
{{/each}}
</tbody>
</table>