I'm trying to access a value inside some Javascript to use in an AngularJS service. I have stored the value inside a variable successfully but I am having issues getting that variable into my AngularJS service. Here is my code:
JS function:
function onNotification(e) {
$("#app-status-ul").append('<li>EVENT -> RECEIVED:' + e.event + '</li>');
switch( e.event )
{
case 'registered':
if ( e.regid.length > 0 )
{
$("#app-status-ul").append('<li>REGISTERED -> REGID:' + e.regid + "</li>");
// Your GCM push server needs to know the regID before it can push to this device
// here is where you might want to send it the regID for later use.
var regid = e.regid
console.log(regid);
}
break;
The variable regid is what I am trying to access.
AngularJS Service:
App.service('regID', function()
{
return{}
});
Angular Function:
App.controller('MainCtrl', function($scope, $state, regID, $window){
console.log('MainCtrl');
var pushNotification;
document.addEventListener("deviceready", onDeviceReady, false);
$scope.regID = regID;
$scope.regID.regID = $window.regid;
console.log($scope.regID);
function onDeviceReady()
{
pushNotification = window.plugins.pushNotification;
$("#app-status-ul").append('<li>registering ' + device.platform + '</li>');
if ( device.platform == 'android' || device.platform == 'Android'){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"460885134680",
"ecb":"onNotification",
});
} else {
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"onNotificationAPN"
});
}
}
function successHandler (result, $scope, regID, $window)
{
alert('result = ' + result);
}
function errorHandler (error)
{
alert('error = ' + error);
}
});
Every time I run this $window.regid comes back as undefined. Any ideas as to why?
So you have not assigned it to a global variable. You have assigned it to a local function scope variable.
To assign it to a global variable use window.regID rather than var regID.
Your regID service does nothing. It should return $window.regID.
This all being said, this is not the right approach. The correct approach would be a service that returns a promise. The service also listen to native javascript or jqlite events, and resolve the promise when the event is handled.
Related
Following is the code that makes an http request to MyApp's API for user profile data(like name, photo) to update the navbar.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$scope.userProfile = $userProfile;
console.log("$userProfile: " + JSON.stringify($userProfile));
setTimeout(function() {
console.log("$userProfile: " + JSON.stringify($userProfile));
}, 3000);
});
app.factory('$userProfile', function($http) {
var profile = null;
$http.
get('/api/v1/me').
success(function(data) {
profile = data;
console.log("profile after get: " + JSON.stringify(profile));
}).
error(function(data, $status) {
if ($status === status.UNAUTHORISED) {
profile = null;
console.log("profile if error: " + JSON.stringify(profile));
}
});
console.log("profile (finally): " + JSON.stringify(profile));
return profile;
});
app.directive('navBar', function() {
return {
controller: 'navBarController',
templateUrl: 'templates/nav_bar.html'
}
});
I am console logging to check for the unexpected results I am getting and the logs are as follows:
profile (finally): null
$userProfile: null
profile after get:
{"photo":"http://localhost:3000/img/1023.jpg","name":"Utkarsh Gupta"}
$userProfile: null
The first two log msgs are obvious as $http.get() is asynchronous so the profile is null as defined at the start of the function. But after the $http.get() function returns successfully, the profile var got updated as shown in the third log msg but the $userProfile service continues to be null.
Looks like your service is not injected into the controller.
Have you tried it that way?
app.controller('navBarController', ["$scope", "$userProfile", function($scope, $userProfile) {}]
Example here:
https://docs.angularjs.org/guide/services
In your service, you have declared var profile = null; and triggering the $http api call then immediately returning the variable profile which is null at the time of returning and later you are updating the variable once the api got response and you are expecting it should be propagated to the controller which is not the case.
As service is singleton in nature, instance will be created once and
never created/updated.
Hence, your code is not a recommended one to use a service. I have updated the code below where service will return a method called load to call the api which is getting triggered from the controller where $scope can be directly assigned with the response data.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$userProfile.load().
success(function(data) {
$scope.userProfile = data;
console.log("profile after get: " + JSON.stringify($scope.userProfile));
}).
error(function(data, $status) {
if ($status === status.UNAUTHORISED) {
$scope.userProfile = null;
console.log("profile if error: " + JSON.stringify($scope.userProfile));
}
});
});
app.factory('$userProfile', function($http) {
var getProfile = function() {
return $http.
get('/api/v1/me');
};
//console.log("profile (finally): " + JSON.stringify(profile));
return {
load: getProfile
};
});
app.directive('navBar', function() {
return {
controller: 'navBarController',
templateUrl: 'templates/nav_bar.html'
}
});
Note: Also, please don't use $ prefix to service, variable, controller names as this is reserved to AngularJS and may create
conflicts when you use the same name as AngularJS reservered keywords/services.
You need to first fix your services (factory). It needs to return and object. Right now you are just running async code in your service, no way for your controller to use it. Second once you fix your service (look at the code below) you need to create a function to get the user profile. The user profile function needs to return a promise since you are working with async code. Again look at the code below and I hope it helps.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$userProfile.get().then(function(response){
$scope.userProfile = response;
});
setTimeout(function() {
console.log("$userProfile: " + JSON.stringify($userProfile));
}, 3000);
});
app.factory('$userProfile', function($http) {
var self = {};
self.get = getProfile;
return self;
function getProfile(){
var profile = null;
return $http.get('/api/v1/me')
.success(function(data) {
return data.data;
})
.error(function(data, $status) {
if ($status === status.UNAUTHORISED)
return profile;
});
}
});
Instead of initialising profile as null at the top, you must initialise it as:
var profile = {};
And then tack on to that empty profile object your API returned data like:
$http.
get('/api/v1/me').
success(function(data) {
//like this
profile.data = data;
})
In your code, when the $userProfile-service function finishes it returns the profile simply as null. And even after the $http.get request is complete your $userProfile has the same null because now the updated profile variable is not accessible anymore as it is out of scope. But if you initialize and return profile as an object, you can access it even after the function has returned because objects are passed by reference in javascript. You can then access the content of the profile object in your controller the way you are alreday doing because now $userProfile is the same exact profile object that you declared above and not a null anymore and any update on that profile object anywhere in your entire code will be reflected wherever that object is being accessed.
EDIT : i get an error like this when i last checked in browser console.
TypeError: Cannot read property 'defer' of undefined
I need to call one $http request which gives the token that can be used to call another $http request and finally the required response.
Hence i am using promises to make it work synchronously.However the function does not get executed after the $q.defer() function
Following is my code:
$scope.firstTimeAuth = function($q) {
var deferred = $q.defer();
var ref = window.open('https://accounts.google.com/o/oauth2/auth?client_id=' + clientId + '&redirect_uri=http://localhost/callback&scope=https://www.googleapis.com/auth/fitness.activity.write &approval_prompt=force&response_type=code&access_type=offline', '_blank', 'location=no');
ref.addEventListener('loadstart', function(event) {
if((event.url).startsWith("http://localhost/callback")) {
requestToken = (event.url).split("code=")[1];
$http({
method: "post", url: "https://accounts.google.com/o/oauth2/token",
data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=http://localhost/callback" + "&grant_type=authorization_code" + "&code=" + requestToken
})
.success(function(data) {
defer.resolve(true);
accessToken = data.access_token;
refreshToken = data.refresh_token;
alert("firstTimeAuth success");
if(typeof(Storage) != "undefined") {
localStorage.setItem("refreshToken",refreshToken);
alert(localStorage.getItem("refreshToken"));
} else {
alert("Sorry, your browser does not support Web Storage...");
}
//functions here
})
.error(function(data, status) {
alert("ERROR: " + data);
defer.resolve(true);
});
ref.close();
}
});
return deferred.promise;
}
This is my second function
$scope.getAcessToken = function($q)
{
var deferred = $q.defer();
alert("inside getAcessToken function");
refreshToken = localStorage.getItem("refreshToken");
if(refreshToken)
{
$http({
method: "post", url: "https://accounts.google.com/o/oauth2/token",
data: "client_secret=" + clientSecret + "&grant_type=refresh_token" + "&refresh_token="+ refreshToken + "&client_id=" + clientId
})
.success(function(data){
accessToken = data.access_token;
alert("getAcessToken success" + accessToken);
deferred.resolve(true);
})
.error(function(data,status){
alert("ERROR: " + JSON.stringify(data) + status);
deferred.resolve(true);
});
}
else
{
$scope.firstTimeAuth();
}
return deferred.promise;
}
and i call them like this.
alert("not installed");
var lastSaved = $scope.getFirstEpochTime();
//walkthroug
//Registe
$scope.firstTimeAuth().then(function(){
alert("firstime done");
$scope.getDataSets().then(function(){
alert(" in the last block");/*
$scope.handleResponse().then(function(){
$scope.insert().then(function(){
$scope.select();
})
alert("done in installed");
})
*/})
})
Please let me know what is wrong with the code. i am very new to this.. thanks.
Are you injecting $q in your controller at first place.
angular.module('module.name').controller('ControllerName',
['$scope', '$q', function ($scope, $q) {
}]);
I am not really getting why are you passing $q to your function, you don't need that to be. $scope.firstTimeAuth = function($q) {
By defining a parameter for a function, you're creating a local variable which hides anything with the same name in outer scope. In your case you're defining:
$scope.firstTimeAuth = function($q) {}
And then you're invoking it like $scope.firstTimeAuth(); in many places. Since you don't pass anything, $q in the functions scope will be undefined. You should only inject it in the entire controller scope and remove such parameters specified in scope methods so that it doesn't hide the injected service.
Or if you must pass them around for some reason, do it properly.
I have a run block that is querying my server to check if the user is authenticated.
.run(function($http, userService){
var base_url = 'http://server.com:3000';
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
});
})
Later, I have another run that will require info from the first run block. The problem with this is that my run code has async code and I am not getting the true value at the first time to userService.getIsUserLoggedIn().
How can I tell angularjs to execute the second run block only after the first one has been completed?
The second run block:
.run(function($rootScope, $location, $state, userService){
//Run to handle authentication
var authOnly = ['/painel'];
var unAuthOnly = ['/home'];
var checkAuthRoute = function(url){
var exist = authOnly.indexOf(url);
return exist > -1;
};
var checkUnAuthRoute = function(url){
var exist = unAuthOnly.indexOf(url);
return exist > -1;
};
$rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());
if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
console.log('Aqui..');
evt.preventDefault();
$state.go('login');
}
});
})
Thanks
You can use callbacks to make chain async requests in Javascript. Something like this might work:
.run(function($http, $rootScope, $location, $state, userService){
var base_url = 'http://server.com:3000';
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
handleAuth($rootScope, $location, $state, userService);
});
})
And define this function before the .run code from above:
function handleAuth($rootScope, $location, $state, userService){
var authOnly = ['/painel'];
var unAuthOnly = ['/home'];
var checkAuthRoute = function(url){
var exist = authOnly.indexOf(url);
return exist > -1;
};
var checkUnAuthRoute = function(url){
var exist = unAuthOnly.indexOf(url);
return exist > -1;
};
$rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());
if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
console.log('Aqui..');
evt.preventDefault();
$state.go('login');
}
});
}
Or a much more popular alternative (to prevent constant chaining of callbacks which makes code unreadable--aka the Pyramid of Doom), is to use Promises.
A promise takes an async function and returns a promise, which you can use to chain requests (ex. the $http method returns a promise that you're using called success). It is not available in ECMAScript 5, but will be in 6. People have made a bunch of implementations of Promises, such as Kris Kowal's Q, and Angular has a stripped down version of this library called $q.
Because this is async call, technically first block is finished, but the call(s) inside it wasn't. The only thing that comes to my mind is adding promise into userService and communicate through it:
.run(function($http, $q, userService){
var base_url = 'http://server.com:3000';
var deferred = q.defer();
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
deferred.resolve();
});
userService.setPromise(deferred.promise);
})
And in second run:
.run(function(userService){
userService.getPromise().then(function(){
//code that requires first run to finish
});
})
But if somewhere you need code from second run to finish(I mean do smth only after second run), it will require same structure again, and that's not really good, so you need to change the logic.
I am trying to call a service from inside the function "onNotification(e, $scope, currentUser)" However every time I try and log it, it either comes back with the error:
processMessage failed: Error: TypeError: Cannot set property 'Current' of undefined
or if I use "var userRegistration = this.currentUser" I get "undefined" in the log, however I know that the service is being updated as it returns the correct value when I log the result outside the function.
Here's my code:
function onNotification(e, $scope, currentUser) {
var Currentuser = this.currentUser;
$("#app-status-ul").append('<li>EVENT -> RECEIVED:' + e.event + '</li>');
switch( e.event )
{
case 'registered':
if ( e.regid.length > 0 )
{
$("#app-status-ul").append('<li>REGISTERED -> REGID:' + e.regid + "</li>");
// Your GCM push server needs to know the regID before it can push to this device
// here is where you might want to send it the regID for later use.
console.log(Currentuser);
console.log("regID = " + e.regid);
}
break;
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if ( e.foreground )
{
$("#app-status-ul").append('<li>--INLINE NOTIFICATION--' + '</li>');
// on Android soundname is outside the payload.
// On Amazon FireOS all custom attributes are contained within payload
var soundfile = e.soundname || e.payload.sound;
// if the notification contains a soundname, play it.
var my_media = new Media("/android_asset/www/"+ soundfile);
my_media.play();
}
else
{ // otherwise we were launched because the user touched a notification in the notification tray.
if ( e.coldstart )
{
$("#app-status-ul").append('<li>--COLDSTART NOTIFICATION--' + '</li>');
}
else
{
$("#app-status-ul").append('<li>--BACKGROUND NOTIFICATION--' + '</li>');
}
}
$("#app-status-ul").append('<li>MESSAGE -> MSG: ' + e.payload.message + '</li>');
$("#app-status-ul").append('<li>MESSAGE -> MSGCNT: ' + e.payload.msgcnt + '</li>');
break;
case 'error':
$("#app-status-ul").append('<li>ERROR -> MSG:' + e.msg + '</li>');
break;
default:
$("#app-status-ul").append('<li>EVENT -> Unknown, an event was received and we do not know what it is</li>');
break;
}
}
Here's the Service within the same js file:
App.service('currentUser', function ()
{
return{};
});
How should I go about calling the service inside this function? My Angular knowledge is limited. Any help would be much appreciated.
Update: In response to user PSL. onNotification is called here:
App.controller('LogHomeCtrl', function($scope, $log, $state, currentUser)
{
var pushNotification;
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady()
{
pushNotification = window.plugins.pushNotification;
$("#app-status-ul").append('<li>registering ' + device.platform + '</li>');
if ( device.platform == 'android' || device.platform == 'Android'){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"460885134680",
"ecb":"onNotification"
});
} else {
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"onNotificationAPN"
});
}
}
function successHandler (result)
{
alert('result = ' + result);
}
function errorHandler (error)
{
alert('error = ' + error);
}
});
Try Changing
var Currentuser = this.currentUser;
to
var Currentuser = currentUser;
currentUser does not belong to onNotification it is meerly being passed in as a parameter and therefore being defined as a variable in its local scope. But not as a property of itself
Edit
onNotification ought to be defined inside a controller. eg
yourModuleName.controller('controllerName', ['$scope', 'currentUser', function($scope, currentUser) {
$scope.currentUser = currentUser;
$scope.onNotification = function(e, $scope, $scope.currentUser) {
}
}]);
On my web application, there are two kinds of users: guests & logged. The main page loads the same content for each.
My goal :
When a registered user clicks the link, 2 ajax requests ($http) retrieve
the data of another page and load them in a model.
If the user is a guest, another model appears saying that he has to register.
My link :
<h4 ng-click="guestAction($event, showOne($event,card.id));">click me</h4>
GuestAction :
$scope.guestAction = function($event, callbackB) {
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1)
{
alert('guest spotted !');
return false;
}
else
{
alert('user');
console.log(callbackB);
eval('$scope.'+callbackB);
}
});
}
This way, if a guest is spotted, we return false and stop the execution. If it's a regular user, we execute the function showOne. As I want to do 2 asynchronous requests one after the other, I chose to use the callback trick.
The problem is that showOne() is executed directly when ng-click is launched. I tried to pass showOne() as a string, and eval() the string in GuestAction, but the parameters become undefined...
Any idea how to solve this problem? I want to use a generic method which fires a function only if the user is logged.
I would recommend using a service and promises, see this AngularJS $q
You don't have to use a service for $http requests but that is just my preference, it makes your controller a lot cleaner
Here is the service with the promise:
app.factory('myService', function ($http, $q) {
var service = {};
service.guestAction = function () {
var deferred = $q.defer();
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
}).error(function (data) {
deferred.reject('Error checking server.');
});
return deferred.promise;
};
return service;
});
And then in our controller we would call it something like so:
app.controller('myController', function ($scope, myService) {
$scope.guestAction = function($event, card) {
myService.guestAction().then(function (data) {
if (data) {
alert('guest spotted !');
} else {
alert('user');
// Then run your showOne
// If this is also async I would use another promise
$scope.showOne($event, card.id);
}
}, function (error) {
console.error('ERROR: ' + error);
})
};
});
Now obviously you may have to change things here and there to get it working for your needs but what promises do is allow you to execute code and once the promise is returned then continue, I believe something like this is what you are looking for.
You have to pass functions as parameters without the parenthesis and pass in the parameters separately:
<h4 ng-click="guestAction($event,card.id, showOne);">click me</h4>
and
$scope.guestAction = function($event,id, callbackB) {
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1)
{
alert('guest spotted !');
return false;
}
else
{
alert('user');
callbackB($event,id);
}
});
}