AngularJS scope variables not updating in jQuery promise/deferred - javascript

I am building a single page application on AngularJS and I have a controller set up with a function that is run on a button click. This function runs with a promise. When the function is resolved I am updating a root variable and changing the $location path. But the root variable and $location dont seem to be updating.
Please note this all code is exampled from production
DOM:
<div ng-controller="ExampleController">
<button ng-click="button_function('I am a variable')">Run function</button>
</div>
Controller:
app.controller('ExampleController', ['$scope', '$location', function($scope, $location) {
$scope.button_function = function(variable) {
$scope.$root.show_something = true;
my_function.something(variable).done(function(data) {
if (data) {
$scope.$root.show_something = false;
$location.path('/go-to-path');
} else {
alert('Something went wrong');
}
}]);
};
}]);
This is the my_function code:
var my_function = {
something: function(variable) {
var deferred = $.Deferred();
var window = window.open('http://dynamic.url/', '_blank');
$(window).on('loadstart', function(e) {
var url = e.originalEvent.url;
if (url === 'http://dynamic.url/expected_response') {
window.close();
deferred.resolve({
key_1: 'data',
key_2: 'more data'
});
}
});
return deferred.promise();
}
};
All looks good right? But when the my_function.something(variable) is "done" the $location and $scope.$root.show_something don't seem to update.
Am I doing something wrong?
Thanks

You should return deferred.promise instead of deferred.promise().
--edit: my bad I didn't see you are not using $q as I misread.

I have found the fix.
In my controller after the deferred is "done" I wrapped my variables in $timeout
app.controller('ExampleController', ['$scope', '$location', '$timeout', function($scope, $location, $timeout) {
$scope.button_function = function(variable) {
$scope.$root.show_something = true;
my_function.something(variable).done(function(data) {
if (data) {
$timeout(function() {
$scope.$root.show_something = false;
$location.path('/go-to-path');
}, 1);
} else {
alert('Something went wrong');
}
}]);
};
}]);
Answer found here

Related

How to redirect in Angular 1.5?

Complete beginner to AngularJs, how do I redirect to a certain page in this service method?
The destination should be "/#/login", I've tried with $location and $window but I might very well be missing something.
Thanks
angular.module('app.services', ['ngRoute'])
.service('TaskPlannerApiService', [
'$http', '$q', '$timeout', function taskPlannerApiService($http, $q, $timeout) {
var me = this;
me.getDataAsync = function () {
var logincheck = true;
var apiCall = $q.defer();
var timeoutHanlder = $timeout(function () {
apiCall.reject('Timed out');
console.log('timed out');
}, 2000);
$http.get('/api/login/check').success(function (chk) {
console.log(chk);
logincheck = chk;
console.log(logincheck);
if (chk === false) {
logincheck = false;
}
if (logincheck == true) {
console.log("/api/taskplanner");
$http.get('/api/taskplanner/').success(function (data) {
$timeout.cancel(timeoutHanlder);
apiCall.resolve(data);
console.log(data);
}).error(function (data) {
$timeout.cancel(timeoutHanlder);
apiCall.reject(data);
console.log(data);
});
}
else {
console.log("redirect");
}
})
$timeout(function () { apiCall.notify('Retrieving Data . . .'); }, 0);
return apiCall.promise;
};
}
])
$location Inject this your controller
Next, if you want the place to use the below code.
$location.path('/login');
If you are using angularjs internal routes than you have to inject $location in your controller and give this code below.
$location.path( "/login" );
If you are using ui-router which is quite famous in angularjs then you will be dealing with states. so in such case you have to inject $state in controller and change your state using this:
$state.go('state name')

AngularJS - Service not injected properly into factory

