why variables keep being empty on factory angular? - angularjs

services.factory('profilFactory',['$q','$http',function($q,$http){
var factory2 =
{
profils : {},
getProfils : function(){
$dfd = $q.defer();
$http.get('data.json')
.success(function(data,status){
this.profils = data.profil;
$dfd.resolve(this.profils);
})
.error(function(data,status) {
$dfd.reject('erreur recuperation des profils');
});
return $dfd.promise;
},
getProfil : function(idProfil){
var profil={};
var profils = {};
factory2.getProfils().then(function(data){
profils= data;
console.log(profils);//all right until here profils has values
});
console.log(profils);// now profils is empty :\ and the foreach will not execute
angular.forEach(profils, function(value, key){
if(value.id == idProfil){
profil= value;
}
});
return profil;
}
};
return factory2;
}]);
This is a screenshot of the problem : method "getProfil"

To answer your question, "Why are the variables empty in the factory", it's because you are using a console.log statement in a location where the data has not yet been loaded from the server. To learn more, Google this: "angularjs http get promises"
services.factory('profilFactory',['$q','$http',function($q,$http){
var factory2 =
{
profils : {},
getProfils : function(){
$dfd = $q.defer();
$http.get('data.json')
.success(function(data,status){
this.profils = data.profil;
$dfd.resolve(this.profils);
})
.error(function(data,status) {
$dfd.reject('erreur recuperation des profils');
});
return $dfd.promise;
},
getProfil : function(idProfil){
var profil={};
var profils = {};
// Run a function to get data, "THEN" we run a function to process the data:
factory2.getProfils().then(function(data){
// Data has now been loaded so we can process it and return it.
profils = data;
angular.forEach(profils, function(value, key){
if(value.id == idProfil){
profil= value;
}
});
return profil;
});
console.log(profils); // This is EMPTY because it runs immediately after
// the factory2.getProfils() function which may need several seconds to
// load data. That's why "profils" is empty. The data hasn't loaded at
// this point.
//
// No data will be available at this level of the code. Don't try to access
// "profils" here! Only in your .then() function above.
}
};
return factory2;
}]);

Your console.log statement is outside of the callback. That is the issue. You need to console.log in the callback or use a watcher to update it when it gets changed. For future reference you should always copy and paste your code here.

Related

Why ng-repeate object not getting updated?

