So I have a handlebars helper that is very simple -
Handlebars.registerHelper('selectRow', (rowIndex, selectedIds) ->
console.log 'row index'
console.log rowIndex
console.log selectedIds
isSelected = _.indexOf(rowIndex, selectedIds)
if isSelected > -1
return 'class="row-selected"'
)
and I have this handlebars code -
<div class="title">{{ title }}</div>
<hr/>
<table cellspacing="0" cellpadding="0">
<thead>
{{#each columns}}
<th class="col-heading" data-heading="{{ this }}">{{ this }}</th>
{{/each}}
</thead>
<tbody>
{{#each rows}}
<tr {{#selectRow #index selected }}>
{{#each this}}
<td>
{{this}}
</td>
{{/each}}
</tr>
{{/selectRow}}
{{/each}}
</tbody>
</table>
the selected parameter is always undefined. If I add a {{ selected }} anywhere else, it shows an array, which, as you can see from below, it should -
data = #model.data()
selected = #model.get('selection').get('selected')
#$el.html(#tableContentsTemplate({
columns: #model.get('columns')
rows: data
title : #model.get('title')
selected: JSON.stringify(selected)
}))
How do I pass in the selected parameter correctly to my helper?
You're using the selected variable inside a Handlebars each loop, so I imagine selected is not in scope.
I think you're just a bit confused about how _.indexOf works. From the fine manual:
indexOf _.indexOf(array, value, [isSorted])
Returns the index at which value can be found in the array, or -1 if value is not present in the array.
So the array you're searching through, selectedIds, should be the first argument and the element you're searching for is the second. Perhaps you mean to say:
isSelected = _.indexOf(selectedIds, rowIndex)
or better (IMO):
isSelected = _(selectedIds).indexOf(rowIndex)
I generally find that using the _() function yields clearer code.
Also, shouldn't {{#selectRow #index selected }} be {{selectRow #index selected}}? A leading # should introduce a block but your helper is not written as a block helper.
Once both of the above issues have been fixed, sensible things seem to be happening for me: http://jsfiddle.net/ambiguous/pkVZc/1/
Related
I am trying to create a dynamic table with handlebars.js.
An example of an object I want to put in the table is:
Object { title: "The Room", director: "Tommy Wiseau", genre: "Drama?", rating: "1"}
The HTML I would like to look like:
<thead>
<th>Title</th>
<th>Director</th>
<th>Genre</th>
<th>Rating</th>
<th>Edited</th>
</thead>
<tbody>
<tr>
<td>The Room</td>
<td>Tommy Wiseau</td>
<td>Drama?</td>
<td>1</td>
<td>2017-05-16</td>
</tr>
</tbody>
My problem is that the user can also add their own table headers, so i can't for example just do:
<td>{{title}}</td>
It has to be done dynamically. I have looked at: {{#each}}, but just can't seem to wrap my head around around it. Also, how can I print the property name instead of just the value in an object (like I want in the table headers).
<thead>
{{#each data}}
<th>{{#key}}</th><!--property name here-->
{{/each}}
</thead>
<tbody>
{{#each data}}
<tr>
<td>{{this}}</td><!--property value here-->
</tr>
{{/each}}
</tbody>
This is my template. I feel like I am closing in on the right solution, but I am not sure where I am doing something wrong.
$.getJSON("php/loadItems.php?category=" + selected, function(data) {
let source = $("#itemTemplate").html();
let template = Handlebars.compile(source);
delete data[0].id
$("#tableContainer").append(template(data));
});
This is how I handle the data in my JS. Any help is greatly appreciated!
Based on what you are describing, I think you would have no option other than to build an array of table headers (object keys) by iterating over every object in your array and filtering out all of the duplicates. In other words, you must build an array of all of the unique keys from all of your data objects and these will serve as the column headings in your table. You could use an existing library to help yo do this, like Underscore's .uniq, but I will provide an example implementation:
var uniqueKeys = data.reduce(function (acc, obj) {
return acc.concat(Object.keys(obj).filter(key => acc.indexOf(key) === -1));
}, []);
Next, we must pass our array of unique keys to our template. The unique keys will be used to create the table headings as well as in lookup operations for each object in data.
template({
uniqueKeys: uniqueKeys,
data: data
});
Our template must be updated to the following:
<thead>
<tr>
{{#each uniqueKeys}}
<th>{{this}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each data}}
<tr>
{{#each ../uniqueKeys}}
<td>{{lookup .. this}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
Note that the line {{lookup .. this}} is saying, "render the value on the current object in data at the property with the name of this key in uniqueKeys.
Also note that I added <tr> tags within your <thead>. According to MDN, these are the only permissible tags within a <thead>.
I have created a fiddle for reference.
Would something like this work for what you're trying to accomplish?
{
"titles": [
"The Room",
"Test"
],
"tableObjs": [{
"director": "Tommy Wiseau",
"genre": "Drama?",
"rating": "1"
},
{
"director": "Tommy Wiseau",
"genre": "Drama?",
"rating": "1"
}
]
}
<thead>
{{#each titles}}
<th>{{#key}}</th>
<!--property name here-->
{{/each}}
</thead>
<tbody>
{{#each tableObjs}}
<tr>
<td>{{this.director}}</td>
<td>{{this.genre}}</td>
<td>{{this.rating}}</td>
<!--property value here-->
</tr>
{{/each}}
</tbody>
I have following lines of code in my AngularJS project.
<div ng-show="update">
<table border="1">
<tr ng-repeat="x in names">
<td>{{ x.uname}}</td>
<td>{{ x.upass}}</td>
<td><button ng-model="index" ng-click="show()"
ng-value="{{x.index}}">Edit</button></td>
</tr>
</table>
When Particular button is clicked I want to retrieve that button label.
For that I have written code in Show function:
var v = $scope.index;
alert(v);
But alert box is displaying "Undefined" when i click on button.
Please suggest me where I am wrong??
Thanks in advance
What about providing your value as an argument to the onclick function ?
<div ng-show="update">
<table border="1">
<tr ng-repeat="x in names">
<td>{{ x.uname}}</td>
<td>{{ x.upass}}</td>
<td><button ng-click="show(x.index)">Edit</button></td>
</tr>
</table>
and then
function show(id){
alert(id);
}
This should works. Also ng-model does not work on button.
under ng-click = show(item) - this gives the particular item object.
function show(obj){ console.log(obj)}
this function give the particular clicked obj from view.
ng-model doesn't work on button.
You can pass the index of the repeat iteration to the show method like this:
<button ng-click="show($index)" ...
and use it like this:
function show(i){
var v = names[i];
alert(v);
}
You can see it in action here: PLNKR DEMO
NOTE: if you are passing the index to the backend service the index may not match up with the actual array if you are using a filter in your view to alter/reduce the ng-repeat expression
Another option as pointed out by #Grundy is to just pass the object back, which saves you the step of having to access it from the array... (note: this is the suggested best practice)
I am assuming that you want the button label that you have clicked on and the index of it in the list provided. Here is what you can do.
<div ng-repeat="buttonId in testdata2">
<button ng-bind="buttonId" ng-click="clickedMe(buttonId, $index)"></button>
</div>
$scope.clickedMe = function(buttonId, index) {
console.log(buttonId + " - " + index);
};
Have your files in local and run them.
I have a table using angularjs where I want to loop through an array to print specific headers from a json object. The header prints out fine, but the problem comes when I try to use a variable from my nested ng-repeat as a json selector. If you replace the inner ng-repeat with the commented section below it, it will work.
Table:
<table>
<thead>
<th ng-repeat="column in tableHeader">{{column}} <a ng-click="sort_by(column);"><i class="glyphicon glyphicon-sort"></i></a></th>
</thead>
<tbody>
<tr ng-repeat="data in filtered>
<td ng-repeat="column2 in tableHeader">{{data.column2}}</td>
<!-- <td>{{data.Environment}}</td>
<td>{{data.HostIP}}</td>
<td>{{data.ServiceName}}</td>
<td>{{data.Status}}</td>
<td>{{data.StartTime}}</td>
<td>{{data.Capacity}}</td>
<td>{{data.Txn}}</td>
<td>{{data.Errors}}</td>
<td>{{data.Build}}</td>
<td>{{data.Project}}</td>
<td>{{data.Author}}</td>
<td>{{data.ModifyDate}}</td>
<td>{{data.Port}}</td>
<td>{{data.BasePath}}</td> -->
</tr>
</tbody>
</table>
Array located in controller:
$scope.tableHeader = ['Environment', 'HostIP', 'Status', 'ServiceName', 'StartTime', 'Capacity', 'Txn', 'Errors', 'Build', 'Project', 'Author', 'ModifyDate', 'Port', 'BasePath'];
I think you're looking for {{data[column2]}}. Since column2 is just the string value of the property you want, treat data like an associative array in this case to get the property you're trying to display.
column2 was created by the ng-repeat and is what you want. Note {{column2}}:
<td ng-repeat="column2 in tableHeader">{{column2}}</td>
To elaborate on the title, what I am trying to achieve is the following.
I am building an interactive table component in Ember. Here is the stripped template:
<table>
<thead>
<tr>
{{#each header in headers}}
<th>{{header}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each row in rows}}
<tr>
{{#each header in headers}}
<td>{{input value=row.[header]}}</td> <!-- can I bind to row.header here somehow? -->
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
I want each input field for a specific row and specific column, to be bound to that row's row object, specifically to a property named the way the header column is named.
Essentially, I want to use the value of the header variable to bind a property in the row object called by that value (If current header has value 'title' then I want to bind to row.title)
Here is an example of how I initialize these objects:
var headers = ['title','description'];
var rows = [],
row = {};
for(var i = 0; i < headers.length; i++) {
row[headers[i]] = ''; // this line does similar thing to what I am trying to achieve
}
rows.push(row);
/* This gives
rows = [
{
title: '',
description: ''
}
]
*/
After researching, I found this in the Handlebars documentation that says I can access properties like this:
{{#each articles.[10].[#comments]}}
...
{{/each}}
Which is, according to the docs, pretty much the same as:
articles[10]['#comments']
However, using:
rows.[header]
doesn't work for me because it tries to literally access the 'header' property of the rows object (i.e. rows.header) and not the value contained in the header variable.
You can always extend the textfield component and at least get the value you are looking for.
App.DynamicInputComponent = Ember.TextField.extend({
row: null,
col: null,
value: function(){
var row = this.get('row');
var col = this.get('col');
return row[col];
}.property('row', 'col')
});
Then, in your template you can do:
<table>
{{#each item in model}}
<tr>
{{#each col in columns}}
<td> {{ dynamic-input type='text' row=item col=col }} </td>
{{/each}}
</tr>
{{/each}}
</table>
(Partially) working solution here
This functionality can now easily be achived in Ember (since 1.13) using the new and awesome inline helpers mut and get:
The way to achieve this is in two basic steps:
Use get to dynamically lookup a property from r named as whatever the value of h is at that each iteration. For example, if h = 'title', then this would return r['title'].
Use mut to specify that this extracted value is mutable by our input component (specifically its value property).
This is how the whole each looks:
{{#each rows as |r|}}
<tr>
{{#each headers as |h|}}
<td>
<input onkeyup={{action (mut (get r h)) value="target.value" }}>
</td>
{{/each}}
</tr>
{{/each}}
Detailed example on Ember Twiddle
In my JsRender template, I want to filter the json object to only render records that meet certain criteria--a parent id, for example, which is a variable that is initialized immediately before executing the render.
What I'd like to do is below, but the second line of syntax is just a guess. How to do this?
<script id="tmpl_report_entry_table_data_rows" type="text/x-jsrender">
{{if ENTRY_ID==n_current_entry_id_from_external_variable}}
<tr class="attribute_data_row">
<td class="entry_id attribute_data"><span>{{:ENTRY_ID}}</span></td>
<td class="attribute_1 attribute_data hidden"><span>{{:ATTRIBUTE__1}}</span></td>
<td class="attribute_2 attribute_data"><span>{{:ATTRIBUTE__2}}</span></td>
<td class="attribute_14 attribute_data"><span>{{:ATTRIBUTE__14}}</span></td>
<td class="attribute_13 attribute_data"><span>{{:ATTRIBUTE__13}}</span></td>
<td class="attribute_11 attribute_date attribute_data"><span>{{:ATTRIBUTE__11}}</span></td>
<td class="attribute_11 attribute_date_hidden"><span>{{:ATTRIBUTE__11}}</span></td>
<td class="attribute_3 attribute_data"><span>{{:ATTRIBUTE__3}}</span></td>
<td class="attribute_4 attribute_data"><span>{{:ATTRIBUTE__4}}</span></td>
<td class="attribute_5 attribute_data">
<span>{{:ATTRIBUTE__5}}</span>
</td>
<td class="cmd"></td>
</tr>
{{/if}}
</script>
<script>
var obj_my_data = [
{"ENTRY_ID":79,
"test":true,
"ATTRIBUTE__1":"Aleutian Islands",
"ATTRIBUTE__2":"Arrowtooth Flounder",
"ATTRIBUTE__13":"BSAI trawl limited access",
"ATTRIBUTE__3":"Open",
"ATTRIBUTE__4":"TAC",
"ATTRIBUTE__5":"",
"ATTRIBUTE__11":",",
"ATTRIBUTE__14":"Entire GOA"},
{"ENTRY_ID":80,
"test":true,
"ATTRIBUTE__1":"Aleutian Islands",
"ATTRIBUTE__2":"Atka Macherel",
"ATTRIBUTE__13":"BSAI trawl limited access",
"ATTRIBUTE__3":"Open",
"ATTRIBUTE__4":"TAC",
"ATTRIBUTE__5":"",
"ATTRIBUTE__11":",",
"ATTRIBUTE__14":"Entire GOA"}
];
$(document).ready(function(){
$("table tbody").append($("#my_template").render(obj_my_data));
});
</script>
While you could modify your data to carry your current row ID, it is generally as easy (and cleaner) to 'parameterize' the template, by passing in parameters with the render method. You can do that by passing in an additional context parameter. It can carry both parameters and helper functions that you can pass in dynamically just for this template rendering...
$("#my_template").render(myData, {currentRowId: myCurrIdVar}));
Then you can access those named parameters from within the template (or within nested templates, too) in the same way as you would access a registered helper - by appending '~' to the name.
{{if ENTRY_ID==~currentRowId}}
...
{{/if}}
I have added a new sample demo on GitHub showing that.
You could just assign the current row value to a property of the data object...
$(document).ready(function(){
obj_my_data.currentRow = n_current_entry_id_from_external_variable;
$("table tbody").append($("#my_template").render(obj_my_data));
});
Your template can then check against this property.
<script id="tmpl_report_entry_table_data_rows" type="text/x-jsrender">
{{if ENTRY_ID == currentRow}}
// remaining template.