firstly here is my factorys code:
angular.module('myModule').factory('actionsService', ['$http', '$window', '$q', 'jsUtils', 'dataService', 'perspectivesService', function($http, $window, $q, jsUtils, dataService, perspectivesService) {
'use strict';
//some functions
function buildMenuItem(item, targets, callInvoker) {
if(...) {
//some code
} else {
perspectivesService.removeExtension(item.id);
}
}
//some more functions
}]);
I get the following error:
ERROR TypeError: perspectivesService.removeExtension is not a function
I know the problem isn't comming from my perspectivesService, because I'm injecting it the same way into controllers and calling the same function (removeExtension) and it works fine. There obviously is a problem with the dependency injection here, but I can't figure out exactly what. From what I've read there shouldn't be a problem to inject my service into the factory and it's done the same way you would inject it into a controller.. Any ideas what I'm doing wrong here?
There is my perspectivesService code:
(function () {
'use strict';
angular.module('myModule').service('perspectivesService', perspectivesService);
perspectivesService.$inject = ['$http'];
function perspectivesService($http){
return {
removeExtension: removeExtension,
setEditEnabled: setEditEnabled,
isEditEnabled: isEditEnabled
};
function removeExtension(ext){
$http({
method: 'POST',
url: 'remove',
params: { removeExt: ext
}
}).then(function(resp){
console.log('Its was successfull');
});
}
function setEditEnabled(edit){
localStorage.setItem('editMode', edit);
}
function isEditEnabled(){
if(localStorage.getItem("editMode") === 'true'){
return true;
}else if(localStorage.getItem("editMode") === 'false') {
return false;
}else{
return false;
}
}
}
})();

Trouble signing out with Google Sign in (gapi) with angularjs

