Filtering and $http promises in Angular - javascript

I've got a problem with filtering data from JSON file, which is an array of 20 objects.
in my factory I have these two functions.
function getData() {
return $http
.get('mock.json')
.success(_handleData)
.error(_handleError);
}
function _handleData(data) {
var filteredData = _filterData(data, "name", "XYZ");
console.log('filteredData', filteredData);
return filteredData;
}
and here console.log("filteredData") shows only filtered elements (i.e. 3 out of 20);
next - in a service I've got this one on ng-click:
var filterMe = function () {
DataFactory
.getData(_address)
.success(_handleServiceData );
}
where
var _handleServiceData = function (data) {
filtered = data;
};
the thing is - why the 'data' in _handleServiceData shows all of the elements instead of these previously filtered?
edit: here's the plunk - results are logged in console

Because the filteredData you return from _handleData function is not passed to the success callback you attach in filterMe function. That's because you attach that callback on the very same promise, since success function doesn't create new promise like the then method does. So to solve this modify your code like this:
function getData() {
return $http
.get('mock.json')
.then(_handleData, _handleError); //use "then" instead of "success"
}
Then in filterMe function:
var filterMe = function () {
DataFactory
.getData(_address)
.then(_handleServiceData );
}

Because promises are asynchronous, and you seem to return the value of filtered to your caller before it could be assigned.
You should be doing
function getData() {
return $http
.get('mock.json')
.then(_handleData); // then (for chaining), not success!
}
var filterMe = function () {
return DataFactory
// ^^^^^^ return a promise, not assign globals in async callbacks
.getData(_address)
.catch(_handleError); // I assume you want to deal with errors only in the end
}

Related

Chrome Extension | Is there any way to make chrome.storage.local.get() return something?

