AngularJS does not pass data to JQuery DataTable - angularjs

I am using datatables from datatables.net and am running into some issues with the AngularJS ng-repeat and values being populated into the table. I have added a button that will pass a value into the table and this works great. However, when I try to add sorting and scroll bar to the table it stops working. I'm not sure what I'm doing wrong here.
html
<div ng-controller="TodoCtrl" id="TodoCtrl">
<table id="example" cellpadding="0" cellspacing="0" border="0" class="display table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>Bus Id</th>
<th>X Cords</th>
<th>Y Cords</th>
<th>Event Type</th>
<th>Time Stamp</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in todos"><td>{{todo.busId}}</td><td>{{todo.xCord}}</td><td>{{todo.yCord}}</td><td>{{todo.eventType}}</td><td>{{todo.timeStamp}}</td></tr>
</tbody>
</table>
<form class="form-horizontal">
<input type="text" ng-model="vbusId" ng-model-instant>
<button class="btn" ng-click="addTodo()"><i class="icon-plus"></i>Add</button>
</div>
jscript
function TodoCtrl($scope) {
$scope.todos = [];
$scope.addTodo = function (vbusId, vxCord, vyCord, vposTimeStamp, veventType) {
$scope.todos.push({busId:'vbusId', xCord:vxCord, yCord:vyCord, timeStamp:vposTimeStamp, eventType:veventType});
}
}
table script
$(document).ready(function() {
$('#example').dataTable( {
"sScrollY": "200px",
"bPaginate": false
} );
} );
If I comment out the table script the dynamic table works and gets populated with the passed data. If i uncomment the table code the table shows up with the sorting and scroll bar but it will not accept the values. Can someone tell what I am missing?
Thanks a bunch!

You need to ensure that the datatables is being called AFTER angular has digested the page. Something like this:
function TodoCtrl($scope, $timeout) {
$scope.todos = [];
$scope.addTodo = function (vbusId, vxCord, vyCord, vposTimeStamp, veventType) {
$scope.todos.push({busId:'vbusId', xCord:vxCord, yCord:vyCord, timeStamp:vposTimeStamp, eventType:veventType});
}
$timeout(function(){
$('#example').dataTable( {
"sScrollY": "200px",
"bPaginate": false
} );
}, 0, false);
}
However, flat mixing of angular and jquery in this way is a terrible idea. You really should be writting an angular directive to wrap the jquery plugin, or just not use jQuery at all.

Related

angular-datatables - ng-repeat does not update datatable with new rows when $onChanges event called in component controller