So I have tried the following templates trying to integrate this:
HTML:
<google-sign-in-button button-id="login-button" options="options"></google-sign-in-button>
CSS:
.directive('googleSignInButton', function() {
return {
scope: { buttonId: '#', options: '&' },
template: '<div></div>',
link: function(scope, element, attrs) {
var div = element.find('div')[0];
div.id = attrs.buttonId;
gapi.signin2.render(div.id, scope.options());
}
};
})
--
I've also just tried doing this in the header and using the regular sign in button:
HTML:
<div class="g-signin2" data-onsuccess="onSignIn"></div>
IN THE HEADER:
<script>
window.onLoadCallback = function(){
gapi.auth2.init({
client_id: '123.apps.googleusercontent.com'
});
}
</script>
No matter what I do, i can't figure out how to log a user out. In my controller, when i try and do gapi.auth.signOut(); it says gapi is undefined
EDIT:
I've also tried dabbling in this to log a person out on run but ideally i'd want to make a log out work anywhere and not just when the page loads. I just don't really know where to put it or the correct way to make it happen:
.run(function ($rootScope, $state) {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init();
auth2.then(function(){
auth2.signOut();
});
});
})
EDIT #2:
I also tried to create this factory with a resolve on my ui-router but it's not getting the data in time or at all
.factory('Auth', function($http, $state, $q, $rootScope) {
var factory = { loggedIn: loggedIn };
return factory;
function loggedIn() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init();
auth2.then(function(){
return auth2.isSignedIn.get();
});
});
}
})
EDIT #3:
I tried creating a service but I keep getting the following error for some reason to even test it:
Error: undefined is not an object (evaluating 'gapi.auth2.init')
.service('googleService', ['$http', '$rootScope', '$q', function ($http, $rootScope, $q) {
var self = this;
this.signedIn = function() {
auth2 = gapi.auth2.init();
auth2.then(function(){
return auth2.isSignedIn.get();
});
}
this.signOut = function(){
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
}])
.controller('login', ['$rootScope', '$scope', '$q', 'googleService', function ($rootScope, $scope, $q, googleService) {
console.log(googleService.signedIn());
}])
I build upon my fiddle from my previous answer on a related question.
Basically what I added was a function to the controller scope that would be called when a user clicks on the signout button.
angular.module('app', [])
.controller('MainController', ['$scope',
function($scope) {
$scope.isSignedIn = false;
...
$scope.signOut = function(){
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
$scope.$apply(function(){
$scope.isSignedIn = false;
console.log('User signed out.');
});
});
}
}
])
I used the code snippet provided by Google documentation and that seemed to work immediately.
Do pay attention when changing variables in scope, you have to wrap your scope changes in $scope.$apply for angular to force to check changes in scope.
You can find the full code in this fiddle.
(I will be removing this Google api project at some point, so replace the API Key with your own if it doesn't work)
This is demo code, so if you would actually put this in project, I'd recommend hiding some of the complexity behind services and directives.
Update: if you want to use a service, you'll have to use angulars promises heavily, see $q docs.
Here's a sample on how you could create a service using promises and callbacks.
There's no simple way to get around the callback hell. But I hope wrapping these things into a service will solve that partially.
Here's an updated fiddle taking advantage of the service.
This is the js code:
angular.module('app', [])
.controller('MainController', ['$scope','googleService',
function($scope, googleService) {
$scope.isSignedIn = false;
googleService.load().then(function(){
$scope.signIn = function(){
googleService.signIn().then(function(){
$scope.isSignedIn = googleService.isSignedIn();
});
};
$scope.signOut = function(){
googleService.signOut().then(function(){
$scope.isSignedIn = googleService.isSignedIn();
});
};
});
}
])
.service('googleService', ['$q', function ($q) {
var self = this;
this.load = function(){
var deferred = $q.defer();
gapi.load('auth2', function(){
var auth2 = gapi.auth2.init();
//normally I'd just pass resolve and reject, but page keeps crashing (probably gapi bug)
auth2.then(function(){
deferred.resolve();
});
addAuth2Functions(auth2);
});
return deferred.promise;
};
function addAuth2Functions(auth2){
self.signIn = function() {
var deferred = $q.defer();
auth2.signIn().then(deferred.resolve, deferred.reject);
return deferred.promise;
};
self.isSignedIn = function(){
return auth2.isSignedIn.get();
}
self.signOut = function(){
var deferred = $q.defer();
auth2.signOut().then(deferred.resolve, deferred.reject);
return deferred.promise;
};
}
}]);
Basically, inside the load function you wrap the complexity of loading gapi, and auth2. After the load promise resolved in your controller, you are certain that the signIn, signOut, etc will work because it is loaded.
I took a slightly different approach. Though, I am not good enough to explain why your code does not work and this works for me. Hopefully, someone else can help on this.
Here is my approach. Let me know if this helps.
login.html
<script>var gapiOnLoadCallback = function() { window.gapiOnLoadCallback(); }</script>
<script src="https://apis.google.com/js/platform.js?onload=gapiOnLoadCallback" async defer></script>
<div id="googleLoginButton"></div>
<button ng-show="signedIn" ng-click="signOut()">Sign Out</button>
login.js
angular.module('app', [])
.controller('LoginController', ['$window','$scope', function($window, $scope) {
$window.gapiOnLoadCallback = function() {
gapi.signin2.render('googleLoginButton', {
'onsuccess': onSuccess,
'onfailure': onFailure
});
}
var onSuccess = function(googleUser) {
// Success handling here
};
var onFailure = function() {
// Failure handling here
};
$scope.signOut = function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
// Some UI update
});
};
};
login-directive.js
angular.module('app', []).directive("loginButton", function() {
return {
restrict: 'E',
scope: {},
templateUrl: 'login/login.html',
controller: 'LoginController'
}
});

Angular Factory Not passing data back