This is the function which load listings from server. Initially listings are displayed but when gets null response on applying filter, it still shows previous result and not clearing previous listings.
$scope.browseListing = function (strURL) {
$scope.CurrentTab = strURL;
$scope.getURL(strURL);
$http.post($scope.URL)
.then(function (response) {
if (response.data != 'null') {
$scope.Data = response.data;
$scope.TotalListingCount = $scope.Data.length;
$window.alert('Result is not null');
}
else {
$scope.TotalListingCount = '0';
$window.alert('Result is null');
$scope.Data = [];
}
}, function (response) {
$log.info(response);
});
};
Edited
How do I solve this so that on empty response previous listings gets cleared and shows no listings?
May be your scope does not update. Please try this below ( it's not 100% good approach, But at this time you can solve your issue)
if(!$scope.$$phase) {
$scope.$apply(
$scope.Data = [];
);
}
$scope.TotalListingCount = '0';
$window.alert('Result is null');
and please check your console having any error.
Update :
try another way like this (declare empty object globally)
.then(function (response) {
$scope.TotalListingCount = '0';
$scope.Data = [];
if (response.data != 'null') {
$scope.Data = response.data;
$scope.TotalListingCount = $scope.Data.length;
$window.alert('Result is not null');
}
else {
$window.alert('Result is null');
}
}
It's does not works well, then please share your filter code. Bcs the problem should be there.
The following create new scopes, and inherit prototypically: ng-repeat, ng-include, ng-switch, ng-view, ng-controller, directive with scope: true, directive with transclude: true.
So, use $parent with 'ng-repeate' to reference to parent scope instead of using newly created scope byng-repeat` as-
<tr ng-repeat="listing in $parent.Data | orderBy : sortColumn : SortDirection">
After adding $parent in ng-repeat to scope property in UI it updates UI as per changes.
Here is full description of scope

How to scope methods in AnjularJS

I have this controller that has several functions that call each other. On success, I want to return something to be displayed (located in the last function). For some reason, without errors, the return is not working but the console.log is. Can someone please tell me why the return does not work and give me a solution please. Thanks so much!
.controller("dayController", function(){
.controller("weatherController", function(){
this.currentWeatherToDisplay = function(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(gotLocation,initialize);
}
else{
alert("Device does not support geolocation");
initialize();
}
};
var currentLocation;
//get the location coords
function gotLocation(pos){
var crd = pos.coords;
currentLocation = loadWeather(crd.latitude+','+crd.longitude);
initialize();
}
function initialize(){
if(!currentLocation){
loadWeather("Washington, DC");
}
else{
loadWeather(currentLocation);
}
}
function loadWeather(location){
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
return html;
},
error: function(error) {
console.log(error);
return error;
}
});
}
});
Well, mmmm you are use some jQuery plugin to get the weather given a current location, and like almost every jQuery plugins this use callbacks call to works (success, and error) first one i recommend you to rewrite this method to something like this:
function loadWeather(location){
var defered = $q.defer();
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
defered.resolve(html);
},
error: function(error) {
console.log(error);
defered.reject(error);
}
});
return defered.promise;
}
Also you must inject the $q dependency to the controller, like this:
module.controller("weatherController", function($q){...}
or this
module.controller("weatherController", ['$q',function($q){...}
I recommend the last by minyfication improvement in angular, when you return a promise like the function loadWeather, you must understand some basic principles about the the $q (based kriskoval Q library), a promise is a expected value in the future, an have a method then to work with that data (its a very short concept), that means:
function gotLocation(pos){
var crd = pos.coords;
loadWeather(crd.latitude+','+crd.longitude)
.then(function(html){
//html contain the expected html from loadWeather defered.resolve(html)
currentLocation = html;
})
.catch(function(error){
//error contain the expected error by execute defered.reject(error)
// maybe gonna try to initialize here
initialize();
})
}
This must work, remember to change the initialize function to some like this:
function initialize(){
var promise;
if(!currentLocation){
promise = loadWeather("Washington, DC");
}
else{
promise = loadWeather(currentLocation);
}
promise.then(function(html){
// some logic with succesful call
}, function(error) {
// some logic with error call
})
}

Angular binding not updating with socket.io broadcast

I have a server variable named currentPlayer that binds properly in the emit 'init' call, but won't bind in my broadcast.emit. The variable turnNumber which is passed through from the client first updates properly. When the pass event is fired I can see currentPlayer switching values in the terminal, but I'm stumped as to why it doesn't update in the view.
I had some trouble binding to primitive types before, so I tried going an extra step and assigning currentPlayer to an object array: $scope.globalVars = [{ currentPlayer: data.currentPlayer }], but that didn't help.
//server
var currentPlayer = 1;
socket.emit('init', {
currentPlayer: currentPlayer
});
socket.on('pass', function (data) {
currentPlayer = currentPlayer == 1 ? 2 : 1;
console.log(currentPlayer);
socket.broadcast.emit('pass', {
number: data.turnNumber,
currentPlayer: currentPlayer
});
});
//client
socket.on('init', function (data) {
$scope.globalVar.currentPlayer = data.currentPlayer;
});
socket.on('pass', function (data) {
$scope.globalVar.turnNumber = data.number;
$scope.globalVar.currentPlayer = data.currentPlayer;
});
//click event
$scope.globalVar.turnNumber++;
socket.emit('pass', {
turnNumber: $scope.globalVar.turnNumber
});
<!-- html -->
{{globalVar.currentPlayer}} {{globalVar.turnNumber}}
UPDATE:
I realized I could handle the current turn logic in my angular controller. I'm no longer changing the variable on the server and trying to pass it back to the client. Maybe as Chandermani says it's simply out of angular's context.
//server
socket.on('pass', function (data) {
socket.broadcast.emit('pass', {
number: data.turnNumber,
currentPlayer: data.currentPlayer
});
});
//client
socket.on('pass', function (data) {
$scope.globalVar.turnNumber = data.number;
$scope.globalVar.currentPlayer = data.currentPlayer;
});
//click event
$scope.globalVar.currentPlayer = $scope.globalVar.currentPlayer == 1 ? 2 : 1;
$scope.globalVar.turnNumber++;
socket.emit('pass', {
turnNumber: $scope.globalVar.turnNumber,
currentPlayer: $scope.globalVar.currentPlayer
});
These calls seem to be execution outside the anuglar context. Try to use $scope.$apply in the socket.on handlers, like
socket.on('pass', function (data) {
$scope.$apply(function() {
$scope.globalVar.turnNumber = data.number;
$scope.globalVar.currentPlayer = data.currentPlayer;
});
});

Passing current tab url to server in angular resources fetch

I am trying to send the current tab url to the resource service { in param } .
but the global tablUrl is not having any value at
var url = "http://[localhost]/getProfile?domain="+tabUrl
but getting logged corrent at :
console.log(tabUrl);
this is my code :
var tabUrl;
angular.module('jsonService', ['ngResource'])
.factory('JsonService', function($resource) {
chrome.tabs.getSelected(null, function(tab) {
tabUrl = tab.url;
console.log(tabUrl);
});
var url = "http://[localhost]/getProfile?domain="+tabUrl
return $resource(url,{}, {
list : {
method : 'GET',
cache : true
}
});
});
template binding :
<body ng-controller="extensionCtrl">
this is controller :
app.controller('extensionCtrl', function($scope , JsonService) {
JsonService.get(function(data){
$scope.data = data;
});
});
First:
Please, don't use the deprecated chrome.tabs.getSelected. Use chrome.tabs.query instead.
Second:
chrome.tabs.getSelected/chrome.tabs.query are asynchronous. This means that execution continues while they do some work in the background and the specified callback is called when they are done.
So, in a case like this:
line 1: chrome.tabs.getSelected(null, funkyCallback);
line 2: var url = ...
line 3: return $resource(...);
...a possible (and very probable) order of execution is:
1. chrome.tabs.getSelected (starts retrieving the active tab in the background)
2. line 2 gets executed (at this time 'tabURL' is not set yet)
3. line 3 gets executed (returning something)
4. Once the the active tab is retrieved, 'funkyCallback' is called
(setting 'tabURL' after it is too late).
When using asynchronous APIs (such as most of the chrome.* APIs), you have to change the whole logic of your scripts to be in line with the asynchronous nature of the API calls.
E.g., you could achieve the same result like this:
HTML:
<html ng-app="jsonService">
...
<body ng-controller="extensionCtrl">
<p>{{jsonData}}</p>
...
JS:
var app = angular.module("jsonService", ["ngResource"]);
app.factory("JsonFactory", function($resource) {
var url = "http://localhost/getProfile?domain=:tabUrl";
var retObj = $resource(url, {}, {
list: {
method: "GET",
cache: true
}
});
return retObj;
});
app.controller("extensionCtrl", function($q, $rootScope, JsonFactory) {
chrome.tabs.query({ active: true }, function(tabs) {
JsonFactory.list({ tabUrl: tabs[0].url }, function(data) {
// On success...
$rootScope.jsonData = data;
}, function(data) {
// On error...
$rootScope.jsonData = "Error using JsonFactory.list(...) !";
});
});
});
See, also, this short demo that does something similarly asynchronous

Can't get waiting for function to return (with promises?) working in angular controller

I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}

Resources