Angular can't see my Array change in the view - angularjs

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();
});

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/

NodeJS + AngularJS $http get

I am using Nodejs and AngularJS and i am having a problem with filling a table in the front-end from a generated JSON file.
I have the below ejs file:
<% include layout %>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class= "panel-title"> <%=title %></h3>
</div>
<br>
<div
data-ng-app="projectionsModule"
data-ng-controller="projectionsController">
<div class="container">
<%include projectionsGrid%>
</div>
</div>
<script src="/bower_components/angular/angular.min.js"></script>
<script src="../../javascripts/app/projections/projectionsModule.js"> </script>
<script src="../../javascripts/app/projections/projectionsService.js"></script>
<script src="../../javascripts/app/projections/projectionsController.js"></script>
and the projectionsGrid.ejs as of below:
<table
data-ng-show="projections.length > 0"
class='table table-striped table-hover'
>
<tr class="success">
<td>Name</td>
<td>Age</td>
</tr>
<tr data-ng-repeat="projection in projections">
<td>{{projection.Name}}</td>
<td>{{projection.Age}}</td>
</tr>
</table>
The controller is the following:
angular.module("projectionsModule")
.controller("projectionsController", projectionsController);
projectionsController.$inject = ['$scope', 'projectionsService'];
function projectionsController($scope, projectionsService) {
// $scope.projections = [];
getAllProjections();
function getAllProjections() {
projectionsService.getAllProjections().
success(function (response) {
$scope.projections = response.projections;
alert(response.projections);
// console.log(response.projections[0]);
})
}
}
and the Service :
angular.module("projectionsModule")
.factory("projectionsService", projectionsService);
projectionsService.$inject = ['$http'];
function projectionsService($http) {
return {
getAllProjections : function () {
return $http.get('/getAllProjections');
}
};
}
It seems that the
projectionsService.getAllProjections().
success(function (response) {
$scope.projections = response.projections;
does not work.
In the browser with Inspect all files are loaded correctly.
The json file is the below:
{
"projections": [
{
"name": "Alex",
"age": "18"
}
]
}
The printscreen i get when i run it:
enter image description here
Could someone please help mew cause i really do not know what else to do.
Thank you.
Print screen with error:
enter image description here
Please see related plunker.
You are using promises wrong. If you need explanation, ask me in a comment.
// Mandatory code for plunkr link
In your case, instead of returning an object, just make a $http call and delete the timeout. I'll let you do it, but if you need help, feel free to ask.
EDIT AFTER COMMENT you forgot to include $http as a dependency. You can also use $http.get, which is faster and (for me at least) easier to understand (see the documentation here)
angular.module("projectionsModule")
.factory("projectionsService", function($q, $http) {
return {
projectionFunction: projectionFunction
}
function projectionFunction() {
return $http.get('127.0.0.1:1337/getAllProjections', {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
});
EDIT WITH SOLUTION You need to use $q to use promises. Take a look into it, you will use it very often with your http requests. Your function should look like this :
angular.module("projectionsModule")
.factory("projectionsService", function($q, $http) {
return {
projectionFunction: projectionFunction
}
function projectionFunction() {
var defer = $q.defer();
$http.get('127.0.0.1:1337/getAllProjections', {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(success) {
defer.resolve(success);
}, function(error) {
defer.reject(error);
});
return defer.promise;
}
});
In your controller, you can then do something like that :
function getAllProjections() {
projectionsService.projectionFunction().then(function(success) {
$scope.projections = success.data.projections;
// do an alert(success) to see what is in it !
}, function(error) {
alert(error.data);
});
}

ng-repeat not displaying data when fetch via factory service

Need help to understand my mistake/ to display data on page.
I gone through almost every question on forum saying "ng-repeat not working". But still not able to find out the answer.
(Please note: First time I tried to fetch data by creating factory service with $http)
Please take a look at my below JavaScript code (Module, Controller and factory defined at one place)
//App declaration
var myApp = angular.module("appLeadsLogRpt", []);
//Controller declaration
myApp.controller('controllerLeadsLogRpt', ['dataService', fncontrollerLeadsLogRpt]);
function fncontrollerLeadsLogRpt(dataService) {
var vm = this;
//Table headers
vm.TableHeaders = ["Lead Id", "Source", "Create Date", "Status", "Contact Id", "Customer Name", "AssignedTo", "Mail Content", "Closed Reason", "Last Lead Note"];
dataService.getAllData()
.then(getData,null)
.catch(showError);
function getData(data) {
vm.LeadsLogRptData = JSON.parse(data);
//console.log(JSON.parse(data));
}
function showError(errMsg) {
console.log(errMsg);
}
}
//Factory Service to fetch data
myApp.factory('dataService', ['$http', DataService]);
function DataService($http) {
return {
getAllData: GetAllData
};
function GetAllData() {
return $http({
method: 'get',
url: 'DataHandler.ashx?method=leadsReport&listId=504473'
})
.then(sendResponseData)
.catch(sendError)
}
function sendResponseData(response) {
return response.data;
}
function sendError(response) {
return response.status;
}
}
</script>
And my Html like below:
<div id="DvContent" style="width:100%" data-ng-app="appLeadsLogRpt">
<div style="width:100%;" data-ng-controller="controllerLeadsLogRpt as vm1">
<input type="text" id="txtJsonData" value='<%=jsonLeadsLogRpt %>' style="display:none" />
<div><h3>Leads Log Report</h3></div>
<div style="text-align:right;margin-bottom:2px;padding-right:2px;">
<img src="/mp_images/excelicon.gif" border=0 width=22 height=22 alt="Open in Excel">
</div>
<div id="divExportToExcel">
<table style="width:100%" id="tblLeadLogRpt" class="table table-bordered">
<thead>
<tr style="background-color:#CCCCCC">
<th data-ng-repeat="item in vm1.TableHeaders" class="ng-cloack">{{item}}</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="item1 in vm1.LeadsLogRptData">
<td class="ng-cloack">{{item1.A_LeadId}}</td>
<td class="ng-cloack">{{item1.B_Source}}</td>
<td colspan="8"></td>
</tr>
<tr data-ng-if="LeadsLogRptData.length==0"><td colspan="10">Data Not Found</td></tr>
</tbody>
</table>
</div>
</div>
</div>
If I assign hard coded data return by server to ng-repeat it works fine
Please let me know what I am doing wrong.
One more question.
In factory I fetch data by calling get method of $http
If I want to call it by Post method, how do I pass params?
In Jquery, I doing it following way.
$.ajax({
url: 'AbcdHandler.ashx',
type: 'POST',
data: {
'method': 'ABCData',
'StartDate': startDate,
'EndDate': endDate
},
success: function (result) {
return JSON.parse(result);
},
error: OnError
});
Thanks in advance for reading and helping.
My latest observation:
when I write data on console, got this. (take a look on function getData(data))
[{"A_LeadId":"426429","B_Source":"LabX"},{"A_LeadId":"429369","B_Source":"LabX"},{"A_LeadId":"430586","B_Source":"Info"},{"A_LeadId":"430589","B_Source":"Info"},{"A_LeadId":"433848","B_Source":"LabX"},{"A_LeadId":"448592","B_Source":"Info"},{"A_LeadId":"451795","B_Source":"Bid"},{"A_LeadId":"453008","B_Source":"Low Bid"},{"A_LeadId":"453009","B_Source":"Low Bid"},{"A_LeadId":"453010","B_Source":"Low Bid"},{"A_LeadId":"455736","B_Source":"Info"},{"A_LeadId":"455743","B_Source":"Info"},{"A_LeadId":"457030","B_Source":"Info"},{"A_LeadId":"457052","B_Source":"LabX"},{"A_LeadId":"461503","B_Source":"Manually Entered"}]
If I copy this and directly assign to vm.LeadsLogRptData, system shows me output on screen properly.
e.g. vm.LeadsLogRptData = [{"A_LeadId":"426429","B_Source":"LabX"},......];
One more thing. When I check length of data, it shows me 621. ({{vm.LeadsLogRptData.length}})
Actually there are only 15 curly brackets pair ({}) and system should show me length as 15
Hope I explain/describe my issue correctly.
(Need something which converts my data properly in Json format)
Thanks,
I got answer
Just use eval with Json.parse and system start displaying data properly
e.g. vm.LeadsLogRptData = eval(JSON.parse(data));
Anyone please explain me the logic behind this?
Thanks to every one who read and replied so far.
Maybe you should use params property of $http object, like:
function GetAllData() {
return $http({
method: 'GET',
url: 'http://localhost:12345/DataHandler.ashx',
params: {method: "leadsReport", listId: 504473}
})
.then(sendResponseData)
.catch(sendError)
}
If you want to perform POST request, you should use it like this:
function PostData() {
return $http({
method: 'POST',
url: 'http://localhost:12345/DataHandler.ashx',
data: {exampleData: exampleData}
})
.then(sendResponseData)
.catch(sendError)
}
Remember to change http://localhost:12345/ for your server URL.

AngularJS : data binding not working as expected using a service

I'm having trouble with services in AngularJS.
Being a newbie it's probably something crucial I'm missing here.
The title {{p01g.visiteTitel}} isn't magically refreshing but keeps displaying "sometitle".
The ng-repeat is working as expected.
(dataFactory is a service that connects to a remote server using $resource)
My service :
myApp.service('p00Service', ['dataFactory', function(dataFactory) {
var service = this;
service.visites = [];
service.visiteAantal = 0;
service.visiteTitel = "sometitle";
service.findVisites = function (datum) {
dataFactory.get({verb: "search", q: datum}, function (data) {
angular.copy(data.visites, service.visites);
service.visiteAantal = service.visites.length;
if (service.visiteAantal === 0) {
service.visiteTitel = "geen visites op " + datum
} else if (service.visiteAantal === 1) {
service.visiteTitel = "1 visite op " + datum
} else {
service.visiteTitel = service.visiteAantal + " visites op " + datum
}
});
};
}]);
My controller :
myApp.controller('p01gCtrl', ['p00Service', function (p00Service) {
var vm = this;
var datum = moment(); //I'm using moment.js -> moment() is date of today
p00Service.findVisites(datum);
vm.visites = p00Service.visites;
vm.visiteTitel = p00Service.visiteTitel;
}]);
My HTML :
<div class="p01g" ng-controller="p01gCtrl as p01g">
<div class="well_grey" style="min-height:40px;max-height:40px;max-width:330px">
<p style="font-size:20px;text-align:center;cursor:pointer;">
{{p01g.visiteTitel}}
</p>
</div>
<div class="well" style="min-height:190px;max-height:190px;max-width:330px">
<table style="width:100%;line-height:40px">
<tbody ng-repeat="visite in p01g.visites">
<tr>
<td style="width:20%;line-height:40px;padding-left:7px"><span style="font-size:16px">{{visite.t133datum | date:"dd/MM/yy"}}</span></td>
<td style="width:60%;line-height:40px;text-align:center"><span style="font-size:16px">{{visite.t133achternaam}}</span></td>
<td style="width:20%;line-height:40px;padding-left:30px"><span style="font-size:16px">{{visite.t133classificatie}}</span></td>
</tr>
</tbody>
</table>
</div>
</div>
the dataFactory looks like this :
myApp.factory("dataFactory", ['$resource', function ($resource) {
return $resource("/vf/rest/visites/:verb", {}, {
get: {method: "GET", isArray: false, cache: false}
});
}]);
I copied your code exactly and created a plunkr and added my own dataFactory to return sample data and there are no errors and the data is getting bound fine, so I believe the issue is in your implementation of dataFactory, or the way you are calling it (make sure it's expecting a callback function, since that is what you are passing it).
Edit: Here is a new plunkr with updated code that I believe reproduces your issue. So the reason why the title is not getting updated is because
vm.visiteTitel = p00Service.visiteTitel;
is setting vm.visiteTitel to the value of the string in p00Service.visiteTitel, but it is NOT getting a reference to p00Service.visiteTitel, so if you update visiteTitel in your p00Service after this assignment (which is happening in this case because the $resource callback is async), then it has no effect on vm.visiteTitel.
One way to make this work would be to pass a callback to p00Service to update your controller values like so:
p00Service.findVisites(datum, function(visites, visiteTitel) {
vm.visites = visites;
vm.visiteTitel = visiteTitel;
});
and then update your service to call this callback after the data is loaded:
service.visites = [];
service.visiteAantal = 0;
service.visiteTitel = "sometitle";
service.findVisites = function (datum, callback) {
dataFactory.get({}, function (data) {
...
if(callback) {
callback(service.visites, service.visiteTitel);
}
});
};
This code can be cleaned up a bit, but here is a workable plunkr demonstrating vm.visites and vm.visiteTitel getting updated correctly.

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).

Resources