in my chrome extension I need to use chrome storage. In my background script first I create an object and add it to chrome storage and then I want to get my object from there and to be returned. Something like that:
...
var obj = {};
chrome.storage.local.set(obj, function () { });
...
var data = getData(obj); // I want my object to be returned here
var returnedData = null;
function getData(obj) {
chrome.storage.local.get(obj, function(result) {
returnedData = result; // here it works, I can do something with my object
});
return returnedData; // here it doesn't work
}
As far as I understood from here chrome.storage.local.get is asynchronous with its consequences. But is there any way how to get something from chrome storage and make it to be returned? I mean maybe I should wrap chrome.storage.local.get in another function or so?
Many thanks in advance!
If you want to stay away from global variables and you're okay with modern browser requirements, then you can implement a native JavaScript Promise object. For example, here's a function that returns the stored data for a single given key:
function getData(sKey) {
return new Promise(function(resolve, reject) {
chrome.storage.local.get(sKey, function(items) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
reject(chrome.runtime.lastError.message);
} else {
resolve(items[sKey]);
}
});
});
}
// Sample usage given this data:
// { foo: 'bar' }
getData('foo').then(function(item) {
// Returns "bar"
console.log(item);
});
If you need support for IE11 and below, then you'll have to turn to a library like jQuery.
No it's not possible
But there are several ways around this problem
Do everything you want to do with the data returned from .get() inside the callback (or start it from there using function calls). This is what #wernersbacher posted
Take a look at deferreds (jQuery or Q libraries). A deferred's promise can be returned from getData. Inside the .get() callback, you can resolve the deferred. Outside of getData you can use .then() to do something after the deferred resolved
Something like this
function getData(obj) {
var deferred = $.Deferred();
chrome.storage.local.get(obj, function(result) {
deferred.resolve(result);
});
return deferred.promise();
}
$.when(getData(obj)).then(function(data) {
// data has value of result now
};
You have to do it like that:
var returnedData = null;
function setData(value) {
returnedData = value;
}
function getData(obj) {
chrome.storage.local.get(obj, function(result) {
setData(result); // here it works, I can do something with my object
});
return; // here it doesn't work
}
..because you tried to return a value which did not get read from storage yet, so it's null.
Update with Manifest V3 :
Now chrome.storage.local.get() function returns a promise that you can chain or can await in an async function.
const storageCache = { count: 0 };
// Asynchronously retrieve data from storage.local, then cache it.
const initStorageCache = chrome.storage.local.get().then((items) => {
// Copy the data retrieved from storage into storageCache.
Object.assign(storageCache, items);
});
Note : You must omit the callback paramter to get the promise.
Reference : https://developer.chrome.com/docs/extensions/reference/storage/#:~:text=to%20callback.-,get,-function
You need to handle it with callback functions. Here are two examples. You use a single function to set, however you create a separate function for each "On Complete". You could easily modify your callback to pass additional params all the way through to perform your needed task.
function setLocalStorage(key, val) {
var obj = {};
obj[key] = val;
chrome.storage.local.set(obj, function() {
console.log('Set: '+key+'='+obj[key]);
});
}
function getLocalStorage(key, callback) {
chrome.storage.local.get(key, function(items) {
callback(key, items[key]);
});
}
setLocalStorage('myFirstKeyName', 'My Keys Value Is FIRST!');
setLocalStorage('mySecondKeyName', 'My Keys Value Is SECOND!');
getLocalStorage('myFirstKeyName', CallbackA);
getLocalStorage('mySecondKeyName', CallbackB);
// Here are a couple example callback
// functions that get executed on the
// key/val being retrieved.
function CallbackA(key, val) {
console.log('Fired In CallbackA: '+key+'='+val);
}
function CallbackB(key, val) {
console.log('Fired In CallbackA: '+key+'='+val);
}

initialize $scope variable using $resource and $q

I am trying to initialise a Angualr scope variable using a function that calls resource.query() from service and its returning empty.
Below is my code
Controller
$scope.categories = _processedCategories($scope.excludedCategories);
// excluded categories is an array of objects
function _processedCategories(excludedCategories) {
preDefinedCategoryService.rest.query({},
function (data) {
categories = _.filter(data._embedded.categories, function (n) {
return (n.id !== excludedCategories.id);
});
return categories
},
function () {
console.log ('error');
});
}
I am getting $scope.categories empty, but when I $scope.categories = _.filter(..) instead, it populate them directly. I know its something related to $promise but how to resolve it according to my requirement? Becase I want to reuse this function later aswell.
You can't return from asynchronous operation. Use Promise API to provide a callback to be invoked when data is loaded and processed:
_processedCategories($scope.excludedCategories).$promise.then(function(categories) {
$scope.categories = categories;
});
// excluded categories is an array of objects
function _processedCategories(excludedCategories) {
return preDefinedCategoryService.rest.query({}, function (data) {
return categories = _.filter(data._embedded.categories, function (n) {
return (n.id !== excludedCategories.id);
});
}, function () {
console.log('error');
});
}
Note, that _processedCategories now returns result of preDefinedCategoryService.rest.query function invocation, which is promise object.

Trouble with jQuery when on an array of promises

function bar() {
var promiseArray = [];
$.each(options, function (index, option) {
promiseArray.push(
someAjaxFunction(option)
);
});
return $.when.apply($, promiseArray);
}
I have this function bar()
Which is called in a situation like:
foo().then(bar()).then(function (data) { console.log(arguments); console.log(data); });
Which logs out
["OK↵sdfadsfasd/sdfadsfads"]
(where the random letters are string related to the context but not remotely what it should return)
If inside bar instead of returning the promise, I do:
return $.when.apply($, promiseArray).then(function(){ console.log(arguments) });
That works as expected and returns an array of objects that are the contents of the ajax request bodies
I'm guessing it is something to do with the $.when.apply being on an unspecified number of ajax calls
Can anyone see what I'm doing wrong?
You need to pass the bar function as a reference.
foo().then(bar())
should be
foo().then(bar)
.then(bar()) would mean that you are passing a deferred to then as an argument. You need to pass a function to then as an argument. The function you pass may return a deferred.
If you'd like bar to still execute immediately as .then(bar()), you'll need to rewrite bar such that it returns a function that returns a deferred:
function bar() {
var promiseArray = [];
$.each(options, function (index, option) {
promiseArray.push(
someAjaxFunction(option)
);
});
//this function gets passed to `.then` and its return value is used
//for the purpose of chaining deferreds
return function () {
return $.when.apply($, promiseArray);
};
}

do something when nested async function is finished

function hello() {
var arr = [];
$.get(url, function (data) {
var items = $(data).find("item");
$(items).each(function (idx, item) {
arr.push(item);
});
});
return arr; //undefined because nested loops are not finished processing.
}
How do I make sure that arr is populated before returning it?
There is no way to escape from asynchronous calls. You would need callbacks to get the result of the GET call.
function asynCall() {
var response;
// Ajax call will update response here later.
return response;
}
var responseFromFun = asyncCall(); // This will be undefined or null.
This is how your code works now. So response will always be undefined or null.
To get the response from Ajax calls pass a callback to the function when invoking it instead of assigning a response to it.
function asyncCall(callBack) {
var response;
$.get(...) {
response = someValueReturnedFromServer;
callBack(response);
}
// There wont be a return here
}
asyncCall(function(response){
// Do something with response now
});
The downside here is that if you are passing arr object (in your code) to some other function even that has to be changed to use callbacks !

Returning data from $.when with multiple $.getJSON calls

In the following code, I am retrieving data using $.getJSON (returned form the repository) and $.when as the last call is dependent on data from the first:
var getData =
function () {
var data = { userData: null, userTitles: null, userPage: null };
$.when(repository.getUserDetails().done(f1)),
repository.getUserPolicyTitles().done(f2)
.then(repository.getUserPage().done(f3));
function f1(userData) { data.userData = userData; console.log(data.userData) };
function f2(userTitles) { data.userTitles = userTitles; console.log(data.userTitles) };
function f3(userPage) { data.userPage = userPage; console.log(data.userPage) };
return data;
}
return {
getData: getData
};
Most of this works fine. However, I would like to return the data back to a calling module but it returns before the data is ready as I suppose you would expect.
What is the best way to achieve this?
Thanks
Davy
Your usage of deferred seems incorrect. This is my interpretation.
Also, you need to consider that once you start invoking asynchronous methods, you can't make a synchronous return call, which is what you're doing. Instead of returning data, you need to return a promise; then you provide the promise with a callback that will do stuff with the data
var getData = function () {
var myDeferred = $.Deferred();
var data = { userData: null, userTitles: null, userPage: null };
$.when(repository.getUserDetails().done(f1),
repository.getUserPolicyTitles().done(f2),
repository.getUserPage().done(f3)).then(
function(){ myDeferred.resolve(data); },
function(){ myDeferred.reject.apply(myDeferred, arguments); });
//... f1, f2, f3
return myDeferred.promise();
}
return {
getData: getData
};
Then, when you want to actually use the data, you do
your_library.getData().then(function(data){
// magic goes here
}, function(errors_of_some_sort) {
// error handling magic goes here
});
When using $.when, you can only get the data after all your AJAX calls have returned. So for example:
$.when($.getJSON('somewhere'), $.getJSON('somewhere2'))
.done(function(r1, r2){
//set your data object with the responses from the server here
});
So the short answer is you can't "return" the data you retrieved from the server, but you can assign a callback to use the data (or set to some entity of your module) when the data is ready.

Categories

Resources