I have made an angular-datatable component to be used in my project (using angular 1.5) and have bound the data it is populated with (the angular way) to an array of objects that gets updated in the parent scope. Upon changing the parent scope value, the $onChanges event is called and the bound data is updated, but the rows in the table do not change via ng-repeat to reflect the update -- after the table is initially drawn, no changes can be seen with subsequent data changes. Here is my code:
JS:
angular.module('datatable').
component('datatable', {
bindings: {
graphData: '<',
tableId: '=',
divId: '='
},
templateUrl: 'components/graphs/datatable.template.html',
controller: function datatableController (DTOptionsBuilder, DTColumnBuilder) {
let self = this;
self.tableKeys = Object.keys(self.graphData[0])
self.$onChanges = function () {
self.dtOptions = {
paginationType: 'full_numbers',
displayLength: 30,
dom: 'Bfrtip',
buttons: [
{extend: 'excelHtml5'},
'copy'
],
scrollY: 450,
scrollX: '100%',
scrollCollapse: true,
};
};
}
})
HTML (Component):
<datatable graph-data="$ctrl.submittedResults" table-id="'allDataTableResults'" div-id="'allDataTable'"></datatable>
HTML (Template):
<div class="col-md-10 col-md-offset-1">
<div class="well well-lg" style="background: white">
<div id="{{$ctrl.divId}}" style="width: 100%; height: 700px; display: block; margin: 0 auto">
<table datatable="ng" dt-options="$ctrl.dtOptions"
class="table table-striped table-bordered" cellspacing="0">
<thead>
<tr>
<th ng-repeat="key in $ctrl.tableKeys">{{key}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in $ctrl.graphData">
<td ng-repeat="val in row">{{val}}</td>
</tr>
</tbody>
</table>
</div>
</div>
I have tried just about everything I have seen in related questions on Stack Overflow to no avail, and I had no luck with rerendering the table completely with dtInstance. Any help would be greatly appreciated.
v0.5.5 doesn't allow '$' char in controller variable name which unfortunately is the default value in Angular Component. Override it as a workaround e.g. controllerAs: ctrl
https://github.com/l-lin/angular-datatables/blob/v0.5.5/dist/angular-datatables.js#L892
Reported this issue: https://github.com/l-lin/angular-datatables/issues/916

Why isn't ng-repeat working here?

I have an array within my scope called checkedInChildren. The array contains just string values. I want a table to repeat for each string in the checkedInChildren array. Here is what I have so far:
<div ng-app="MyApp" ng-controller="myCtrl">
<div>
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="child in checkedInChildren">
<td>test</td>
</tr>
</tbody>
</table>
</div>
</div>
Here is my controller:
app.controller("myCtrl", function($scope, $http) {
$scope.checkedInChildren = [];
$scope.addChildToCheckedIn = function(childName) {
$scope.checkedInChildren.push(childName);
}
});
No rows show no matter what names I add to that array. I omitted some code in the controller and some of the HTML, but I am debugging and clearly seeing values in the array. Any ideas?
You have to add {{child}} to bind each item to the view.
<tr ng-repeat="child in checkedInChildren">
<td>
{{child}}
</td>
</tr>
on your HTML you're referencing a different controller, put the proper name
<div ng-app="MyApp" ng-controller="checkInCtrl">...</div>
Demo
Working Plnkr
It is not displaying test because there is no item in your checkedInChildren array.
From Angular Doc:
The ngRepeat directive instantiates a template once per item from a
collection.
$scope.checkedInChildren = [];
$scope.addChildToCheckedIn = function (childName) {
$scope.checkedInChildren.push(childName);
}
$scope.addChildToCheckedIn('John');
You did not call addChildToCheckedIn method, that is why checkedInChildren array is empty.
hope that solve your problem.
Did you call the addChildToCheckedIn function? Try this. Hope this helps.

ng-repeat shows objects but not elements of objects

I want to show specific elements from objects I receive from a service using ng-repeat. When I use {{account}} it renders all the elements, but when I try for example {{account.idType}} it shows nothing. What's going on?
Here is my HTML
<table>
<thead>
<tr>
<th>List of Accounts</th>
</tr>
</thead>
<tbody ng-repeat="account in accounts" ng-if="account.idType != 0">
<tr>
<td>{{account.idType}}</td>
<td>{{account.linkRel}}</td>
<td>{{account.linkURI}}</td>
<td><input type="button" ng-click="editAccount(account.idType)" value="edit"></td>
</div>
and this is the controller:
rest.controller('accountListCtrl',['$scope','$resource','$location','$parse','$routeParams',
function($scope,$resource, $location, $parse,$routeParams)
{
var Account = $resource('http://localhost\\:8085/WSAT/account/');
$scope.accounts = Account.get();
}]);
the response I get from the service is this:
{"linklist":[{"idType":"0","linkRel":"Get all accounts","linkType":"Sibling","linkURI":"http://localhost:8085/WSAT/account","linkVerb":"GET"},{"idType":"0","linkRel":"Create a new account","linkType":"Sibling","linkURI":"http://localhost:8085/WSAT/account","linkVerb":"POST"},{"idType":"7","linkRel":"try","linkType":"Child","linkURI":"http://localhost:8085/WSAT/account/7","linkVerb":"GET"},{"idType":"9","linkRel":"try234","linkType":"Child","linkURI":"http://localhost:8085/WSAT/account/9","linkVerb":"GET"}]}
Because $resource is asynchronous (It returns a promise).
Replace $scope.accounts = Account.get() by
Account.get().then(function(data){
$scope.accounts=data.result;
});

angularjs 2 data sources with 1 controller?

Ok. This one really has me stumped. Here is what I am trying to build.
I have a table. In the first <tr> I have it repeating data, and the last <td> has a button. When I click this button, I want to show a set of hidden rows underneath each repeated <td> tag. In those hidden rows, I want to call more data.
I'm using mysqli and php to encode the data on the DB into json, which I then populate into the cells. I can do all of this with 1 controller. However, if I want to pull data from another source, to populate into the hidden cells, I have to make another controller.
Here is the base code: (pardon the inline css, its easier for me to format it into external css after its functional)
<script src="js/angular.min.js"></script>
<script>
var app = angular.module('testApp', []);
app.controller('tableCtrl_1', function($scope, $http){
getdata();
function getdata(){
$http.post("angular_data.php").success(function(data){
$scope.getdata = data;
});
};
});
app.controller('tableCtrl_2', function($scope, $http){
getdata();
function getdata(){
$http.post("angular_data_2.php").success(function(data){
$scope.getdata = data;
});
}
});
</script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12" style="padding:0px;">
<table class="table table-bordered" ng-controller="tableCtrl_1">
<tr style="height:70px;background-color:#0C4791;">
<th style="text-align:center;color:white;vertical-align:middle;">Flexi Floor/Low Wall</th>
<th style="text-align:center;color:white;vertical-align:middle;">Cooling</th>
<th style="text-align:center;color:white;vertical-align:middle;">Heating</th>
<th style="text-align:center;color:white;vertical-align:middle;">Nominal Capacities(cooling/heating)</th>
<th style="text-align:center;color:white;vertical-align:middle;">Pipe Length</th>
<th style="text-align:center;color:white;vertical-align:middle;">List Price</th>
<th style="text-align:center;color:white;vertical-align:middle;">Quantity</th>
</tr>
<tr ng-repeat = "getdata in getdata | filter:'Flexi Floor/Low Wall':true">
<td style="vertical-align:middle;text-align:center;">{{getdata.model_no_from}} + {{getdata.model_no_to}}</td>
<td style="vertical-align:middle;text-align:center;">{{getdata.cooling}}</td>
<td style="vertical-align:middle;text-align:center;">{{getdata.heating}}</td>
<td style="vertical-align:middle;text-align:center;"><span style="color:blue">{{getdata.nominal_cooling}}</span><span style="color:red;">{{getdata.nominal_heating}}</span></td>
<td style="vertical-align:middle;text-align:center;">{{getdata.pipe_length}}</td>
<td style="vertical-align:middle;text-align:center;"><span style="font-weight:bold;">{{getdata.system_listPrice | currency: "£"}}</span></td>
<td style="vertical-align:middle;text-align:center;"><button class="btn btn-default btn-large btn-block">Select</button></td>
</tr>
</table>
<table class="table table-bordered" ng-controller="tableCtrl_2">
<tr ng-repeat ="getdata in getdata">
<td style="vertical-align:middle;text-align:center;" colspan="7">{{getdata.sales_description}}</td>
</tr>
</table>
</div>
</div>
</div>
</body>
The second table is just so that I could test and make sure it's pulling in data properly. (turns out it wasn't, as I had to encode in utf-8)
SO TLDR:
controller 1 pulls in main data, populates cells. Click the button in a cell, and it will show hidden cells underneath with controller 2 data.
Problems I am having: matching the secondary data to the ng-repeat of the primary data, using both data sources in one controller area.
image for visual aid:
Image link
This is my technique working with relational data from MySql. Using the angular build in filter to march the main view. In MS access it is a main form and subform. for me I use slim framework to build easy API Slim
Here is the $scope handle the related key.
$scope.showDetail = function(saleId,saleDetails){
$scope.dataDetails = $filter('filter')(saleDetails,{main_id:saleId},true);
}
look at the Plunker
Good luck!