I am trying to create an Angular Factory, this is based on a example from a plural site course http://www.pluralsight.com/training/player?author=shawn-wildermuth&name=site-building-m7&mode=live&clip=3&course=site-building-bootstrap-angularjs-ef-azure.
From debugging the code in Chrome it appears to run fine. I can see when I debug it that the service gets my data and puts it in my array but when I look at the controller in either $scope.data or dataService.data the arrays are empty. I don't see any javascript errors. I'm not sure what I'm doing wrong, any suggestions. I'm using AngularJS v1.3.15.
module.factory("dataService", function($http,$routeParams,$q) {
var _data = [];
var _getData = function () {
var deferred = $q.defer();
$http.get("/api/v1/myAPI?mainType=" + $routeParams.mainType + "&subType=" + $routeParams.subType)
.then(function (result) {
angular.copy(result.data,_data);
deferred.resolve();
},
function () {
//Error
deferred.reject();
});
return deferred.promise;
};
return {
data: _data,
getData: _getData
};});
module.controller('dataController', ['$scope', '$http', '$routeParams', 'dataService',function ($scope, $http, $routeParams, dataService) {
$scope.data = dataService;
$scope.dataReturned = true;
$scope.isBusy = true;
dataService.getData().then(function () {
if (dataService.data == 0)
$scope.dataReturned = false;
},
function () {
//Error
alert("could not load data");
})
.then(function () {
$scope.isBusy = false;
})}]);
On
return {
data: _data,
getData: _getData
};});
you have "data: _data," while your array is named just "data". Change the name of the variable to match and it will work:
var _data = [];
Why would you use deferred from $q this way?
The proper way to use $q:
$http.get("/api/v1/myAPI?mainType=" + $routeParams.mainType + "&subType=" + $routeParams.subType)
.success(function (result) {
deferred.resolve(result);
}).error(
function () {
//Error
deferred.reject();
});
And then in controller
dataService
.getData()
.then(function success(result) {
$scope.data = result; //assing retrived data to scope variable
},
function error() {
//Error
alert("could not load data");
});
In fact, there are some errors in your codes :
In your Service, you define var data = [];, but you return data: _data,. So you should correct the defination to var _data = []
you don't define _bling, but you use angular.copy(result.data,_bling);
One more question, why do you assigne the service to $scope.data : $scope.data = dataService ?
EDIT :
Notice that there 3 changes in the following codes:
comment the $scope.data = dataService;, because it makes no sense, and I think that $scope.data should be the data that the service returns.
$scope.data = dataService.data;, as I described in 1st point. You can see the result from the console.
In the if condition, I think that you want to compare the length of the returned data array, but not the data.
module.controller('dataController', ['$scope', '$http', '$routeParams', 'dataService',function ($scope, $http, $routeParams, dataService) {
// $scope.data = dataService;
$scope.dataReturned = true;
$scope.isBusy = true;
dataService.getData().then(function () {
if (dataService.data.length === 0){
$scope.dataReturned = false;
}else{
$scope.data = dataService.data;
console.log($scope.data);
}
},
// other codes...
})}]);

Angularjs cacheFactory : id is already taken

I am using the $cacheFactory to store a language JSON file for the app, and i run a factory method every new route like this:
index.js
$routeProvider
.when('/',{
'templateUrl':'views/home/index.html',
'controller':'Home',
'resolve': {
'onEnter': function ($rootScope, langFactory) {
return langFactory.getLangFile($rootScope.lang.appLang);
}
}
})
.when('/auth/login',{
'templateUrl':'views/auth/login.html',
'controller':'AuthLogin',
'resolve': {
'onEnter': function ($rootScope, langFactory) {
return langFactory.getLangFile($rootScope.lang.appLang);
}
}
})
factories.js
.factory('langFactory', ['$rootScope', '$window', '$http', '$cacheFactory', '$q', function ($rootScope, $window, $http, $cacheFactory, $q) {
var getLangFile = function (langCode) {
var deferred = $q.defer()
, cache = $cacheFactory('langCache');
if (!!!cache.get('cache' + $rootScope.lang.appLang)) {
$http.get(langCode + '-langFile-to-be-REMOVED.json').success(function (response) {
cache.put('cache' + $rootScope.lang.appLang, response);
deferred.resolve();
}).error(function (err) {
$window.console.error('Unable to retrieve app language: ' + err);
deferred.reject(err);
});
} else {
deferred.resolve();
}
return deferred.promise;
};
return {
'getLangFile':getLangFile
};
}])
On first page load it works , then if i browse, without refreshing, to auth/login i get a console error:
[$cacheFactory:iid] CacheId 'langCache' is already taken!
Seems like (since i call the factory method on every route) it cant use the same id !?
I actually dont know what to do to fix this, any help appriciated, thanks.
This is what worked for me:
cache = $cacheFactory.get('langCache') || $cacheFactory('langCache');
Ok thanks to a guy on IRC i fixed this problem, i just had to change this:
var getLangFile = function (langCode) {
var deferred = $q.defer()
, cache = $cacheFactory('langCache');
to
var cache = $cacheFactory('langCache')
, getLangFile = function (langCode) {
var deferred = $q.defer();

Categories

Resources