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>
Related
I have an array of object, which I am showing in table through ng-repeat.
<table>
<thead>
<tr>
<th ng-repeat="col in columnHeaders">{{col}}</th> //['Name', 'Bank Name','Code', 'Type','Status'];
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data track by $index">
<td ng-repeat="col in columnRows">{{row[col]}}</td> //['name', 'bankName','code', 'type','isActive'];
</tr>
</tbody>
</table>
I am using ng-repeat in <th></th> and <td></td> too. But my columnHeaders name and row property(columnRows) names are different. I want to change my property name to same as column header name while using ng-repeat on <td></td> tag.
I am thought of using alias 'as' but not sure how to use it for each element.
Can anyone help me?
Instead of using two columnRows and header rows(array of string) , make a single array of keyHash(column data key and header string )
check running fiddle for this
and code be like :-
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<table>
<thead>
<tr>
<th ng-repeat="col in columnRows">{{col.displayStr}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data track by $index">
<td ng-repeat="col in columnRows">{{row[col.key]}}</td>
</tr>
</tbody>
</table>
<script>
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.columnRows = [
{key:'name',displayStr:'Name'},
{key:'bankName',displayStr:'Bank Name'},
{key:'code',displayStr:'Code'},
{key:'type',displayStr:'Type'},
{key:'isActive',displayStr:'Status'}
]
$scope.data = [
{
name:'James',
bankName:'RBL',
code:'1234',
type:'Saving',
isActive:true
},
{
name:'Riyan',
bankName:'DSB',
code:'1234',
type:'Current',
isActive:true
}
];
});
</script>
</body>
</html>
The code below is my latest attempt. There have been many others:
Javascript:
...
var issueArray = [];
_.each(issueGroups, function(i) {
var x = {
issue: i[0].issue,
comment: i[0].comment,
count: i.length,
new_row: 1
};
issueArray.push(x);
});
issueArray[0].new_row = 0;
var x = {
test: t[0].test,
issues: issueArray,
rowspan: issueArray.length
};
finalResult.push(x);
});
return finalResult;
The important thing to note here is that for each element of finalResult there is an array called issues that has a member called new_row which is true for all but the first row.
Template:
...
<tbody>
{{#each failuresByTest}}
<tr>
<td rowspan="{{rowspan}}">{{test}}</td>
{{#each issues}}
{{#if new_row}}
</tr>
<tr>
<td>{{issue}}</td>
<td>{{comment}}</td>
<td>{{count}}</td>
{{else}}
<td>{{issue}}</td>
<td>{{comment}}</td>
<td>{{count}}</td>
{{/if}}
{{/each}}
</tr>
{{/each}}
</tbody>
...
My data is such that the first element of finalResult has 3 issues. I expect it to look like this:
<tr>
<td rowspan="3">Test1</td>
<td>123</td>
<td>Bug 1</td>
<td>2</td>
</tr>
<tr>
<td>456</td>
<td>Bug 2</td>
<td>21</td>
</tr>
<tr>
<td>654</td>
<td>Bug 4</td>
<td>3</td>
</tr>
But it ends up looking like this:
<tr>
<td rowspan="3">Test1</td>
<td>123</td>
<td>Bug 1</td>
<td>2</td>
<td>
<table>
<tr>
<td>456</td>
<td>Bug 2</td>
<td>21</td>
</tr>
<tr>
<td>654</td>
<td>Bug 4</td>
<td>3</td>
</tr>
</table>
</td>
</tr>
However Meteor doesn't like this. I get:
=> Errors prevented startup:
While processing files with templating-compiler (for target web.browser):
client/templates/runs/run_page.html:128: Unexpected HTML close tag
</tr> <tr> <td><a h...
This side-tracked me so badly that I ended up putting the row tags wrongly to fix this and then it displayed wrongly. I have now edited the question accordingly since this is my real problem.
How do I solve this and persuade Meteor that actually I do know better than it!
Your template logic is flawed:
You are trying to insert a new <tr> inside a <td>. Since <tr> can only be contained inside tables, the browser will automatically add a table around it so that the html is valid.
<tbody>
{{#each failuresByTest}}
<tr>
<td rowspan="{{rowspan}}">{{test}}</td>
{{#each issues}}
{{#if new_row}}
<tr>
<td>
So depending on how each failureBytest should be rendered, you should either create a new table for each failure, or create a new row outside the previous row inseatd of inside it's cells.
I worked around Meteor's controlling behaviour with the following.
Javascript:
var issueArray = [];
_.each(issueGroups, function(i) {
var x = {
issue: i[0].issue,
comment: i[0].comment,
count: i.length,
new_row: 0
};
issueArray.push(x);
});
issueArray[0].new_row = 1;
_.extend(issueArray[0], {rowspan: issueArray.length});
_.extend(issueArray[0], {test: t[0].test});
var x = {
issues: issueArray
};
finalResult.push(x);
Note that the test name and rowspan have now moved into the first array element. I can probably remove new_row.
Template:
<tbody>
{{#each failuresByTest}}
{{#each issues}}
<tr>
{{#if new_row}}
<td rowspan="{{rowspan}}">{{test}}</td>
{{/if}}
<td>{{issue}}</td>
<td>{{comment}}</td>
<td>{{count}}</td>
</tr>
{{/each}}
{{/each}}
</tbody>
I really do not like putting "parent data" in the first array element. I prefer for all elements to be of exactly the same type/structure without duplicate/extra information added.
I have a simple Angular.js application that grabs tabular data from a mysql database and shows it in a simple bootstrap table. I’m using this code below to show the table column names without hardcoding them individually…
HTML:
<table class="table">
<thead>
<tr style="background:lightgrey">
<th ng-repeat="column in columns"> {{ column }} </th>
</tr>
</thead>
and in the controller I create ’$scope.columns’ with something like this…
var columnNames = function(dat) {
var columns = Object.keys(dat[0]).filter(function(key) {
if (dat[0].hasOwnProperty(key) && typeof key == 'string') {
return key;
}
});
return columns;
};
DataFactory.getTables(function(data) {
$scope.columns = columnNames(data);
$scope.tables = data;
});
And this works as expected and it’s great, but what about the the rest of the data.. So for example, the body of my table currently looks like this…
HTML:
<tbody>
<tr ng-repeat="x in tables ">
<td> {{ x.id}} </td>
<td> {{ x.name }} </td>
<td> {{ x.email }} </td>
<td> {{ x.company }} </td>
</tbody>
I’ve tried using two loops like this…
HTML:
<tbody>
<tr ng-repeat="x in tables">
<td ng-repeat=“column in columns”> {{ x.column }} </td>
</tr>
</tbody>
But this code doesn’t work, So is it possible to populate a table with angular without hardcoding the column names in HTML, and if so whats the most efficient way to do so?
You might want to try this https://jsfiddle.net/8w2sbs6L/.
<div data-ng-app="APP">
<table ng-controller="myController" border=1>
<thead>
<tr>
<td ng-repeat="column in columns">{{column}}</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in tables">
<td ng-repeat="column in columns">{{x[column]}}</td>
</tr>
</tbody>
</table>
</div>
<script>
'use strict';
angular.module('APP', [])
.controller('myController', ['$scope', function($scope){
$scope.tables = [
{
"column1":"row1-column1",
"column2":"row1-column2",
"column3":"row1-column3",
"column4":"row1-column4"
},
{
"column1":"row2-column1",
"column2":"row2-column2",
"column3":"row2-column3",
"column4":"row2-column4"
}
];
$scope.columns = [
"column1",
"column2",
"column3",
"column4"
];
}]);
</script>
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.
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.