Getting $digest erro error when using ng-repeat - angularjs

I am using ng-repeat to show the User list from SQL Lite Database but when i am binding $scope.Users with result.rows, it gives me the following error
$rootScope:infdig Infinite $digest Loop
<tr ng-repeat="u in Users">
<td>
<button class="w3-btn w3-ripple" ng-click="editUser(u)">✎ Edit</button>
</td>
<td>{{ u.fname }}</td>
<td>{{ u.lname }}</td>
</tr>
controller method:
var showRecords = function () // Function For Retrive data from Database Display records as list
{
db.transaction(function (tx) {
tx.executeSql(selectAllStatement, [], function (tx, result) {
if (result.rows.length > 0) {
dataset = result.rows;
$scope.Users = dataset;
}
});
});
}
Here is Link
Please provide me the solution for this.

I got the solution now.
Returning data from transaction with SQL Lite Database, need to
JSON.stringify
and after that need to parse with
JSON.parse
var showRecords = function () // Function For Retrive data from Database Display records as list
{
db.transaction(function (tx) {
tx.executeSql(selectAllStatement, [], function (tx, result) {
if (result.rows.length > 0) {
$scope.$apply(function () {
$scope.Users = JSON.parse(JSON.stringify(result.rows));
});
}
});
});
}
Here is the working Link

Related

Unable to bind Json Data with Table Header using AngularJS

Im getting data in this format from api, but when i try binding it to table using angularjs it is creating empty space instead of values. Im also getting more then one table from some Api's please explain who to bind different datatables in different tables too. thanks
{"Table":
[{
"SchoolId":1,
"schoolname":"Microsoft",
"SCHOOLCODE":"29911583",
"WEBSITE":"JLR",
"USEREMAIL":"faucibus#aliquamiaculislacus.org",
"PHONE":"841-9331",
"ADDRESS1":"682-5760 Felis Street",
"ISACTIVE":0,
"PLANTYPE":3
}]
}
Angular Controller
SMSApp.factory('GetStudentService', function ($http) {
studobj = {};
studobj.getAll = function () {
var stud=[];
stud = $http({ method: 'Get', url: 'http://localhost:58545/api/Student?Studentid=1' }).
then(function (response) {
return response.data;
});
return stud;
};
return studobj;
});
SMSApp.controller('studentController', function ($scope, GetStudentService) {
$scope.msg = "Welcome from Controller";
GetStudentService.getAll().then(function (result) {
$scope.school = result;
console.log(result);
});
});
HTML Code
<tbody ng-controller="studentController">
<tr ng-repeat="schools in school track by $index">
<td>{{schools.SchoolId}}</td>
<td>{{schools.schoolname}}</td>
<td>{{schools.SCHOOLCODE}}</td>
<td>{{schools.WEBSITE}}</td>
<td>{{schools.USEREMAIL}}</td>
</tr>
</tbody>
WHAT I GET
Change in your Angular Controller :
SMSApp.controller('studentController', function ($scope, GetStudentService) {
$scope.msg = "Welcome from Controller";
GetStudentService.getAll().then(function (result) {
/********************* Changed Here ********************/
$scope.school = JSON.parse(result._body); // Or only JSON.parse(result)
$scope.school = $scope.school.table;
});
});
And your HTML Code :
<tbody ng-controller="studentController">
<tr ng-repeat="schools in school track by $index">
<td>{{schools.SchoolId}}</td>
<td>{{schools.schoolname}}</td>
<td>{{schools.SCHOOLCODE}}</td>
<td>{{schools.WEBSITE}}</td>
<td>{{schools.USEREMAIL}}</td>
</tr>
</tbody>
Naming the data school is confusing. Do instead:
GetStudentService.getAll().then(function (data) {
$scope.tableObj = data;
console.log(data);
});
<tbody ng-controller="studentController">
<tr ng-repeat="school in tableObj.Table track by school.SchoolId">
<td>{{school.SchoolId}}</td>
<td>{{school.schoolname}}</td>
<td>{{school.SCHOOLCODE}}</td>
<td>{{school.WEBSITE}}</td>
<td>{{school.USEREMAIL}}</td>
</tr>
</tbody>
From the Docs:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.
— AngularJS ng-repeat API Reference
Just replace your result with result.Table in your controller because if you properly see your response it is inside "Table" named array. Try this you should be able to see your records
SMSApp.controller('studentController', function ($scope, GetStudentService) {
$scope.msg = "Welcome from Controller";
GetStudentService.getAll().then(function (result) {
$scope.school = result.Table;
console.log(result);
});
});
Note: I have replaced your API call in the jsfiddle link with the response mentioned in the question.
JSFiddle Link : http://jsfiddle.net/zu8q7go6/9/