How to render a column with model binding using angular-datatables?

Is there a way to render a column with model binding in textbox using DTColumnBuilder?
Something like:
DTColumnBuilder.newColumn('ColumnName').withTitle('Column Name').renderWith(function (data) {
return '<input type="text" ng-model="ColumnName" />';
}),
No. You can render the table with (example) :
DTColumnBuilder.newColumn('firstName', 'First name')
.renderWith(function (data) {
return '<input type="text" ng-model="json.firstName" />'
}),
but the ng-model is never recognized because it is not angular itself that do the rendering. If you let angular do the rendering, i.e datatable="ng" and ng-repeat it works :
<table datatable="ng" dt-options="dtOptions" dt-columns="dtColumns">
<tr ng-repeat="item in json">
<td>{{ item.id }} </td>
<td><input ng-model="item.firstName"/></td>
<td>{{ item.lastName }} </td>
</tr>
</table>
demo -> http://plnkr.co/edit/f0ycjJvsACaumY13IVUZ?p=preview
notice that the JSON items is updated when you are editing in the input boxes.
Had the same problem, here is my solution:
Register callback for dtInstance
On "draw.dt" from DataTable $compile related html with angular
In other words:
HTML:
<table datatable=""
dt-options="vm.dtOptions"
dt-columns="vm.dtColumns"
dt-instance="vm.dtInstanceCallback"
class="table table-bordered table-condensed">
</table>
JS:
renderWith(function(data, type, full) {
return `<a class="ng-scope"><span ng-click='vm.remove("${data}")' class='fa fa-times-circle'></span></a>`
});
...
vm.dtInstanceCallback = (dtInstance) => {
vm.dtInstance = dtInstance;
dtInstance.DataTable.on('draw.dt', () => {
let elements = angular.element("#" + dtInstance.id + " .ng-scope");
angular.forEach(elements, (element) => {
$compile(element)($scope)
})
});
}
I minimized selection of elements, to optimize performance, maybe it's not needed. So far tested in Chrome & Safari, worked in both

Resources