Cannot use .then() inside ng-click function - javascript

My controller looks like this
function menuController($scope, dataService) {
$scope.getMenuItem = '';
$scope.submenu = new Array();
var setMenuItems = function (w) {
$scope.submenu = new Array();
$.each(w, function (i, val) {
if ($scope.getMenuItem == 'year') {
$scope.submenu.push( {
name: val[0].year, link: year
});
}
});
}
$scope.UpdateSubmenu = function (a) {
$scope.getMenuItem = a;
loadData();
};
var loadData = function () {
dataService.getAlbums().then(setMenuItems);
}
loadData();
}
when loadData() is called during initialization it works fine. However when it is called from UpdateSubmenu it fails with TypeError: dataService.getAlbums(...).then is not a function
The UpdateSubmenu funtion is activated from an ng-click event.
The dataService looks like this
(function () {
"use strict";
var dataService = function ($http) {
var albums;
var getAlbums = function () {
if (albums)
return albums;
return $http.get("./api/Album").then(function (response) { albums = response.data; return albums; });
}
return { getAlbums: getAlbums };
}
angular.module("Main").factory("dataService", dataService);
}());
Why cant I use then() not accepted?

The issue is because you are caching the albums and returning that if it exists. The first time getAlbums() is called it returns the $http promise, the second time it returns an array of albums, which does not have a then() method.
I usually handle this by creating a promise using $q and instantly resolving it:
(function () {
"use strict";
var dataService = function ($http, $q) {
var albums;
var getAlbums = function () {
if (albums) {
var deferred = $q.defer();
deferred.resolve(albums);
return deferred.promise;
// ... or more simply ...
//return $q.when(albums);
}
else {
return $http.get("./api/Album")
.then(function (response) { albums = response.data; return albums; });
}
}
return { getAlbums: getAlbums };
}
angular.module("Main").factory("dataService", dataService);
}());

Instead of returning albums... return $q.resolve(albums) In short... to chain a promise (using .then() ) you must return always return a promise.

Related

Defer return of factory until loop is completely finished angularjs