Do a loop instead of multiple $http chaining?

sorry, i have checked answers, but still dont understand how to make it :
I've got an array :
var surfaces = [
{min:0,max:499},
{min:500,max:999},
{min:1000,max:1499},
{min:1500,max:1999},
{min:2000,max:2399},
{min:2400,max:2999},
{min:3000,max:3999},
{min:4000,max:5999},
{min:6000,max:100000}
]
And I've got this looong $http chained calls with $q :
$q.when()
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[0]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[1]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[2]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[3]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[4]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[5]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[6]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[7]).success(function(data){
$scope.normes.push(data);
})
})
.then(function () {
$http.post('backend/backend.php?action=get_normes',surfaces[8]).success(function(data){
$scope.normes.push(data);
})
})
I'd rather do a loop, but have now idea on how to do this !!
EDIT : I've got way bigger problem : data is not in order in $scope.normes ! Its always different ! How could i do to always push in order ? How could the data could be in order ?
I got ng-repeats like this but the information is not well ordered because of $http synchornicity ;
<div class="row ">
<div class="col-lg-10" style="overflow: auto; max-height: 600px;">
<table class="table table-bordered table-hover " border="1" >
<thead >
<tr>
<th> Désignation</th>
<th> Total Magasins</th>
<th> Moy Habitants Zone</th>
<th> Ca Moyen</th>
<th> EO Mini</th>
<th> EO Maxi</th>
<th> Coef Moyen</th>
<th> Pourcent Côtier</th>
<th> Pourcent Urbain</th>
<th> Tx Habituels</th>
<th> Tx fréquentation</th>
<th> Tx fidélisation</th>
<th> Zone Attraction</th>
<th> Revenus Zone</th>
<th> Nb Perso Foyer</th>
<th> Age Moyen</th>
</tr>
</thead>
<tbody >
<tr ng-repeat="norme in normes" >
<td>Surface 0-499 m²</td>
<td>{{::norme.nb_magasins | number:0}}</td>
<td>{{::norme.moy_habitants_zone | number:0}}</td>
<td>{{::norme.ca_moyen | number:0}}</td>
<td>{{::norme.eomin | number:0}}</td>
<td>{{::norme.eomax | number:0}}</td>
<td>{{::norme.coef_moyen | number:2}}</td>
<td></td>
<td></td>
<td>{{::norme.tx_habituels | number:2}}</td>
<td>{{::norme.tx_frequentation | number:2}}</td>
<td>{{::norme.tx_fidelisation | number:2}}</td>
<td>{{::norme.attraction | number:0}}</td>
<td>{{::norme.revenus_zone | number:0}}</td>
<td>{{::norme.nb_pers_foyer | number:2}}</td>
<td>{{::norme.age_moyen | number:2}}</td>
<td ></td>
</tr>
</tbody>
</table>
</div>
Each time i redo the $q, it comes with a different order ! How could i do ?
EDIT : So I'm now getting standards JSON objects from the Back End so it is simplier(Editied the html table on this post), but with the solutions you gently provided, it doesnt appear yet in the right order. The $http get started in the right order, but it seems that $scope.normes doesnt list as the $http have been started ! (Oh i think i maybe can order with the orderby in the front end... I forgot, but i thought it was possible to order the json objects as they get pushed into the array, but in the view it doesnt appear as when the $http have been called)
Try this::
$scope.onAllDone = function() {
var promises = [];
angular.forEach(surfaces , function(surface) {
promises.push($http.post('backend/backend.php?action=get_normes',surface)).then(function(data) {
$scope.normes.push(data);
return data;
});
});
return $q.all(promises);
}
USE::
$scope.onAllDone().then(fnSuccess, fnError);
Promises and $q.all (doc) are your friends.
In more detail, you will have to make a promise for each call (if the call itself does not return one), push them in an array and call $q.all(promises).then(allFinished).
function callUpdate(x, promises) {
var d = $q.defer();
$http.post('backend/backend.php?action=get_normes',x).then(function(resp){
$scope.normes.push(resp.data);
d.resolve(resp.data);
})
promises.push(d.promise);
}
...
var promises = [];
$scope.normes = [];
for (var i = 0; i < surfaces.length; i++) {
callUpdate(surfaces[i], promises);
}
$q.all(promises).then(function() {
// called when all promises have been resolved successully
});
Create an array of promises
var promises = [];
surfaces.forEach(function(surface){
promises.push($http.post('backend/backend.php?action=get_normes',surface));
})
You can use $q.all() and wait for all promises to complete and use Array.concat() to create $scope.normes array.
$q.all(promises).then(function (results) {
$scope.normes = [].concat.apply([],results);
});
When chaining promises, it is important to return the subsequent promise to the .then method handler function:
var arrayPromise = $q.when([])
.then(function (dArray) {
var httpPromise = $http.post('backend/backend.php/action=get_normes',surfaces[0])
var dPromise = httpPromise.then(function(response) {
$scope.normes.push(response.data);
dArray.push(response.data);
return dArray;
});
return dPromise;
}).then(function(dArray) {
console.log("one XHR is complete");
console.log(dArray);
});
When the code fails to return anything to the handler function, the $q service resolves the promise as undefined and immediately executes the next function. By returning the subsequent promise, the $q service will wait for the promise to resolve before executing the next promise in the chain.
That said. How to create a loop that chains sequential promises?
var arrayPromise = $q.when([]);
for (let n=0; n<surfaces.length; i++) {
arrayPromise = arrayPromise
.then(function (dArray) {
var httpPromise = $http.post(url,surfaces[n]);
var dPromise = httpPromise.then(function(response) {
$scope.normes.push(response.data);
dArray.push(response.data);
//return dArray to chain
return dArray;
});
return dPromise;
});
};
arrayPromise.then(function(dArray) {
console.log("all XHRs are complete");
console.log(dArray);
}).catch(function(errorResponse) {
console.log("one of the XHRs failed");
console.log(errorResponse.status);
throw errorResponse;
});
Create a variable with a promise for an empty array. Then in the loop simply assign the promise derived from the .then method back to that variable. Each iteration of the loop returns an array that is one item larger than the previous iteration. Also each iteration has the side effect of pushing an item to scope.

