I started to work with Angular two months ago so sorry in advance if my question is duplicate.
Although, I found similar question AngularJS Promises $q.all and SignalR I don't know how to fit that in my code.
I get data from the server by signalR and I want to show landing page as long as I don't get all the data from the server. I try to do that with $q.all but I got error. Bellow is my code and error that I get.
Here is my service:
var menuItemshub = new Hub(‘menuItemsHub’, {
rootPath: $rootScope.defaultRootPath,
//server side methods
methods: [‘getMenuItems’],
queryParams: {
‘token’: $rootScope.loggedInUser.accessToken,
},
//handle connection error
errorHandler: function (error) {
console.error(error);
}
});
var countrieshub = new Hub(‘countriesHub’, {
rootPath: $rootScope.defaultRootPath,
//server side methods
methods: [‘getCountries’],
queryParams: {
‘token’: $rootScope.loggedInUser.accessToken,
},
//handle connection error
errorHandler: function (error) {
console.error(error);
}
});
var menuItemshubInitDone = function () {
return menuItemshub.promise.done();
};
var countrieshubInitDone = function () {
return countrieshub.promise.done();
};
var getMenuItems = function () {
return menuItemshub.getMenuItems();
};
var getCountries = function () {
return countrieshub.getCountries();
};
Here is my controller
configurationService.menuItemshubInitDone().then(function () {
configurationService.getMenuItems().then(function (response) {
// Success
$rootScope.menuItems = response.MenuItems;
}, function (error) {
});
});
configurationService.countrieshubInitDone().then(function () {
configurationService.getCountries().then(function (response) {
// Success
$rootScope.countries = response.Countries;
$rootScope.selectedAction = $rootScope.countries;
$rootScope.setAction($rootScope.selectedAction[0]);
}, function (error) {
});
});
And I want to do something like:
var all = $q.all([configurationService.getCountries(),
configurationService.getMenuItems()]);
all.then(function () {
$rootScope.showLandingPage = false;
});
I get following error SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started. I tried with
$q.when([configurationService.menuItemshubInitDone()]);
and then to call $q.all but i get the same error again.
I have been trying to find the solution for few days by googling but I couldn't figure out what I need to do.
Thank you for help in advance.
I have found what I was doing wrong. Here is code that works fine now, in case if somebody else get stuck as I was:
$scope.userConfigurations = function () {
var all = $q.all([getMenuItems, getCountries]);
all.then(function () {
$rootScope.showLandingPage = false;
});
var getMenuItems = configurationService.menuItemshubInitDone().then(function () {
configurationService.getMenuItems().then(function (response) {
// Success
$rootScope.menuItems = response.MenuItems;
}, function (error) {
});
});
var getCountries = configurationService.countrieshubInitDone().then(function () {
configurationService.getCountries().then(function (response) {
// Success
$rootScope.countries = response.Countries;
$rootScope.selectedAction = $rootScope.countries;
$rootScope.setAction($rootScope.selectedAction[0]);
}, function (error) {
});
});
Related
function get (id, ignore) {
var deferred = $q.defer();
$http.get('v1/info/' + id, {
ignoreAuthModule: ignore
})
.success(function (data) {
deferred.resolve(data.data);
})
.error(function (reason) {
deferred.reject(reason.message););
});
return deferred.promise;
}
init();
function init(){
users.get($routeParams.id)
.then(function (data) {
if(data.has_something === 1){
$scope.hasSomething = true;
}else{
$scope.hasSomething = false;
}
});
}
I have a Service that get the information about user using promise and Fetching information from the service with init function
//if i call init function this should call two times one from function initialization and other i'm calling it from service
how can i stop two times calling api I mean it should call one time if already called
You're using the explicit promise creation antipattern here, and your code could be much simpler. Here is how you can use memoization to avoid requesting the same user twice:
.factory('users', ['$http', function ($http) {
var userPromises = {};
function get (id, ignore) {
if (!userPromises[id]) {
userPromises[id] = $http.get('v1/info/' + id, {
ignoreAuthModule: ignore
})
.then(function (data) {
return data.data;
})
.catch(function (reason) {
throw new Error(reason.message);
});
}
return userPromises[id];
}
return {
get: get
};
});
You can assign your deferred.promise to some variable and then return that variable, and before your http call just check whether that variable is already defined or not
function get (id, ignore) {
if (angular.isUndefined(user)) {
var deferred = $q.defer();
$http.get('v1/info/' + id, {
ignoreAuthModule: ignore
}).then(function(response) {
if (response.status == 200) {
deferred.resolve(response.data);
} else {
deferred.reject(response.data);
};
user = deferred.promise;
return user;
} else {
return user;
}
}
This way your api will get called only once.
I'm doing this login exercise where users can login and post notes, and view the notes that they've posted. My problem is when I logout and login with a different user I see the notes from the previous user.
Here's an illustration:
I log in with a different user then this shows up:
I restart the page and the appropriate note shows up:
The controller for this:
exports.homeController = function ($scope, $location, $q, $users, $window, $notes, $http) {
var auth = function () {
var userInfo = $users.getUserInfo()
if (userInfo) {
return $q.when(userInfo)
} else {
return $q.reject({ authenticated: false })
}
}
$scope.userInfo = auth()
myNotes($scope.userInfo.$$state.value.accessToken) // I invoke my function to get the notes for each specific user but it doesn't seem to work.
$scope.logout = function () {
$users.logout()
.then(function (results) {
$scope.userInfo = null
$scope.myNotes = null
$location.path('/')
}, function (err) {
console.log(err)
})
}
$scope.notes = {
notes: ''
}
$scope.postNote = function () {
$notes.postNotes($scope.userInfo.$$state.value.accessToken, $scope.notes)
.then(function (result) {
$scope.myNotes.push($scope.notes)
$scope.notes = ''
}, function (err) {
console.log(err)
})
}
function myNotes (user_id) {
$notes.getMyNotes(user_id)
.then(function (result) {
console.log(result)
$scope.myNotes = result.data
}, function (err) {
console.log(err)
})
}
}
This is the app https://login-sys.herokuapp.com/
I've found your non-minified code for the services.
Based on that I think the problem is that you declare var deferred = $q.defer() one time in the $notes service.
I think it should be "renewed" every time the service methods are called:
function getMyNotes (user_id) {
var deferred = $q.defer();
$http.get('/api/myNotes/' + user_id + '?access_token=' + user_id)
.then(function (result) {
deferred.resolve(result)
}, function (err) {
deferred.reject(err)
});
return deferred.promise
}
Similarly in postNotes.
The second time you return the same promise with the same value, so your homeController's getMyNotes function will get the same result despite the $notes service making a new request.
In the $users service's logout and signup functions you are already using it correctly.
I am new to angularjs.I am using factories where i have written the fb login code.
And during the last step i am sending all the data to my server where the user is registered in my database and the token is sent.
Here is the code.
'use strict'
APP.factory('authenticationFactory',['ENV','$http','$rootScope', function (ENV,$http,$rootScope) {
return {
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data).then(function (resp) {
if(resp.status == 200) {
return resp.data;
}
})
},
fbLogin: function () {
var FB = window.FB;
var scopes = 'public_profile,email';
var that = this;
FB.login(function (response) {
return that.facebookStatusChangeCallback(response);
}, {scope: scopes});
},
facebookStatusChangeCallback: function(response){
if (response.status === 'connected') {
// Logged into your app and Facebook.
var r = this.facebookApiRequest(response);
console.log(r);
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not your app.
console.log('Please log into this app.');
} else {
// The person is not logged into Facebook, so we're not sure if
// they are logged into this app or not.
console.log('Please log into Facebook.');
}
},
facebookApiRequest: function (authResponse) {
var that = this;
var r = FB.api('/me?fields=id,name,email,gender,first_name,last_name,age_range,link,birthday', function (response) {
var r = FB.api("/" + response.id + "/picture?height=720", function (pictureResponse) {
if (pictureResponse && !pictureResponse.error) {
/* handle the result */
response.profile_pic = pictureResponse.data.url;
response.access_token = authResponse.authResponse.accessToken;
response.provider = 'facebook';
response.devicetoken = '';
response.full_name = response.first_name+' '+response.last_name;
var r = that.socialPluginLogin(response).then(function (resp) {
return that.resp;
});
return r;
} else {
console.log('error while fatching fb pic');
}
});
console.log(r);
});
console.log(that);
},
socialPluginLogin : function (data) {
var resp = this.socialLogin(data).then(function (resp) {
return resp;
});
return resp;
}
};
}]);
I am calling the fbLogin() function from my controller. i need the response from the function socialLogin() so that i can change the state.
Where am i going wrong.??
The answer was pointing in the wrong direction, another try:
Your function fbLogin should return a promise, which can be resolved by socialLogin later. Since fbLogin doesn't return a thing, you don't receive any signal from the completed login.
See this:
// We add $q here
APP.factory('authenticationFactory',['ENV','$http','$rootScope','$q', function (ENV,$http,$rootScope,$q) {
var loginPromise;
return {
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data).then(function (resp) {
if(resp.status == 200) {
// This is your connection to the controller
loginPromise.resolve(resp.data);
return resp.data;
}
})
},
fbLogin: function () {
var FB = window.FB;
var scopes = 'public_profile,email';
var that = this;
FB.login(function (response) {
return that.facebookStatusChangeCallback(response);
}, {scope: scopes});
// Create and return a promise
loginPromise = $q.defer();
// EDIT: My fault, return the promise:
return loginPromise.promise;
},
//...
And add this to the controller:
authenticationFactory.fbLogin().then(function(data){
// Check it out:
console.dir(data);
})
Additional things you should consider:
Define your functions in the function body, not in the return statement. You can eliminate that=this this way
Only return the API, not all the functions
Read up on promises, they are the way to go in the angular world. You might as well use callbacks, but those are tedious to handle.
Change your socialLogin function to below, your function would return a promise object which you can consume in socialPluginLogin via then which you are already doing.
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data)
},
I am Learning Meteor and Javascript. I am using an npm package to get meta data of an url on the server side. This works fine. But I get undefined when passing that result back to client. Would appreciate some help.
Here is my code
if (Meteor.isClient) {
Meteor.call('getMetaData', "http://www.bbc.co.uk/news", function (err, data) {
if (err) {
console.log("error", err);
};
console.log("Meta data: " + data); //shows undefined
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
var preview = Meteor.npmRequire('page-previewer');
Meteor.methods({
getMetaData: function (url) {
preview(url, function (err, data) {
if (!err) {
console.log(data); //Works fine
return data;
}
});
}
})
});
}
You need to convert the preview function to an synchronous function,using Future like this, this will make this function wait normal err,data callbacks into a synchronous function.
var Future = Npm.require('fibers/future'),
preview = Meteor.npmRequire('page-previewer');
Meteor.methods({
getMetaData: function(url) {
var f = new Future();
preview(url, function(err, data) {
if (!err) {
return f.return(data);
}
});
return f.wait();
}
});
Now this snippet should work
if (Meteor.isClient) {
Meteor.call('getMetaData', "http://www.bbc.co.uk/news", function (err, data) {
if (err) {
console.log("error", err);
}else{
console.log("Meta data: " + data); //shows undefined
}
});
};
try using else block to get the meta data. here's a solution of a similar problem .
https://forums.meteor.com/t/client-getting-undefined-for-server-method/6129/4?u=faysal
so basically you need to add only one extra line
else{ console.log('metadata '+ data);}
I've got a call using Resource in angularjs but i get some problems because i can't abort every calls it does. This kind of structure i use for an autocomplete.. is it possible convert from resource call to http? This is the code
var Resource = $resource(URL, {},{ getAutocompleteResults: { method: "GET", params: {text: ""} }});
var locked = false;
function getMoreData() {
if(locked)
return;
locked = true;
Resource.autoCompleteResults()
.$promise.then(function(data) {
$scope.autocompleteViewResults = data;
locked = false;
});
}
This is what i've tried so far with no success.
$scope.autocompleteViewResults = function () {
$http
.get(URL, {
params: {
text = ""
}
})
.success(function (data) {
$scope.autocompleteViewResults = data;
});
};
Or if someone knows an alternative method..
The $scope.autocompleteViewResults variable is being assigned 2 times.
Try this:
$scope.autocompleteViewResults = {};
$scope.getResults = function(valueAsTyped) {
$http
.get(URL, {
params: {
text: valueAsTyped
}
})
.success(function (data) {
$scope.autocompleteViewResults = data;
});
};
Update
If you need to cancel old requests.
var promiseCanceller = $q.defer();
$scope.autocompleteViewResults = {};
$scope.getResults = function(valueAsTyped) {
promiseCanceller.resolve('request cancelled'); // cancel currently running request
$http
.get(URL, {
params: {
text: valueAsTyped
},
timeout: promiseCanceller.promise // pass promiseCanceller as the timeout
})
.success(function (data) {
$scope.autocompleteViewResults = data;
});
};