I'm trying to make a method that returns an array of objects after getting the objects from an API. The problem is that the return from the factory happens before all the calls are finished. I've tried to use $q.defer but it still sends the return before it's ready to ship.
This is what I've come up with so far.
angular.module('watchList').factory('storageService', ['$http', '$q', function ($http, $q) {
storage = {};
storage.getMovies = function () {
var movies = localStorage.getItem('movies');
var movieArray = angular.fromJson(movies);
var newArray = [];
var defer = $q.defer();
angular.forEach(movieArray, function (id) {
newArray.push($http.get(api + id));
});
$q.all(newArray).then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
This is the controller that I'm trying to make the call from
angular.module('watchList').controller('watchListController', ['$scope', 'storageService', function ($scope, storageService) {
$scope.movies = storageService.getMovies();
I want the loop to finish everything before it returns the array.
You don't need to create a promise, you can just return the promise returned by the $q.all(newArray) call.
The thing is that you cannot expect to get a result synchronously when it only becomes available asynchronously. So you need to keep with using then:
storage.getMovies = function () {
var movies = localStorage.getItem('movies');
var movieArray = angular.fromJson(movies);
var newArray = movieArray.map(function (id) {
return $http.get(api + id);
});
return $q.all(newArray);
}
storageService.getMovies().then(function(movies) {
$scope.movies = movies;
// ... other code working with $scope.movies
});
Side note: the map method does what you do with forEach, but immediately returns the array, which is quite practical.
getMovies will return immediately with a promise. You need to use "then" to wait on that promise.
$scope.movies = storageService.getMovies().then((response) => ...)

How to call to new object in promise join

I use the promise join and I need to use send the data from the readFile to myFacade (src) and my facade send obj to getA which latter on will be sent to arg[0]...
run = function (filePath) {
return Promise.join(
fs.readFileAsync(filePath, 'utf8')
.then(myFacade)
.then(getA),
users.getUsersAsync(usersObj)
.then(users.modifyRec.bind(null, process.env.us))
).then(function (args) {
return runProc('run', args[0], args[1]);
....
To make this work not in promise you should do something like
var parsed = new MyFacade(str);
var attribute = parsed.getA()
This is the code which should be called
var yaml = require('yamljs');
function MyFacade(src) {
this.data = yaml.parse(src);
}
MyFacade.prototype = {
getA: function () {
return this.data.def_types.web;
},
getB: function () {
return this.data.conars;
}
};
module.exports = MyFacade;
how to make it work with the promise chain above?
Just pass exactly the code you'd have used without promises as a callback:
return Promise.join(
fs.readFileAsync(filePath, 'utf8')
.then(function(str) {
var parsed = new MyFacade(str);
var attribute = parsed.getA()
return attribute;
}),
users.getUsersAsync(usersObj)
.then(users.modifyRec.bind(null, process.env.us)),
function(attr, rec) {
return runProc('run', attr, rec);
});
You're using
.then(getA)
Which means "call the function getA on the result of the previous promise." But you don't have a function getA; the result of the previous promise has a method getA. You want call:
.call('getA')
As for
.then(myFacade)
There are two options. One is a common thing to add to a constructor function:
function MyFacade(src) {
if(!(this instanceof MyFacade)) return new MyFacade(src);
this.data = yaml.parse(src);
}
This allows the constructor to be called without new. Alternatively, you can pass an anonymous function to then:
.then(function(str) {
return new MyFacade(str);
})

Javascript Kriskowal Q JS promise not working

I have created a promise using kriskowal/q module but when i try to use this it does not go into any function either happy path or error path.
here is my promise creation class
var Q = require('q');
var Test = function () {
};
Test.prototype = (function () {
var testt = function () {
var deferred = Q.defer();
var x = 5;
if (x === 5){
deferred.resolve('resolved');
}else{
deferred.error(new Error('error'));
}
return deferred.promise;
};
return {
testt : testt
}
}());
module.exports = Test;
and this is how i am going to use it
var Test = require('../src/js/test.js');
describe("Test", function () {
"use strict";
var test = null;
beforeEach(function () {
test = new Test();
});
it("should return the promise", function () {
test.testt().then(
function (a) {
console.log(a);
},
function (b) {
console.error(b);
}
);
});
});
since this is a jasmine test class if your not familiar with jasmine, what is inside 'it' function is the logic how i am using the promise. And the 'testt' is the function where i create the promise. for more clarification i have attached the entire code.
Issue : It does not print either a or b
Your it is finishing immediately, instead of after the promise's resolution/rejection.
it("should return the promise", function (done) {
test.testt().then(
function (a) {
console.log(a);
done();
},
function (b) {
console.error(b);
done();
}
);
});
See here for more info.

AngularJS: Set private service variable in callback

I'm trying to do this:
app.service('productsService', ['$http', productsService]);
function productsService($http){
return {
getProducts: getProducts
}
var _products = [];
function getProducts(){
$http.get('http://localhost:4000')
.then(function(data){
_products = data;
});
}
}
But at the then callback _products is an undefined variable.
What is the correct way to set _products value from the then callback?
You need to set the variable before the return statement.
app.service('productsService', ['$http', productsService]);
function productsService($http){
var _products = [];
return {
getProducts: getProducts
}
//var _products = []; this will never run
function getProducts(){
$http.get('http://localhost:4000')
.then(function(data){
_products = data;
});
}
}

How to create my own promise?

I have two questions about promises in AngularJS:
How do I create my own promise?
See the code below:
function controller($http) {
var dataCache;
function getData(){
if( dataCache != null ){
// should return my own promise here
// to pass the value of 'dataCache' to 'then' immediately
} else {
return $http.get('...some url ...');
}
}
}
How do I return the last promise?
Code:
function controller($http) {
var urlArr = ['url1', 'url2', 'url3'];
function getDataOneByOne() {
// should call $http.get() for the url in the 'urlArr' one after another in a chain
// and return the last promise
}
}
For the second question, do Array.prototype.reduce on urlArr and build promise chain:
function controller($http, $q) {
var urlArr = ['url1', 'url2', 'url3'];
function getDataOneByOne() {
return urlArr.reduce(function (chain, url) {
return chain.then(function () {
return $http.get(url);
});
}, $q.when());
}
}
Don't forget to handle $http errors, though.
For the first question, I believe you're looking for $q.when(). It wraps a normal value in a promise and resolves it.
function getData(){
if( dataCache !== null ) {
$q.when(dataCache);
} else {
return $http.get('...some url ...');
}
}
getData.then(function() {
});
See Klaster's answer for your second question.

Categories

Resources