ngTable don't updated after add new data

my problem is if I add new item to the database the ngTable don't updated with new data automatically but if I refresh the page (f5) the data is shown.
I use ng table to show data.
PS: the AngularJS consume data from JSON produced by restful WS from a JEE backend connected to a dataBase.
the ng-table :
<table ng-table="tablePy" show-filter="true"
class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th> Code </th>
<th> Libellé</th>
<th>Code Alphabetique 2</th>
<th>Code Alphabetique 3</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat=" py in paysValues | filter:searchValeur ">
<td align="right"> {{py.codPay}}</td>
<td > {{py.libPay}}</td>
<td> {{py.codAlph2}}</td>
<td> {{py.codAlph3}}</td>
</tr>
</tbody>
</table>
the function that fill the table is :
var initPaysData = function () {
var promise =
allPaysService.getPays();
promise.then(
function (res) {
$scope.paysValues = res.data;
$scope.tablePy = new NgTableParams({}, {dataset: $scope.paysValues});
},
function (error) {
$log.error('failure Pays charging', error);
})
};
initPaysData();
I use the factory to get All data from the WS:
BrokoApp.factory('allPaysService', function ($http) {
return {
getPays: function () {
return $http.get('http://localhost:8080/BrokWeb/webresources/referentiel/pays');
}
}
});
this code work but if I add new item to the table is don't shown
the code of add is:
$scope.addPays = function () {
$scope.selectedPy = $scope.libPay;
ngDialog.openConfirm({
template: 'modalDialogAdd',
className: 'ngdialog-theme-default',
scope: $scope
}).then(function () {
console.log('***** before add length=' + $scope.paysValues.length);
ajouterPays();
initPaysData();
console.log('***** after add length=' + $scope.paysValues.length);
$scope.tablePy.total($scope.paysValues.length);
$scope.tablePy.reload();
}, function () {
setNotification(notifications, 'danger', 'Erreur', 'Vous avez annule l operation');
});
};
the console.log show that the length of the List of data is the same before and after Add, the console show:
***** before add length=152
***** after add length=152
XHR finished loading: GET "http://localhost:8080/BrokWeb/webresources/referentiel/pays".
a XHR finished loading: POST "http://localhost:8080/BrokWeb/webresources/referentiel/pays".
the function AjouterPays is
var ajouterPays = function () {
var paysData = {
codAlph2: $scope.codAlph2,
// getting data from the adding form
};
var res = addPaysService.addPys(paysData);
res.success(function () {
setNotification(notifications, 'success', 'Succes', 'Ajout de ' + paysData.libPay);
});
res.error(function () {
setNotification(notifications, 'danger', 'Erreur', 'Erreur est servenue au cours de la creation');
});
// Making the fields empty
$scope.codAlph2 = '';
};
i use the factory addPaysService to post data
BrokoApp.factory('addPaysService', function ($http) {
return {
addPys: function (PyData) {
return $http.post('http://localhost:8080/BrokWeb/webresources/referentiel/pays', PyData);
}
}
});
Can anybody help me.
One simple approach for the updation of the data after you post/save any data or record to the database can be
just have a get request after the save/update is successful
i mean you can call the service/factory function which has the get request to the data base and then assign the response to the scope object of the table
Thank you
In case of the POST success, you can add the created element (if returned by your service) in $scope.paysValues.
Maybe something like that :
var ajouterPays = function () {
var paysData = {
codAlph2: $scope.codAlph2,
// getting data from the adding form
};
var promise = addPaysService.addPys(paysData);
promise.success(function ( response ) {
$scope.paysValues.push( response.data );
setNotification(notifications, 'success', 'Succes', 'Ajout de ' + paysData.libPay);
});
promise.error(function () {
setNotification(notifications, 'danger', 'Erreur', 'Erreur est servenue au cours de la creation');
});
// Making the fields empty
$scope.codAlph2 = '';
};
If your service does not return the created element in case of success, you can simple push the data you sent instead.
PS: note that success() and error() methods on an HttpPromise are deprecated from Angular 1.4.4 (see https://code.angularjs.org/1.4.4/docs/api/ng/service/$http).

Angular can't see my Array change in the view

I'm using angularjs with Node-webkit and making a desktop application with nedb(as an embedded database).
My problem is that when i load (via a service) list of object from the database and try to show them in the view (using ng-repeat), the view can't see the loaded data (looks like it is not getting any update)
When i put a static data :
$scope.levels = [{_id:'abcd'},{_id:'efgh'}];
The view get it without any problems.
I've made some research and looks like that is somthing related to $apply
and $digest...I'm not sure.
Here my code:
//router
.state('level', {
url: '/level',
templateUrl:'frontend/components/level/views/level.html',
controller: 'LevelController'
})
The controller load the data from the DB asynchronously :
levelModule.controller('LevelController', function($scope,$rootScope,LevelService) {
// $scope.levels = [{_id:'abcd'},{_id:'eff'}];
$scope.levels = [];
LevelService.getAllLevels(function(lvs) {
console.log("tous "+JSON.stringify(lvs));
$scope.levels = lvs;
});
Here my service:
levelModule.factory('LevelService', function(DB_URL) {
var Datastore = require('nedb')
, path = require('path');
db = {};
db.levels = new Datastore({ filename:DB_URL+'/levels.db',autoload: true });
return {
getAllLevels : function(callback) {
db.levels.find({}, function (err, lvs) {
if(err)
console.log(err);
else
callback(lvs);
});
},
insertLevel: function(level,callback) {
db.levels.insert(level, function (err, lv) {
if(err)
console.log(err);
else
callback(lv);
});
},
upsertLevel: function(level,callback) {
db.levels.update({_id: level._id}, {_id: level._id,price: {t1:{},t2:{},t3:{}}}, { upsert: true }, function(err,numReplaced,lv){
if(err)
console.log(err);
else
callback(numReplaced,lv);
});
}
};
});
And finally my view:
<table class="table table-bordered table-hover">
<tr ng-repeat="level in levels">
<td>
{{level._id}}
</td>
<td>
<button ng-click="loadPrices(level)" type="button" class="btn btn-default" data-animation="am-fade-and-scale" data-template="frontend/components/level/views/level.edit.html" data-placement="center" bs-modal="modal">
<i class="fa fa-pencil-square-o"></i> Modifier
</button>
</td>
</tr>
</table>
Any help ?
You're using an old school callback, which takes place outside of the Angular digest. It's good that you got it working by calling $apply, but you should try modifying your service to return a Promise. Promises are the Angular way of performing async operations.
You can read up more about them at https://thinkster.io/a-better-way-to-learn-angularjs/promises and https://docs.angularjs.org/api/ng/service/$q.
Solved by putting $apply() in the asynchronous function :
LevelService.getAllLevels(function(lvs) {
$scope.levels = lvs;
$scope.$apply();
});

In Angularjs, how to update table data after deleting a row from it

I am using the angularjs to call Asp.net Web API. After I delete a row from the table?
I also did not find the solution in w3schools.
My HTML code
<tr ng-repeat="user in users | filter: search">
<td>{{ user.Id }}</td>
<td>{{ user.FullName }}</td>
<td>{{ user.Mobile }}</td>
<td>{{ user.Birthdate }}</td>
<td>{{ user.Gender }}</td>
<td>{{ user.AcademicLevel }}</td>
<td>
My Angularjs code
$scope.DeleteUser = function (id) {
UserID = $scope.users[id].Id;
$http.delete('/api/users/' + UserID).success(function (data) {
(function (data) {
$scope.error = "error " + data;
});
}
I searched in Stackoverflow, I found some of these where they did not work for me, they confused me.
$scope.refresh()
$scope.apply();
First of all, handling server requests in your controller is a bad idea. As a rule of thumb, use services to delegate calls to the server, and use the controller for "gluing" up your models to your view.
There are a few issues in your code, it should look something like this:
$scope.DeleteUser = function (id) {
var userId = $scope.users[id].Id;
$http.delete('/api/users/' + userId)
.success(function (data) {
$scope.error = "error " + data;
});
}
Even if the call to the server was successful, you never updated your front-end model. What you have read on other topics regarding $scope.refresh(), I suppose that has the purpose of fetching the data again, like so:
$scope.refresh = function(){
$http.get('/api/users')
.success(function(data){
$scope.users = data;
});
}
So in order the refresh your view, you must update your model.
$scope.DeleteUser = function (id) {
var userId = $scope.users[id].Id;
$http.delete('/api/users/' + userId)
.success(function (data) {
//either this
$scope.refresh();
//or this
$scope.users.splice(id,1);
});
}
Two solutions : once you've performed your deletion, remove the user from the users array by doing a slice :
$scope.DeleteUser = function (index) {
var UserID = $scope.users[index].Id;
$http.delete('/api/users/' + UserID).then(function(del) {
$scope.users.splice(index,1);
});
}
You can get the index of the user by using $index in your ng-repeat but as your code may be asynchronous this is not recommended.
Or, you can just make another get from your API once your deletion is resolved.
$scope.DeleteUser = function (index) {
var UserID = $scope.users[index].Id;
$http.delete('/api/users/' + UserID).then(function(del) {
$http.get('/api/users').then(function(data){
$scope.users = data;
});
});
}
But... What if the person who's using your application is deleting users faster than your API is able to ?
First solution will fail for sure, and the second will hardly maintain you DOM clean as long as all requests came back in the same order they were sent.
One solution is to prevent any request to the API as long as another is already pending.
In JavaScript as Functions are in fact executable Objects they can have properties. So we'll add a isBusy property to the DeleteUser function to know when it is already processing.
$scope.DeleteUser = function (index) {
if(!$scope.DeleteUser.isBusy){
var UserID = $scope.users[id].Id;
$scope.DeleteUser.isBusy = true;
$http.delete('/api/users/' + UserID).then(function(del) {
$scope.users.splice(id,1);
$scope.DeleteUser.isBusy = false;
});
}
}
EDIT:
Update code to use .then() on $http promise object as .success() is now deprecated.
Just remove the user from the array:
// Find index of the user
var index = $scope.users.indexOf(id);
// Remove user from array
$scope.users.splice(index, 1);
Also, why are you fetching the user's id, when you already have it?:
$scope.DeleteUser = function (id) {
$http.delete('/api/users/' + id).success(function (data) {
var index = $scope.users.indexOf(id);
$scope.users.splice(index, 1);
});
}

Resources