I am trying to implement a controller which has its scope variable being set by service variable like this :
$scope.sidebar= resourceService.sidebar;
The variable sidebar is set by a function called on startup:
var cb = function (api, data) {
for (var key in data) {
var logoArray = data[key];
service.sidebar[key] = logoArray.map(function (logo) {
logo.img = api + "/" + logo.img;
return logo;
});
}
}
service.requestOnStartup = function (api) {
var defer = $q.defer();
$http.get(config.ApiEndpoint.Base + api).success(function (data) {
if (angular.isObject(data)) {
defer.resolve(cb(api, data));
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
defer.reject(data);
}
}).error(function (msg) {
$log.error("Invalid request");
defer.reject(msg);
});
return defer.promise;
};
While the control reaches the scope, the service variable is still not resolved and by the time it is resolved, the control over scope is lost. How do i tackle this problem using promises ?
Your code looks fine if you are displaying $scope.sidebar directly. The variable will be filled asynchronously so it will come after a moment.
If you are doing some action on it while the controller is loading, you'll need to use $watch
$scope.$watch('sidebar', function() {
if ($scope.sidebar) {...} // check for existence here
});
By the way, you can simplify your requestOnStartup like this
service.requestOnStartup = function (api) {
return $http.get(config.ApiEndpoint.Base + api).then(function (data) {
if (angular.isObject(data)) {
return cb(api, data);
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
return $q.reject(data);
}
}, function (msg) {
$log.error("Invalid request");
return $q.reject(msg);
});
};
You can also use the existing $q service of angular js:
service.requestOnStartup = function (api) {
return $q(function(resolve, reject){
$http.get(config.ApiEndpoint.Base + api).then(function (data) {
if (angular.isObject(data)) {
resolve(cb(api, data));
} else {
$log.error("[ResourceService] Unexpected data from resource backend");
reject(data);
}
}, function (msg) {
$log.error("Invalid request");
reject(msg);
});
});
};
Related
Basically I am trying to scrap some data from website and perform the DOM extraction, deletion and updation on a callback function binded to the 'end' event of http.request.
I have returned the data from the 'end' event callback too but it is not receiving in my route callback function. I get undefined there.
Below is the code block:
var scraper = {
extractEmail: function (directoryName) {
var result = getDirectory(directoryName);
if (result !== 404) {
var protocol = result.https ? https : http;
protocol.request({
host: 'somevalue.net',
method: "GET"
}, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
return data;
});
})
.on('error', function (err) {
return err;
})
.end();
//return data;
}
else {
//return "Failed";
}
}
};
And here is the Routes.js function:
app.get('/:directory', function (req, res) {
var n = scraper.extractEmail(req.params.directory);
console.log(n);
res.send(n);
});
In here also I don't get the value of n.
Is your 'var scraper' also in the route.js file?
I guess it's not and you are unable to access that other js file,
for doing so use module.exports.
eg.
// module.js
var name = "foobar";
// export it
exports.name = name;
Then, in route.js...
> //route.js
> // get a reference to your required module
> var myModule = require('./module');
> //correct path to folder where your above file is
> // name is a member of myModule due to the export above
> var name = myModule.name;
You cannot return a value from an asynchronous callback. Well, you can, but it most likely will get ignored and most certainly won't do what you want.
You cannot even return a promise in that place. You can only resolve a promise where you now use the return statements. You need to return a promise from the main function and then resolve or reject the promise in your event handlers where you use returns now.
For more info see those answers:
Return Promise result instead of Promise in Nodejs
Return value in function from a promise block
jQuery: Return data after ajax call success
The simplest modification that will make this work is to pass a callback function to extractEmail to receive the data once it's ready.
var scraper = {
extractEmail: function (directoryName, cb) {
var result = getDirectory(directoryName);
if (result !== 404) {
var protocol = result.https ? https : http;
protocol.request({
host: 'somevalue.net',
method: "GET"
}, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
cb(null, data);
});
})
.on('error', function (err) {
cb(err);
})
.end();
}
else {
cb(new Error('Failed'));
}
}
};
And use it like this:
app.get('/:directory', function (req, res) {
scraper.extractEmail(req.params.directory, function(err, n) {
if (err) return console.error(err);
console.log(n);
res.send(n);
});
});
I'm trying to recursively call AWS's SNS listEndpointsByPlatformApplication. This returns the first 100 endpoints then a token in NextToken if there are more to return (details: AWS SNS listEndpointsByPlatformApplication).
Here's what I've tried:
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
return reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
return getEndpoints(platformARN, data.NextToken);
}
else {
console.log('trying to break out!');
return resolve(true);
}
}
});
});
}
I'm calling it with:
getEndpoints(platformARNDev, null)
.then(function(ret) {
console.log('HERE!');
}, function(err) {
console.log(err);
});
Problem is: the first call happens, then the recursive call happens, and I get the message trying to break out! but the HERE! never gets called. I've got something wrong with how my promises are returning I think.
Grateful for pointers.
The problem is that you try and resolve/reject partially completed query. Here is a complete working example with dummy service. I incapsulated the data grabbing into it's own recursive function and only do resolve/reject when i've completely fetched all the data or stumbled upon an error:
// This is the mock of the service. It yields data and token if
// it has more data to show. Otherwise data and null as a token.
var dummyData = [0, 1, 2, 3, 4];
function dummyAsyncCall(token, callback) {
token = token || 0;
setTimeout(function() {
callback({
dummyDataPart: dummyData[token],
token: (typeof (dummyData[token]) == 'undefined') ? null : (token + 1)
});
});
}
// Here is how you would recursively call it with promises:
function getAllData() {
//data accumulator is sitting within the function so it doesn't pollute the global namespace.
var dataSoFar = [];
function recursiveCall(token, resolve, reject) {
dummyAsyncCall(token, function(data) {
if (data.error) {
reject(data.error);
}
if (!data.token) {
//You don't need to return the resolve/reject result.
resolve(dataSoFar);
} else {
dataSoFar = dataSoFar.concat(data.dummyDataPart);
recursiveCall(data.token, resolve, reject);
}
});
}
return new Promise(function(resolve, reject) {
// Note me passing resolve and reject into the recursive call.
// I like it this way but you can just store them within the closure for
// later use
recursiveCall(null, resolve, reject);
});
}
//Here is the call to the recursive service.
getAllData().then(function(data) {
console.log(data);
});
Fiddle with me
That's because you dont need to return resolve/reject, just call resolve/reject when the recursive call completes. A rough code would look like this
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
getEndpoints(platformARN, data.NextToken).then(function () {
resolve(true);
}).catch(function (err) {
reject(err);
});
}
else {
console.log('trying to break out!');
resolve(true);
}
}
});
});
}
(caution: this is just a rough code, may work or may not, but is to give a general idea)
I've added a code snippet below, to support this concept, and it works great, check it out.
i = 0;
$('#output').empty();
function pro() {
return new Promise(function(resolve, reject) {
if (i > 3) {
resolve();
return;
}
window.setTimeout(function() {
console.log(i);
$('#output').append(i).append('<br/>');
i += 1;
pro().then(function() {
resolve()
}).catch(function() {
reject()
});
}, 2000);
});
}
pro().then(function () { $('#output').append("now here"); })
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="output"></div>
I've a function to update the DB rows one by one with Parse's promise
exports.update = function (items, successHandler, errorHandler) {
Parse.Cloud.useMasterKey();
var Item = Parse.Object.extend("Item");
Parse.Promise.as().then(function () {
var promises = [];
for (var i = 0; i < items.length; i++) {
(function (j) { //create new closure so i changes with each callback
var query = new Parse.Query(Item);
query.equalTo("sku", items[j]['sku'];);
promises.push(query.find({
success: function (results) {
if (results.length === 1) {
var object = results[0];
console.log('Item exists, now updating..');
return object.save(items[j]).then(function () {
console.log("Item saved"); // never called, why?
}, function (error) {
console.error("Item not saved with error: " + error.message); // never called, why?
});
}
},
error: function (error) {
console.error("Failure during querying..");
}
}));
})(i);
}
return Parse.Promise.when(promises);
}).then(function () {
return successHandler("Item updated.");
}, function (error) {
return errorHandler(error);
}
);
};
The problem is, the object.save is actually called and data is being saved in the DB, however, the following two promises are never called, not matter success or not.
I think because the Promise you return from your inner success function is not going to be necessarily fulfilled. You better promisify the query.find() function with a .then() instead
promises.push(query.find().then(function(results) {
if (results.length === 1) {
var object = results[0];
console.log('Item exists, now updating..');
return object.save(items[j]);
}
}).then(function() {
console.log("Item saved");
}, function (error) {
console.error("Item not saved with error: " + error.message);
});
}));
I have this service which checks if there a new data from the back end. It it working fine.But the problem is I cant get the data from the service to the controller using $watch nor using the promise.
SERVICE
.service('notificationPollService',function($q, $http, $timeout){
var notification={};
notification.poller = function(){
return $http.get('some/routes/')
.then(function(response) {
$timeout(notification.poller, 1000);
if (typeof response.data === 'object') {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
$timeout(notification.poller, 5000);
return $q.reject(response.data);
});
}
notification.poller();
return notification;
})
WATCH IN THE CONTROLLER
$scope.$watch('notificationPollService.poller()', function(newVal){
console.log('NEW NOT', response) // does nothing either.
}, true);
PROMISE IN THE CONTROLLER
notificationPollService.poller().then(function(response){
console.log("NEW NOTI", response) // not logging every poll success.
});
Is there a way that I missed on how to solve this? Or am I just doing something wrong?
Probably using promise in this case is not the most convenient approach because it is not supposed to be resolved multiple times. You can try to implement poller with old plain callbacks, you can call them repeatedly without need to create new instance of the promise:
.service('notificationPollService', function ($q, $http, $timeout) {
var notification = {};
notification.poller = function (callback, error) {
return $http.get('some/routes/').then(function (response) {
if (typeof response.data === 'object') {
callback(response.data);
} else {
error(response.data);
}
$timeout(function(){
notification.poller(callback, error);
}, 1000);
});
};
return notification;
});
notificationPollService.poller(function(data) {
$scope.data = data; // new data
}, function(error) {
console.log('Error:', error);
});
I am calling the getWeeklyDates which is calling the cachingGlobalConfigurationService which is again calling the globalConfigurationService if the globalConfiguration data could not be found in the localstorage.
The code =>
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
works fine when the globalConfiguration is not cached yet because then I make the ajax call and return a promise.
But the above line of code with .then(function(response1) is undefined when my globalConfiguration can be found in the localStorage and just this is returned:
else {
return cachedGlobalConfiguration;
}
I guess I can not use .then in this case but I would like.
How can I fix that?
1
this.getWeeklyDates= function (projectId, currentDate) {
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
// do business logic
});
2
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (!cachedGlobalConfiguration) {
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
});
}
else {
return cachedGlobalConfiguration;
}
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: Only save to local storage when service.save was successfully
localStorageService.set('globalConfiguration', globalConfiguration);
globalConfigurationService.saveGlobalConfiguration(globalConfiguration);
}
});
3
'use strict';
angular.module('test').service('globalConfigurationService', function ($http) {
this.getGlobalConfiguration = function () {
// TODO get from db
var path = 'scripts/model/globalConfiguration.json';
return $http.get(path).then(function (response) {
return response.data.globalConfiguration;
});
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: save on db
//var path = 'scripts/model/globalConfiguration.json';
//return $http.post(path, globalConfiguration).then(function (response) {
// alert('global configuration was saved succesfully!');
//});
}
});
You can inject $q service and use $q.when to wrap the object while returning, so that way you are always returning a promise from your api (and just removed the redundant else). Also remember to reject the promise from catch callback of the promise (if required).
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService, $q) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (cachedGlobalConfiguration) {
//Return a promise
return $q.when(cachedGlobalConfiguration);
}
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
return $q.reject(error); //<-- reject
});
};
//....
});
$q.when - Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.