AngularJS - DOM not updated in Chrome and Mozilla - angularjs

I'm using angularjs and angular-ui-bootstrap to make a table and page some data, which I get from an API.
I've made sure my service receives the correct data and that the requests are built properly. My pageChange function is properly triggered and my first page gets loaded successfully.
So I have the following controller setup:
(function () {
'use strict';
initContacts.$inject = ['$scope', '$http'];
angular.module('app')
.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
angular.module('app', ['ui.bootstrap']).controller('contactSearchController', initContacts);
function initContacts($scope, $http) {
$scope.contacts = [];
$scope.totalItems = 0;
$scope.pages = 0;
$scope.currentPage = 0;
$scope.maxPageLinksShown = 5;
if (window.location.hash !== '') {
$scope.currentPage = window.location.hash.replace('#', '');
}
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1)).success(function (data) {
$scope.contacts = data.Contacts;
$scope.totalItems = data.Count;
$scope.PageSize = data.Contacts.length;
$scope.pages = Math.ceil((data.Count / $scope.PageSize));
});
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.contacts = data.Contacts;
});
};
}
}
})();
And in my view I have:
<div ng-app="app" ng-controller="contactSearchController">
<table class="table table-striped table-hover contact-search-table">
<thead>
<tr>
<td class="contact-title">
Titel
</td>
<td class="department">
Afdeling
</td>
<td class="name">
Navn
</td>
<td class="work-phone">
Telefon
</td>
<td class="mobile">
Mobile
</td>
<td class="email">
Email
</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, value) in contacts">
<td class="contact-title">{{value.Title}}</td>
<td class="department">{{value.Department}}</td>
<td class="name">{{value.FullName}}</td>
<td class="work-phone">{{value.WorkPhone}}</td>
<td class="mobile">{{value.WorkMobile}}</td>
<td class="email">{{value.Email}}</td>
</tr>
</tbody>
</table>
<div class="col-xs-12 pager-container">
<ul uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()" max-size="maxPageLinksShown" class="pagination-sm" boundary-links="true" num-pages="pages"></ul>
</div>
Now this works to some extent.
My problem
When I click the page links, the pageChanged() function is called, I get the data from my api and it's all correct, the list in the scope appears to be updated fine, but the table in my view doesn't change.
This solution works fine ONLY in IE ! (who would have thought huh...)
No exceptions get thrown.
I swear this was working yesterday.
Any help is much appreciated!
EDIT
What I've tried:
-Putting the assignment of the contacts in an $apply like so:
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.$apply(function () { // apply
$scope.contacts = data.Contacts;
});
});
};
I got a "$digest already in progress" error from this.
tried to wrap the apply in a timeout like so:
$timeout(function(){ //any code in here will automatically have an apply run afterwards });
Got rid of the error but the DOM still won't update.

I had such a problem and I solved it using $scope.$apply:
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.$apply(function () { // apply
$scope.contacts = data.Contacts;
});
});
};

You should use $scope.$applyAsync instead. It will be applied to the next digest cycle.
If you need to target all the $scopes of your AngularJS application, use $rootScope.$applyAsync instead.
Official doc

