Using ng-repeat with ng-include to create a recursive table - angularjs

I've done a bit of research around and I cannot seem to find the best pattern to use ng-repeat recursively to create a deep table. There is some noteworthy mentions here by user mgdelmonte regarding the subject, but alas, no solution.
The HTML and template:
<body ng-app="myApp">
<div ng-controller="myAppController">
<script type="text/ng-template" id="tree_item.html">
<tr style="width:100%">
<td>{{data.name}}</td>
</tr>
<div ng-repeat="data in data.nodes" ng-include="'tree_item.html'">
</div>
</script>
<table class="table table-striped">
<thead>
<tr>
<th style="width:30px;">Test</th>
</tr>
</thead>
<tbody ng-repeat="data in treeData" ng-include="'tree_item.html'">
</tbody>
</table>
</div>
</body>
The JS:
angular.module('myApp', []);
function myAppController($scope) {
$scope.treeData = [{
"name": "Root",
"nodes": [{
"name": "Level 1",
"nodes": [{
"name": "Level 2"
}]
}]
}];
}
Here is my jsfiddle: http://jsfiddle.net/8f3rL/86/
This is the absolute farthest I got with it. It traverses the tree fine, but I'm losing the <tr> tags when it recurses down. Instead it's rendering <span>s.
My intuition is telling me that when the ng-repeat runs on that <div>, it is creating a separate scope for those trs and tds, which I guess are illegal (since they have to be bound to a table element).
Changing the div to a table or tbody or tr is no-op as well (not that I'd even want to nest a table in like that).

The browser doesn't like the multiple tbodys and the div tags between the rows. Try this...
<body ng-app="myApp">
<div ng-controller="myAppController">
<script type="text/ng-template" id="tree_item.html">
<td>{{data.name}}
<table style="margin-left: 20px">
<tr ng-repeat="data in data.nodes" ng-include="'tree_item.html'"></tr>
</table>
</td>
</script>
<table class="table table-striped">
<thead>
<tr>
<th style="width:30px;">Test</th>
</tr>
</thead>
<tbody>
<tr style="width:100%" ng-repeat="data in treeData" ng-include="'tree_item.html'"></tr>
</tbody>
</table>
</div>
Live Demo - Fiddle

Related

cannot get a current page in the smart table tbody

My smart table look like :
<table st-safe-src="input.inputBuckets" st-table="inputBuckets"
class="table">
<thead>
<tr>
<th>Timestamp</th>
<th ng-repeat="row in input.inputSeriesPrinted">{{row}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in inputBuckets track by $index">
<td>{{row.key | date:'yyyy-MM-dd HH:mm:ss'}}</td>
//current page is undefined
<td ng-repeat="item in input.inputData" >{{item[$parent.$index] + (currentPage-1)*10]}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="10" class="text-center">
<div st-pagination="" st-items-by-page="10" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
So, because I have ng-repeat inside ng-repeat and use parent index, I need a current page for a new set of items. Without that (only item[$parent.$index]) displays same data for each page. currentPageis undefined. Any suggestion how can I get currentPage inside mine tbody?
EDIT: Codepen example. Here is my example of the problem with printed indexes of out ng-repeat and inner ng-repeat.
Your question is incomplete as you haven't showen us your controller side. So, from what I understood is the answer below.
CurrentPage is Undefined
You haven't defined it in your controller or your data value is undefined.
You can define the name of the index and use it, something like this.
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.lines = [{
text: 'Page 1'
}, {
text: 'Page 2'
}];
$scope.someData = [{
text: 'Some Data 1'
}, {
text: 'some data 2'
}];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="(pageIndex, line) in lines">
<div class="preview">{{line.text}} Page Index : {{pageIndex}}</div>
<br/>
<div ng-repeat="(dataIndex, data) in someData">
{{someData[pageIndex]}}
Parent Index : {{pageIndex}}
childIndex : {{dataIndex}}
</div>
</div>
</div>
</body>
</html>

How to update the number of columns in an angular table

I want to change the number of columns on modifying a boolean variable.
Check my example (in plnkr):
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('myCtrl', function($scope, $log) {
$scope.debug = {};
$scope.fields = [{
header: "first",
hideField: !$scope.debug.flag
},
{
header: "second"
},
{
header: "third"
},
{
header: "fourth"
},
];
$scope.entries = [{
first: "hello1",
second: "hello2",
third: "hello3",
fourth: "hello4"
}, ]
$scope.myBool = true;
});
<!doctype html>
<html ng-app="ui.bootstrap.demo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-sanitize.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.5.0.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="myCtrl">
<button id="testButton" class='btn btn-danger' ng-click='debug.flag = !debug.flag'><i class="glyphicon glyphicon-hand-right"></i> refreshFields! debug.flag = {{!!debug.flag}}</button>
<hr>
<h2 class="label label-info">table 1 with property.hideField = true</h2>
<table class="table table-hover">
<tr>
<!-- Headers of list-->
<th ng-repeat="property in fields" ng-if="!property.hideField">{{property.header}}
</th>
</tr>
<tbody>
<tr ng-repeat="entry in entries">
<td ng-repeat="property in fields" ng-if="!property.hideField">
{{entry[property.header]}} {{!!debug.flag}}
</td>
</tr>
</tbody>
</table>
<h4 class="label label-info">table 2 with ng-if => property.hideField = false</h4>
<table class="table table-hover">
<tr>
<!-- Headers of list-->
<th ng-repeat="property in fields" ng-if="property.hideField">{{property.header}}
</th>
</tr>
<tbody>
<tr ng-repeat="entry in entries">
<td ng-repeat="property in fields" ng-if="property.hideField">
{{entry[property.header]}} {{!!debug.flag}}
</td>
</tr>
</tbody>
</table>
<h2 class="label label-info">table 3 without ng-if</h2>
<table class="table table-hover">
<tr>
<!-- Headers of list-->
<th ng-repeat="property in fields">{{property.header}}
</th>
</tr>
<tbody>
<tr ng-repeat="entry in entries">
<td ng-repeat="property in fields">
{{entry[property.header]}} {{!!debug.flag}}
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
How to reproduce it:
Click on the red button and the flag will toggle from false to true.
Expected behavior:
Table 1 starts with 3 columns. It should show four after clicking.
Table 2 starts with 1 column. It should show 0 columns after clicking.
Table 3 is just a control table with all the data.
Your heading would become visible if you modified the value of its hideField property. But you're not doing that. You're modifying the value of $scope.debug.flag.
The fact that hideField was initialized with the value of $scope.debug.flag won't magically change hideField every time you change $scope.debug.flag.
Just like if you do
var a = 1;
var b = a;
a = 42;
The value of b will still be 1. Not 42.
Changing the value of $scope.debug.flag won't change the value of hideField. Because, it has already been initialized at the time of controller load. The workaround you can apply here is bind that hideField to a method and evaluate that in your ng-if. Sample below.
JS:
$scope.fields = [{
header: "first",
hideField: function() {
return !$scope.debug.flag;
}
}, {
header: "second"
}, {
header: "third"
}, {
header: "fourth"
}, ];
HTML:
<h2 class="label label-info">table 1 with property.hideField = true</h2>
<table class="table table-hover">
<tr>
<!-- Headers of list-->
<th ng-repeat="property in fields" ng-if="!property.hideField()">{{property.header}}
</th>
</tr>
<tbody>
<tr ng-repeat="entry in entries">
<td ng-repeat="property in fields" ng-if="!property.hideField()">
{{entry[property.header]}} {{!!debug.flag}}
</td>
</tr>
</tbody>
</table>
This is not a clean solution though. But, still will solve your problem.

Angular - How to update table with new values

I have a table with ng-repeat:
<table>
<tr>
<thead>
<th>key</th>
<th>value</th>
</thead>
</tr>
<tbody ng-repeat = "i in data">
<td>{{i.key}}</td>
<td>{{i.value}}</td>
</tbody>
</table>
The table is populated with data-ng-init, when the user clicks on a button:
<button ng-click="getNewVals()">new values</button>
I get json with new key-value, my question is how to repopulate the table with the new values baes on the key? is there a angular way to do so?
ng-repeat is bound to the model you are iterating over. If you update the model, ng-repeat will re-draw. In your example, whenever $scope.data changes, the ng-repeat will update itself.
Use this way. It's good if you provide your JSON.
<table>
<tr>
<thead>
<th>key</th>
<th>value</th>
</thead>
</tr>
<tbody>
<tr ng-repeat = "(key, val) in data">
<td>{{key}}</td>
<td>{{val}}</td>
</tr>
</tbody>
</table>
Look at the following example:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat="x in persons">{{x.name}}, {{x.age}}</li>
</ul>
<button ng-click="changeIt()">Change json</button>
<script>
//1 module declaration
var app = angular.module('myApp', []);
//2 controller declaration
app.controller('myCtrl',function($scope){
$scope.persons = [
{name:"Peter", "age": 23},
{name:"Lina","age":26},
{name:"Robert", "age":33}
];
$scope.changeIt = function(){
$scope.persons = [
{name:"Larry",age: 59},
{name:"Joseph", age: 63},
{name:"Clara", age:71}
];
}
});
</script>
</body>
</html>
Hope it solves your problem.
you writing ng-reapet instead of ng-repeat
try this.
<table>
<tr>
<thead>
<th>key</th>
<th>value</th>
</thead>
</tr>
<tbody ng-repeat = "i in data">
<td>{{i.key}}</td>
<td>{{i.value}}</td>
</tbody>
</table>
<button ng-click="data.push({'key':1,'value':2})">new values</button>

Trying to setup sort on a table using smart-table in angularjs

Using angularjs with smart-table. Table headers are drawn but no data rows. Without smart-table data shows up fine. Any ideas?
error in console ..
"Error: [$compile:ctreq] http://errors.angularjs.org/1.4.7/$compile/ctreq?p0=stTable&p1=stSort
dataset ..
[{
"name": "Robert B",
"associatedNumber": 21,
"standing": true
}, {
"name": "Martin C",
"associatedNumber": 55,
"standing": true
}, {
"name": "Bobby B",
"associatedNumber": 15,
"standing": true
}]
json is returned by an api, /players ..
var app = angular.module('lmsHome', ['smart-table']);
app.controller('lmsHomeCtrl', function ($scope, $http) {
$http.get("/players").success(function (response) {
$scope.players = response;
});
});
html ..
<div ng-app="lmsHome" ng-controller="lmsHomeCtrl">
<table class="table table-hover">
<thead>
<tr>
<th st-sort="name">Player</th>
<th st-sort="associatedNumber">Number</th>
<th st-sort="standing">Still standing?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="player in players" ng-class="{'success': player.standing}">
<td ng-cloak>
<span ng-show="player.standing">
{{player.name}}
</span>
<span ng-show="!player.standing">
<strike>{{player.name}}</strike>
</span>
</td>
<td ng-cloak>{{player.associatedNumber}}</td>
<td ng-cloak>
<span class="label">{{player.standing}}</span>
</td>
</tr>
</tbody>
</table>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="/jquery/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="/bootstrap/js/bootstrap.min.js"></script>
<!-- Angularjs -->
<script src="/angular/angular.min.js"></script>
<!-- A table/grid for Angularjs -->
<script src="/table/smart-table.min.js"></script>
<!-- Custom logic -->
<script src="/js/homeLogic.js"></script>
According to the documentation for Smart Table, in order to use Smart Table,
The first thing is to add the module angular.module('myApp',['smart-table'] to your angular application. Then use the markup you would normally do for html tables. On the table element add the st-table attribute to tell smart-table which collection holds your displayed data, (ie the one you are going to use in the repeater). Now you will be able to compose the table using the plugins.
So it appears as you are missing the st-table attribute.
<table class="table table-hover" st-table="players">
<thead>
<tr>
<th st-sort="name">Player</th>
<th st-sort="associatedNumber">Number</th>
<th st-sort="standing">Still standing?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="player in players" ng-class="{'success': player.standing}">
...
</tr>
</tbody>
</table>

How to conditionally enter column markup based on ng-repeat table?

What I'm trying to do is to conditionally change what is displayed within a td column block, based on some value of my ng-repeat's iterated object value. More specifically, if you look at the code below, what I want to do, is based on some value of data.col2val, I want to change what is displayed in column2 for that row.
I know that there is no such thing as a ng-if-else, but if there was, my code might look something like this:
<table class="table" id="myTable">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody ng-repeat="data in dataStore">
<tr>
<td>{{data.col1val}}</td>
<td ng-if="{{data.col2val}}='something'" style="somestyle">something</td>
<td ng-if-else="{{data.col2val}}='somethingelse'" style="someotherstyle">somethingelse</td>
</tr>
</tbody>
</table>
Maybe there is a trivial way to do this, but I can't seem to figure/google it out. I know how do to this what ASP.NET/Razor but not with Angular, so any assistance is appreciated and pardon my ignorance.
ng-if="data.col2val=='something'"
you don't have tu use curly braces inside ng-if directive
var app = angular.module('app', []);
app.controller('firstCtrl', function($scope){
$scope.dataStore = [
{col1val:"Mike", col2val:"something"},
{col1val:"Mike", col2val:"somethingelse"},
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
<table class="table" id="myTable">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody ng-repeat="data in dataStore">
<tr>
<td>{{data.col1val}}</td>
<td ng-if="data.col2val=='something'" style="somestyle">something</td>
<td ng-if="data.col2val=='somethingelse'" style="someotherstyle">somethingelse</td>
</tr>
</tbody>
</table>
</div>
</body>

Resources