jsRender for in for, but for 2 different structures - javascript

Hi I have these 2 different things:
columns = {
name:"Name",
ref:"Reference"
}
items:[
{ id:1, name:"Dan", ref:"01" },
{ id:2, name:"Dan2", ref:"02" }]
And I need to put them in a table like:
| Name | Reference |
| Dan | 01 |
| Dan2 | 02 |
normally this would be:
{{for columns}}
{{:name}} {{:ref}}<br>
{{/for}}
{{for items}}
{{:name}} {{:ref}}<br>
{{/for}}
However, my keys in the 2 objects are dynamic, so I want to so something that in javascript would be:
var header = "";
for(key in Object.keys(columns){
header+=" "+columns[key));
}
console.log(header);
for(var i=0;i<items.length;i++){
var item = items[i];
var line ="";
for(key in Object.keys(columns){
line+=" "+items[key);
}
console.log(line);
}
Can anyone suggest anything?

You can do it like this:
<script id="tmpl" type="text/x-jsrender">
<table><thead><tr>
{{props columns}}
<th>{{:prop}}</th>
{{/props}}
</tr></thead>
<tbody>
{{for items}}
<tr>
{{props ~root.columns ~item=#data}}
<td>{{:~item[key]}}</td>
{{/props}}
</tr>
{{/for}}
</tbody></table>
</script>
{{props}} iterates over the properties of an object - and within the {{props}} block, key is the property (field) name and prop is the value. (See http://www.jsviews.com/#propstag).
~item=#data defines a variable (a 'helper property') you can access from within the nested blocks, to easily get to 'parent data' from there. (See http://www.jsviews.com/#samples/jsr/paths).
The above assumes using the following data:
var data = {
columns: {
name:"Name",
ref:"Reference"
},
items:[
{ id:1, name:"Dan", ref:"01" },
{ id:2, name:"Dan2", ref:"02" }
]
}
$("#result").html(tmpl.render(data));

Related

ng-repeat: Identify Change of Element Value In Sorted List

I'm creating a table using ng-repeat. This table has a column called Season which is obviously populated by {{episode.season}}. The source data is sorted by season.
I want to add a class to the last <tr> of each season, i.e. when there is a change of season, e.g.
--------------------------
EPISODE 1 | SEASON 1
--------------------------
EPISODE 2 | SEASON 1
--------------------------
EPISODE 3 | SEASON 1 <- THIS ROW SHOULD HAVE DIFFERENT FORMATTING
--------------------------
EPISODE 1 | SEASON 2
--------------------------
Using vanilla JavaScript, I'd iterate through the table whilst comparing each season <td> with the next, and adding a class if there is a difference.
However, I don't know how to do this using angularjs. Any help is appreciated.
SOLUTION
I decided to identify the last episode of each season by looping through the dataset on the server side, setting an approprioate boolean value for this, and then using ng-class on the UI. It seems simplier and neater than looping through the rendered HTML table.
You can use $last of ng-repeat and ng-class to style the last episode of each season
Data
$scope.shows = [{
"season" : "season 1",
"episodes" :["episode 1","episode 2","episode 3"]
},
{
"season" : "season 2",
"episodes" : ["episode 1","episode 2","episode 3"]
},
{
"season" : "season 3",
"episodes" : ["episode 1","episode 2","episode 3"]
}
];
<tbody ng-repeat="show in shows">
<tr ng-class="$last ? 'endSeason' : ''" ng-repeat="episode in show.episodes">
<td>{{show.season}}</td>
<td>{{episode}}</td>
</tr>
</tbody>
FULL EXAMPLE
You can use groupBy to group all series by seasons and then iterate through out each individual season and mark $last series with desired class(final-of-type):
(function(angular) {
'use strict';
var myApp = angular.module('app', ['angular.filter']);
myApp.controller('MyController', ['$scope', function($scope) {
$scope.episodes = [
{season:'1',episode:'2'},
{season:'2',episode:'1'},
{season:'1',episode:'1'},
{season:'1',episode:'3'},
{season:'2',episode:'2'},
{season:'0',episode:'1'}
]
}]);
})(window.angular);
.final-of-type {
background-color:grey;
}
td, th {
border: 1px solid #dddddd;
}
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script data-require="angular-filter#*" data-semver="0.5.7" src="//cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.7/angular-filter.js"></script>
<body ng-app="app">
<div ng-controller="MyController">
<table>
<thead>
<tr>
<th>Season</th>
<th>Episode</th>
</tr>
</thead>
<tbody ng-repeat="(key, value) in ::episodes | groupBy: 'season' | orderBy: 'key'">
<tr ng-repeat="episode in ::value | orderBy: 'episode'" ng-class="{'final-of-type': $last}">
<td>{{::episode.season}}</td>
<td>{{::episode.episode}}</td>
</tr>
</tbody>
</table>
</div>
</body>
Assuming bind to controller syntax.
<div ng-repeat="episode in $ctrl.episodes" ng-class="'final-of-type': $ctrl.isDifferent($index);'"></div>
Then in your directive:
this.episodes = data; // not sure how you're getting this
this.isDifferent = function(i) {
return (i === episodes.length - 1) || (episodes[i].season !== episodes[i + 1].season)
}
That should do it or you.

How to set index values to child rows in angular js

I have a requirement where I need to add index values for child rows. I have Group rows under which there will be child rows. I am ng-repeat and I am using $index for child's as shown:
HTML code:
<table ng-repeat="node in data">
<tr> <td> {{node.groupName}} </td> </tr>
<tbody ng-model="node.nodes">
<tr ng-repeat="node in node.nodes"> <td> {{$index}} </td> </tr>
</table>
But it is displaying as shown:
But I want it to display as shown:
I am new to Angular JS and not getting how to display it like this. How am I supposed to do that. Please help.
As far as I understood your question, you'd like to have something like that:
<table ng-repeat="group in data">
<thead>
<th> {{group.name}} </th>
</thead>
<tbody ng-repeat="item in group.items">
<tr>
<td>{{getIndex($parent.$index - 1, $index)}} | {{item}} </td>
</tr>
</tbody>
</table>
$scope.data = [
{name: 'Group1', items: ['a','b']},
{name: 'Group2', items: [1,2,3]},
{name: 'Group3', items: ['x', 'xx', 'xxx', 'xxxx']}
];
$scope.getIndex = function(previousGroupIndex, currentItemIndex){
if(previousGroupIndex >= 0){
var previousGroupLength = getPreviousItemsLength(previousGroupIndex);
return previousGroupLength + currentItemIndex;
}
return currentItemIndex;
};
function getPreviousItemsLength(currentIndex){
var length = 0;
for (var i = 0; i <= currentIndex; i++){
length += $scope.data[i].items.length;
}
return length;
// or even better to use Array.prototype.reduce() for that purpose
// it would be shorter and beautiful
// return $scope.data.reduce(function(previousValue, currentGroup, index){
// return index <= previousGroupIndex ? previousValue + currentGroup.items.length : previousValue;
// }, 0);
}
You need to play with $parent.$index property and use some math :) in order to achieve that.
It would look like the following:
Check out this JSFiddle to see live example.

How to show more or less rows in a table by ng-click in angularjs with key, value pairs

I've got json where each parameter of the object has over 40 key, val pairs. All data is put into a html table on a page
I want user to be able to change a number of rows on a html page.
Firstly, to reduce number of rows into the table I wrote ng-if statement. I added ng-if because LimitTo doesn't work with key, val pairs. And actually I'm stucking here. The main problem in key, val pairs for me.
How can I add more/less buttons that helps user hide or show more rows when a json file contains key,val pairs.
My html
<table class="table ng-cloak" ng-if='$index<10' ng-repeat="shop in shops | filter:isActive">
<thead>
<tr>
<th>#</th>
<th>Месяц</th>
<th>Число кликов</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='(key, val) in shop.products' ng-if="$index < 10">
<td>{{ $index + 1 }}</td>
<td>{{ key }}</td>
<td>{{ val }}</td>
</tr>
</tbody>
</table>
my reduce json
[
{
"products": {
"1359568800": 74,
"1361988000": 71,
"1364666400": 83,
"1367258400": 72,
"1369936800": 78
},
"name": "moskva",
"avg_check": {
"1359568800": 6479,
"1361988000": 7375,
"1364666400": 8477,
"1367258400": 9292,
"1369936800": 8357
},
"income": {
"1359568800": 479515,
"1361988000": 523662,
"1364666400": 703601,
"1367258400": 669072,
"1369936800": 651921
}
}
]
We cannot use default filter for objects. So use custom filter
app.filter('myLimitTo', [function(){
return function(obj, limit){
var keys = Object.keys(obj);
if(keys.length < 1){
return [];
}
var ret = new Object,
count = 0;
angular.forEach(keys, function(key, arrayIndex){
if(count >= limit){
return false;
}
ret[key] = obj[key];
count++;
});
return ret;
};
Checkout the Fiddle https://jsfiddle.net/ebinmanuval/uLo0bo0u/

ng repeat array in array

I am trying to repeat a child array of a multidimensional array with ng repeat in Angular.
My json object is this:
$scope.items = [{ "id":1,
"BasisA":"1",
"Basis":true,
"personSex":"m",
"isCollapsed":false,
"name":"Mark Polos",
"age":"1955",
"results":[{"1000":{"company_name":"***","model":"***","modelname":"***","pr":222,"rating":4.5,"priority":9,"matching":1},
"1001":{"company_name":"***","model":"***","modelname":"***","pr":228.7,"rating":5.7,"priority":7,"matching":2},
"1002":{"company_name":"***","model":"***","modelname":"***","pr":241.7,"rating":1.9,"priority":4,"matching":3}
}]
}]
Itried somthing like this:
... data-ng-repeat="item in items">
And then in the table of this child:
<tr data-ng-repeat="i in item | orderBy:'insItem.pr'">
It doesn't look like that results property is actually an "array." If that's just a typo in your example, then disregard. If not ... read on.
It looks like an array with a single item, and that Item is a set of properties which are, in turn, objects. In other words, you would reference the property "pr" for the result named "1000" by with code that looks like item.results[0]["1000"].pr NOT with code that looks the way your ng-repeat is expecting(item.results[0].pr).
Can you transform your items when you get them so that results is a true array?
OR - can you use a function inside of your controller that returns the array you are looking for?
View Code:
<... data-ng-repeat="result in resultsFromItem(item)" >
Controller Code:
$scope.resultsFromItem = function (item) {
if(item==undefined || item.results==undefined || item.results.length==0) {
return [];
}
var myResults = [];
for (var key in item.results[0]) {
if(item.results[0].hasOwnProperty(key)) {
myResults.push(item.results[0][key]);
}
}
return myResults;
}
You might even decide to hang that "transformed" results object off each item object (so you only have to go through the transform one time) if you wanted to.
You should access to the results field:
... data-ng-repeat="item in items">
<tr data-ng-repeat="i in item.results">
Since the nested array is in the results property of the main object.
I used three nested ng-repeat directives to get this rolling :-) The third ng-repeat uses ng-repeat="(key, value) in result" functionality to display all result object keys and values, which I got working with the help of this answer on how to iterate over keys and values in ng-repeat. The orderBy: part isn't yet working (if someone knows how to implement that then any help is welcomed).
<ul>
<li ng-repeat="item in items">
id: {{item.id}}, name: {{item.name}}, age: {{item.age}}, results:
<table>
<tr ng-repeat="result in item.results">
<td>
<table style="border: 1px solid black;">
<tr ng-repeat="(key, value) in result | orderBy: value.pr">
<td> {{key}} </td> <td> {{ value }} </td>
</tr>
</table>
</td>
</table>
</li>
</ul>
Plunker

AngularJS : ng-repeat with uniques values in particular method

I have one object like this
$scope.listvalues = [{ name:A, id:101 },
{ name:B, id:102 },
{ name:A, id:103 },
{ name:C, id:101 },
{ name:A, id:102 },
{ name:B, id:103 }];
I need to print this object in following structure
name |101 | 102 |103 |
-----|----|-----|---------
A |YES | YES |YES |
-----|----|-----|------------
B |No | YES |YES |
-----|----|-----|-----------
C |YES | NO |NO |
Here i need to print the Name "A" in unique and also need to indicate the A is available for which Id. Is it possible to do with angularjs ng-repeat?. Any one please suggest...
You can, but you would have to write a filter that changes the structure of your data to the following:
$scope.data = [
{A: {'101': true, '102': true, '103': true}},
{B: {'101': false, ...}},
{C: ...}
]
And then you can write your table like this:
<table>
<tr>
<th>name</th>
<th ng-repeat="(column, value) in data[0]">{{column}}</th>
</tr>
<tr ng-repeat="row in data">
<td ng-repeat="(column, value) in data[0]">{{row[column] ? 'Yes' : 'No'}}</td>
</tr>
</table>
Example filter:
yourModule.filter('makeNgRepeatable', function(){
return function(oldStructure) {
// Add code here to convert oldStructure to newStructure.
return newStructure;
}
});
In your controller, inject makeNgRepeatableFilter and do
$scope.data = makeNgRepeatableFilter([{ name:A, id:101 }, ...]);
You can pack this into a table and then resolve with multiple ng-repeats which cell is YES or NO.
Take a look at this plnkr, it demonstrates how this could be achieved.
http://plnkr.co/edit/QI8ZrsbwYuJUeV4DNWGl?p=preview
First you collect all distinct ids and names.
$scope.names = $scope.listvalues.map(function(d){return d.name}).unique();
$scope.ids = $scope.listvalues.map(function(d){return d.id}).unique();
Note: In the plnkr I defined the functions unique and contains. If you use some other libraries like underscore those functions may already be present.
Then you define a function to determine if a specific cell should be true or false.
$scope.hasValue = function(name, id) {
for(var i = 0; i < $scope.listvalues.length; ++i)
if($scope.listvalues[i].name === name && $scope.listvalues[i].id === id)
return true;
return false;
}
However, it would be simpler if you can convert your listvalues into a reasonable structure. This would prevent some of the overhead.
With your array structure you might need some additional helper array/objects. In your case it could look like this:
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th ng-repeat="th in values">{{th}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="obj in names">
<td>
{{obj.name}}
<div><small>{{obj}}</small></div>
</td>
<td>{{obj.ids.indexOf(values[0]) > -1 ? 'YES' : 'NO'}}</td>
<td>{{obj.ids.indexOf(values[1]) > -1 ? 'YES' : 'NO'}}</td>
<td>{{obj.ids.indexOf(values[2]) > -1 ? 'YES' : 'NO'}}</td>
</tr>
</tbody>
</table>
where helper objects are constructed like:
function initData() {
var map = {values: {}, names: {}},
values = [],
names = [];
$scope.listvalues.forEach(function(obj) {
if (!map.values[obj.id]) {
values.push(obj.id);
map.values[obj.id] = true;
}
if (!map.names[obj.name]) {
names.push(obj);
obj.ids = [obj.id];
map.names[obj.name] = obj;
}
else {
map.names[obj.name].ids.push(obj.id);
}
});
$scope.values = values;
$scope.names = names;
}
initData();
Demo: http://plnkr.co/edit/wWJOjtzstUDKjl9V6hCy?p=preview

Categories