Okay so what worked in this situation was changing the way I'm visualizing the ng-repeat like so:
<tr ng-repeat="contact in getContacts()">
<td class="contact-title">{{contact.Title}}</td>
<td class="department">{{contact.Department}}</td>
<td class="name">{{contact.FullName}}</td>
<td class="work-phone">{{contact.WorkPhone}}</td>
<td class="mobile">{{contact.WorkMobile}}</td>
<td class="email">{{contact.Email}}</td>
</tr>
And heres the getContacts() function:
$scope.getContacts = function () {
var data = $scope.contacts;
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
I don't really know why that works, but it does.

Related

AngularJS - Not update table-pagination after http-get success's event

i have a problem. I'm using AngularJs with WebService-Rest, and can't update some table after the call HTTP-GET to WebService. I did tested everything but i can't get it.
Next, i attach the code. Thanks!
HTML:
...
<div class="row" ng-app="SIGA" ng-controller="CreateTable">
<div class="container-fluid">
<table class="table table-striped">
<tbody>
<tr>
<td>Buscar</td>
<td><input type="text" ng-model="search.nombre" /></td>
</tr>
<tr ng-repeat="e in estaciones | filter:paginate| filter:search" ng-class-odd="'odd'">
<td>
<button class="btn">
Detalle
</button>
</td>
<td>{{e.nombre}}</td>
</tr>
</tbody>
</table>
<pagination total-items="totalItems" ng-model="currentPage"
max-size="5" boundary-links="true"
items-per-page="numPerPage" class="pagination-sm">
</pagination>
</div>
</div>
...
JS: ...
app.controller('RestEstacion', function ($rootScope, $http) {
$http.get('http://localhost:8080/sigarest/webresources/entity.estaciones').
success(function(data) {
$rootScope.estaciones = data; UpdateTable($rootScope);
}).
error(function(status) {
alert('error:'+status);
});
});
app.controller('CreateTable', function ($scope,$rootScope) {
$rootScope.predicate = 'nombre';
$rootScope.reverse = true;
$rootScope.currentPage = 1;
$rootScope.order = function (predicate) {
$rootScope.reverse = ($rootScope.predicate === predicate) ? !$rootScope.reverse : false;
$rootScope.predicate = predicate;
};
$rootScope.estaciones = [];
$rootScope.totalItems = $rootScope.estaciones.length;
$rootScope.numPerPage = 5;
$rootScope.paginate = function (value) {
var begin, end, index;
begin = ($rootScope.currentPage - 1) * $rootScope.numPerPage;
end = begin + $rootScope.numPerPage;
index = $rootScope.estaciones.indexOf(value);
return (begin <= index && index < end);
};
});
JS (Update Function):
function UpdateTable($rootScope){
$rootScope.totalItems = $rootScope.estaciones.length;}
** Original Answer (what comments refer to) **
I think you are assigning the get response object rather than the data inside it. Try this:
success(function(response) {
$rootScope.estaciones = response.data;
UpdateTable($rootScope);
}).
** EDIT **
Now that we have established that you are returning data from the API, the real issue appears to be the double controller using $rootScope as a bridge, which can work but is a bit of an anti-pattern in Angular.
The first controller in your app is trying to act like a service, and so really needs to be converted into one. Here is some SAMPLE PSUEDO CODE to give the idea. I do not fully understand your code, like the pagination directive. There should be a click handler in the pagination directive that would call the service method changePagination and pass in the new page number. There should be no need for $rootScope anywhere in this.
JS
app.service('RestEstacionService', function ($http) {
var RestEstacionService = this;
this.apiData = null;
this.tableData = null;
this.currentPage = 1;
this.numPerPage = 5;
this.url = 'http://localhost:8080/sigarest/webresources/entity.estaciones';
this.getData = function (url) {
return $http.get(url).then(function(response) {
RestEstacionService.apiData = response.data;
// do success stuff here
// figure out which page the view should display
// assign a portion of the api data to the tableData variable
})
};
this.changePagination = function (newPage) {
// do your your pagination work here
};
});
app.controller('RestEstacionController', ['$scope', 'RestEstacionService', function ($scope, RestEstacionService) {
$scope.service = RestEstacionService;
RestEstacionService.getData(RestEstacionService.url);
}]);
HTML
<div class="row" ng-app="SIGA" ng-controller="RestEstacionController">
<div class="container-fluid">
<table class="table table-striped">
<tbody>
<tr>
<td>Buscar</td>
<td><input type="text" ng-model="search.nombre" /></td>
</tr>
<tr ng-repeat="row in services.tableData | filter:paginate| filter:search" ng-class-odd="'odd'">
<td>
<button class="btn">
Detalle
</button>
</td>
<td>{{row.nombre}}</td>
</tr>
</tbody>
</table>
<pagination total-items="services.apiData.length" ng-model="services.currentPage"
max-size="5" boundary-links="true"
items-per-page="services.numPerPage" class="pagination-sm">
</pagination>
</div>

jQuery DataTables recreation on data update with socket.io

I'm using jQuery DataTables with socket.io on AngularJS, and I'm pushing an item to the data binding list on a socket message and digesting afterwards. When it happened, the datatable recreated itself instead of just updating the data and not working properly. I'm also randomly get the error *Warning: Cannot reinitialise DataTable, and when I do, the datatable failed to display.
JavaScript
var app = angular.module('App', ['ui.bootstrap','ngAnimate', 'datatables']);
app.factory('socket', function () {
var socket = io.connect('http://' + document.domain + ':' + location.port + '/t');
return socket;
});
app.controller('controller', function ($scope, socket, $timeout, DTOptionsBuilder, DTColumnBuilder) {
$scope.data=[];
$scope.headers = {'Name':'name','Title','title'}
socket.on('data', function (d) {
d = angular.fromJson(d);
$scope.data.push(d);
$scope.$digest();
});
$scope.dtOptions = DTOptionsBuilder.newOptions().withPaginationType('full_numbers').withOption('bInfo', false);
$scope.dtColumns = [];
$scope.dtInstance = {};
for (key in $scope.headers) {
$scope.dtColumns.push(DTColumnBuilder.newColumn($scope.headers[key]).withTitle(key));
}
});
HTML
<table id="tbl" datatable="ng" dt-options="dtOptions" dt-columns="dtColumns" dt-instance="dtInstance"
class="table table-striped row-border hover">
<tr class="fade" ng-model="d"
ng-repeat="d in data">
You miss a colon in the headers' literal:
$scope.headers = {'Name':'name','Title' : 'title'}
^
Hopefully the JSON items pushed into data is valid and the full markup is:
<table id="tbl" datatable="ng" dt-options="dtOptions" dt-columns="dtColumns" dt-instance="dtInstance" class="table table-striped row-border hover">
<thead></thead>
<tbody>
<tr class="fade" ng-model="d" ng-repeat="d in data">
<td>{{ d.name }}</td>
<td>{{ d.title }}</td>
</tr>
</tbody>
</table>
Use rerender() instead of §digest (why §digest in the first place?):
socket.on('data', function (d) {
d = angular.fromJson(d);
$scope.data.push(d);
$scope.dtInstance.rerender();
});

bootstrap-table not rendering upon updating model in Angular

Hi I am not able to render table using bootstrap-table and angular. here is my code, I think I need to call bootstrap-table init method in angular ajax call. Can some one guide me on how to do this..?
angular
.module('reports')
.controller(
'ReportCtrl',
[
'$scope',
'$http',
'ngProgress',
function($scope, $http, ngProgress) {
var vm = this;
vm.mdp = {};
vm.mdp.data = [];
vm.mdp.columns = [];
$scope.submit = function() {
var report = $scope.tab;
$http.post('/reports/cmd/getData', {
report : report,
date : createdAfter
}).success(function(data) {
vm.mdp.data = data;
$.each(data[0], function(key, value){
vm.mdp.columns.push(key);
});
}).error(function(error) {
alert(error);
});
};
} ]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="mdp" class="panel" ng-controller="ReportCtrl as report" ng-show="panel.isSelected('mdp')">
<table data-toggle="table" data-show-columns="true" data-search="true" data-show-export="true" data-pagination="true" data-height="299">
<thead>
<tr>
<th ng-repeat="c in report.mdp.columns" data-field= {{c}} >{{ c }}</th>
</tr>
</thead>
<tr ng-repeat="r in report.mdp.data">
<td ng-repeat="c in report.mdp.columns">{{ r[c] }}</td>
</tr>
</table>
</div>
Integrating Bootstrap Table with Angular is solved here:
https://github.com/wenzhixin/bootstrap-table/issues/165
https://github.com/AkramKamal/bootstrap-table-examples/tree/master/integrate
I have some minor changes in my implementation of this solution which I will upload to Github / JSFiddle shortly. But the links above will allow you to get going.

Breaking the ice on angular

I have a view:
<div id="productList" ng-controller="ProductController">
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th width="100%">Item ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in productItems">
<td><input type="checkbox" /></td>
<td><% item.ID %></td>
</tr>
</tbody>
</table>
</div>
I have a controller:
var TestApp = angular.module('TestApp', [], function( $interpolateProvider ) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
});
TestApp.controller('ProductController', function ( $scope ) {
ProductRepository.GetPaginatedProducts();
$scope.productItems = ProductRepository.Model;
});
I have a model:
var ProductRepository = function ()
{
return {
Model: null,
GetPaginatedProducts: function()
{
$.ajax( {
"url": Test.URL + "/json/products/paginated",
"dataType": "json",
"method": "post",
"success": function( data )
{
ProductRepository.Model = data;
}
} );
},
}
}
When the ajax finished, it updates the ProductRepository.Model data variable which I want the angular controller scope.productItems to work off of.
This is my first time using angular and i think I've missed the point,
Why is the table not updating with the information?
Please see here for sample ajax call http://plnkr.co/edit/5CiC8MWiwo010nwu32he?p=preview
var TestApp = angular.module('plunker', []);
TestApp.factory('ProductRepository', function($http, $q) {
var Model = [];
return {
Model: Model,
GetPaginatedProducts: function() {
var deferred = $q.defer();
$http.get('paginated.json').then(
//sucess
function(result) {
deferred.resolve(result.data)
}
//error
, function() {});
return deferred.promise;
}
};
})
TestApp.controller('ProductController', function($scope, ProductRepository) {
$scope.productItems = [];
ProductRepository.GetPaginatedProducts().then(function(data) {
//success
$scope.productItems = data;
},
//error
function() {
alert("can't get data");
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="plunker">
<div id="productList" ng-controller="ProductController">
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th width="100%">Item ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in productItems">
<td>
<input type="checkbox" />
</td>
<td>{{item.ID}}</td>
</tr>
</tbody>
</table>
</div>
</div>
As #tymeJV stated in the comment, you should use $http or $resource (or restangular) to perform the data access. If you are updating values outside of Angular's framework, it has no way of knowing that data has changed. By using something like $http or $resource, Angular is aware when an event has occurred that can change values and automatically checks for updates.
Alternately, you could manually issues an $apply() to essentially handle the digest update manually (ensuring Angular goes through a digest cycle when you've changed values outside of Angular). Take a look at the documentation on $apply() at https://docs.angularjs.org/api/ng/type/$rootScope.Scope .
However, your best bet is to use the Angular approach in the first place and avoid using jQuery for this.

show 2 array through ng-repeat error

i am trying to make a table from 2 arrays in AngularJS one array contains Employees name and other array contain Services Name and rest of the cells contain check boxes but when i do i got error Error: [ngRepeat:dupes]
here is my AngularJS code
var model = angular.module('wizard', ['ngRoute'])
.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/m5", {
controller: "model5Controller",
templateUrl: "/templates/m5.html"
});
$routeProvider.otherwise({ redirectTo: '/' });
}]);
var model5Controller = ["$scope", "$http", "$window", function ($scope,
$http, $window) {
$scope.PBA = [];
$scope.SOB = [];
$http.get('/Test/GetPBA').
then(
function (result) {
//success
$scope.PBA = result.data;
},
function () {
//error
});
$http.get('/Test/GetSOB').
then(
function (result) {
//success
$scope.SOB = result.data;
},
function () {
//error
});
$scope.save = function () {
//to be written
};
}];
GetPBA/GetSOB returns JSON type Array of List
both contain properties like Id, Name
here is my html
<div>
<form ng-submit="save()">
<table border="1">
<tr>
<td><strong>Services</strong></td>
<td ng-repeat="e in PBA">{{e.Name}}</td>
</td>
<tr ng-repeat="i in SOB">
<td>{{i.Name}}</td>
<td ng-repeat="e in PBA">
<input type="checkbox" name="{{e.Id}}" />
</td>
</tr>
</table>
</form>
Try this
<tr ng-repeat="i in SOB track by $index">
<td>{{i.Name}}</td>
<td ng-repeat="e in PBA track by $index">
<input type="checkbox" name="{{e.Id}}" />
</td>
</tr>

Resources