Best practice factory angular.js 1.2 - javascript

I'm want to create service with $http with angular.js as is shown in this plunk
But when I try to do it in angular 1.2 I can not have more than one function into the factory as is done here
Works in angular 1.0.2
app.factory('myService', function($http, $q) {
return {
getFoo: function() {
var deferred = $q.defer();
$http.get('foo.json').success(function(data) {
deferred.resolve(data);
}).error(function(){
deferred.reject();
});
return deferred.promise;
},
getBar: function(callback) {
$http.get('foo.json').success(callback);
},
testHttpGetResult: function (){
return $http.get('foo.json');
}
}
});
How should I do it in angular 1.2? must I use a different approach(not a factory)?
Thanks in advance.
Edited
It Works, because have only a function(getFoo)
app.factory('myService', function($http, $q) {
return {
getFoo: function() {
var deferred = $q.defer();
$http.get('foo.json').success(function(data) {
deferred.resolve(data);
}).error(function(){
deferred.reject();
});
return deferred.promise;
}
});

As of Angular 1.2, promise unwrapping is optional, and it will be disabled entirely in future versions of Angular.
In other words, your clean version as of 1.2 requires an opt-in and won't work at all in the future.

I would guess that if you look at the code you have defined "getFoo" as follows:
getFoo: function() {
// ... code here ...
return deferred.promise;
},
When you call this routine you use the following code:
// "foo" represents a PROMISE, not the actual json value!
$scope.foo = myService.getFoo();
The routine isn't returning the value you expect though - rather it is returning a promise which is why the scope doesn't have what you expect. You need to use a ".then" syntax to actually get the data result from your routine. For some reason it seems that older versions of angular might have been doing this for you (possibly)?
Anyways, the issue isn't whether or not you can define multiple routines (you can), but how you call those routines.
Edit
In case I didn't make it clear enough the calling method marked "the clean way" shouldn't (and doesn't work). You should use the following instead:
// The clean way - FIXED
myService.getFoo().then(function (data){
$scope.foo = data;
});
Good luck!

I know this is an old post but I found this article that helped me a lot.
Hope this will help others. It fallows drew_w's Answers.
http://blog.brunoscopelliti.com/angularjs-promise-or-dealing-with-asynchronous-requests-in-angularjs
and this one is even better:
show route only after all promises are resolved
http://blog.brunoscopelliti.com/show-route-only-after-all-promises-are-resolved

Related

Angularjs Scope Variable does not Update View

I have seen this issue a lot on SO, but none of the solutions I have tried are working for me. I tried using $apply() which gives me an error stating that the digest cycle is already running. I tried using the Dot "." notation, but nothing changes. I even tried using timeout and promises, but still it does not update in the view.
Here is the HTML:
<th colspan="4" ng-class="{'ssqtrue': deficiencies === false , 'ssqfalse': deficiencies === true}">
<span ng-model="definfo">{{definfo}}</span>
</th>
Here is my Controller code:
$scope.recalculateDashboard = function (goToDashboard) {
contractorService
.calculateScores()
.success(function () {
getscoringDetails();
getDefInfo();
if (goToDashboard) {
$scope.tabs[0].active = true;
}
}).error(function (reason) {
console && console.log(reason.statusText);
genericErrorAlertHandler();
});
};
function getDefInfo() {
contractorService.getDeficiencyInfo()
.success(function (data) {
$scope.$apply(function() {
$scope.definfo = data;
});
if ($scope.definfo == 'No Deficiencies Found') {
$scope.deficiencies = false;
} else {
$scope.deficiencies = true;
}
}).error(function (reason) {
console && console.log(reason.statusText);
genericErrorAlertHandler();
});
}
For the life of me, I can't figure out what is going on here. Any assistance is greatly appreciated!
Regards to <span ng-model="definfo">{{definfo}}</span>
You don't need ng-model directive here. Enough to use {{definfo}} or better way like folks pointed to use ng-bind like:
<span ng-bind="definfo"></span>
I tried using $apply() which gives me an error
Generally, developers use $scope.$apply when get callback from 3d party code like jQuery that stay out of digest cycle. So don't use it. BTW, safety way is to use $timeout for example a.e. wrap as:
$timeout(function () {
$scope.definfo = data;
});
I believe if you don't use Promise chain you can use success and error callbacks (what you actually did) instead to use then()
About $http.get('/SSQV4/SSQV5/Contractor/GetDeficiencyInfo');
$http.get returns original Promise but it also returns status 'Ok', config, headers ... and i'm sure you don't want parse full response in your controller.
So I would create new Promise in your service and fetch results only.
So instead: return $http.get(URL); I would write in Service:
this.getDeficiencyInfo = function () {
var deferred = $q.defer();
$http({method: 'GET', url: URL}).then(function(res){
deferred.resolve(res.data);
}, function (error) {
console.error(error);
deferred.resolve({error:error}); //actually you can reject
});
return deferred.promise;
};
DEMO in fiddle
I figured this out, although I don't know "why" it caused this particular issue. When the page first loads $scope.definfo is set by a variable from another http call. The variable var pagedata = [] holds a lot of information that comes back from the service when the page is first rendered. The scope variable is then set with this code: $scope.definfo = pagedata.DeficiencyInfo. Removing that line and calling the function getDefInfo directly, sets and updates the scope variable as desired. Perhaps one of you gurus could explain why that is to me and others so that it may help others. Thank you to everyone for your help.

Angular 1 promise in service

I've been going crazy trying to get something to work in Angular 1.3 that may not even be possible... but it seems like it should be. Basically, I'd like to write a service that returns only a calculated value, not a promise.
I have a service that makes an $http.get request and returns a promise. I'd like to call this from another service, get that data, manipulate it and return a value. I don't want to have to deal with .then once I've called my service. My reasoning is that I'd like to have a sharable function that I can call and receive an actual value so that I can use it as a condition for something like ng-show. Like I say, I'd write this function in the controller and use .then to assign a variable with the data from the promise, but I don't want to write that in every single controller I need this in.
Example:
app.factory('searchService', ['$http', function($http) {
return {
performSearch: function(searchString) {
return $http.get('http://asdf.com/search/' + searchString);
},
getItemCount: function(itemName) {
var _this = this;
this.count = 0;
this.performSearch(itemName).then(
function successCallback(resp) {
_this.count = resp.data.items.length;
},
function errorCallback() {
// This section doesn't matter right now
};
return this.count;
}
};
}]);
So I'd like to just be able to call this from the controller and get back a "5" or something. Or even better, calling it from an ng-show like "ng-show="blahCtrl.getItemCount('item_search_string') == 0". Again, I have multiple controllers I may need this in, so I'd rather not duplicate functions all over the place just to extract that number I need.
This just doesn't work for me though... my return value is always whatever I initially set this.count to be. I've inserted a console.log a few places and have seen that (it seems) this.count is returned before the callback function can set its value. No matter what I do, I'm incapable of just returning a single value from this service and it's driving me crazy! What confuses me more is that if I were to do this code from inside the controller:
var _this = this;
searchService.performSearch('asdf').then(
function(data) { _this.searchResults.data; }
);
It works just fine and I get my promise data right inside of the searchResults variable no problem. It just doesn't work that way inside of the service for some reason.
Hopefully this makes sense, I may have rambled a bit or been unclear. Any help would be greatly appreciated... I've spent about as much time researching this and using trial and error on my own as I can.
Thanks!
One solution as i see.
Put this once in controller
app.controller('ctrl', function ($scope, searchService) {
$scope.searchService = searchService;
searchService.getItemCount();
});
and get in all view
{{ searchService.count }}
And i think, it's bad to make a request for each ng-if, ng-show etc.
You are going to need to use $q (https://docs.angularjs.org/api/ng/service/$q). Your function is returning before the promise is fulfilled. Your getItemCount should look like this:
app.factory('searchService', ['$http', '$q', function($http, $q) {
return {
performSearch: function(searchString) {
return $http.get('http://asdf.com/search/' + searchString);
},
getItemCount: function(itemName) {
var deferred = $q.defer();
this.performSearch(itemName).then(
function successCallback(resp) {
deferred.resolve(resp.data.items.length);
},
function errorCallback(err) {
deferred.reject(err);
};
return deferred.promise;
}
};
}]);

Able to access object returned by service, but accessing its properties gives undefined

I'm an angular beginner and I'm trying to set up a factory that gets data from my php back-end and updates the controller. Here is my basic code:
factory
app.factory('sessionStatus', ['$http', function($http){
return {
status:function(){
var status = {};
$http.get('/game_on/api/api.php?session-status')
.then(function(response){
angular.copy(response.data, status);
});
return status;
}
};
}]);
controller
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
$scope.sessionStatus = sessionStatus.status();
console.log($scope.sessionStatus);
}]);
This gives me the following in the console:
Object{}
session:"false"
However, when I adjust the controller to log the value of the session property like so:
console.log($scope.sessionStatus.session);
I simply get undefined in the console.
What am I doing wrong?
EDIT
For anyone else who suffers from my distinct brand of retardation, here's the conclusion I've reached:
The main cause of the issue is that I've been trying to access asynchronously generated information returned by the $http service before its promise has been resolved (i.e. before .then() is finished executing), which is leading to undefined being logged because the data has literally not yet been defined since the request that generates it is still in progress by the time console.log() is called in the controller.
Using the $timeout service I was able to delay the calling of console.log(), which resulted in the data being correctly logged to the console.
$timeout(function(){
console.log($scope.sessionStatus.session);
}, 2000); // logs "false" to the console like originally expected
My previous thinking was that I should make the call to my factory method as short as possible within the controller, and then act upon its results outside of that call. This was obviously a bad idea because .then() exists so that you can act on the results of an asynchronous request at the precise moment the request is completed.
In hindsight this all seems so obvious, but hopefully this explanation helps someone else in the future...
Thanks for all the answers I received, they were a big help!
There are a couple of changes that you can make to the code here. First I would suggest resolving the promise and assigning the value in the controller instead of in the service. Next, you won't need to use Angular.Copy on the result object, you can simply make a variable assignment. Try something like simply this, then build out:
app.factory('sessionStatus', ['$http', function($http){
return {status: $http.get('/game_on/api/api.php?session-status')}
}]);
and
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
sessionStatus.status.then(function(result){
$scope.sessionStatus = result.data; //or some property on result
});
}]);
since javascript is asynchronous it does't wait until http response and return the status. thats why it return null. try something like this
app.factory('sessionStatus', ['$http', function($http){
return {
status:function(){
$http.get('/game_on/api/api.php?session-status')
}
};
}]);
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
$scope.sessionStatus = sessionStatus.status().then(function(response) {
console.log(response);
});
}]);
Try to return a promise from your factory:
app.factory('sessionStatus', ['$http', function($http) {
var factory = {
status: status
};
return factory;
function status() {
return $http.get('/game_on/api/api.php?session-status');
}
}]);
Then in controller:
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus) {
function getSuccess(response) {
$scope.sessionStstus = response.data;
}
function getError(response) {
console.log('error');
}
sessionStatus.status()
.then(getSuccess)
.catch(getError);
console.log($scope.sessionStatus);
}]);

How to make data available in all scopes in Angularjs

I wish to fetch data from the server-side using $http and and make it available to the all routes and controllers in my app.
Javascript code sample
myApp.factory('menuService', function($http){
$http.get('/pages').success(function(data){
return data; //list of pages
});
});
myApp.run(function($rootScope, menuService){
$rootScope.menu = menuService;
});
HTML code sample
<ul >
<li ng-repeat="page in menu">{{page.title}}</li>
</ul>
This code actually returns the data but does not print it on my html page. Please can someone help? Thanks
You are inverting the differed promise pattern.
Instead your code should be:
myApp.factory('menuService', function($http){
return $http.get('/pages');
});
myApp.run(function($rootScope, menuService){
menuService.then(function(data) {
$rootScope.menu = data;
})
});
You may be able to benefit from setting up your menuService a bit different. Try the following...
myApp.factory('menuService', function($http) {
function getData() {
return $http.get('/pages');
}
return {
'getData' : getData
}
});
Now we have a function wrapped in our $http call in a getData() function, we can now easily leverage then() to resolve the promise returned by getData() in .run(), ensuring we get a resolved value and $rootScope.menu gets assigned the values we want. This new setup on menuService now sets the landscape to add other functions at later times, which we'll likely want.
myApp.run(function($rootScope, menuService) {
menuService.getData().then(function(response) {
$rootScope.menu = response;
})
});
Check out the $http docs for a better understanding on asynchronous behavior

Why is $timeout is needed for watch to trigger

I have a directive that watches the height of an element. That element is updated by a controller that uses a factory method to get data. Unless I have a $timeout in that factory my watch never gets updated. Curious! Can anyone shed light onto why?
My controller:
$scope.update = function () {
apiService.getLinks(function (response) {
$scope.links = response;
// If I try $scope.$apply() here it says its already in progress, as you'd expect
});
}
quickLinksServices.factory('quickLinksAPIService', function ($http, $timeout) {
quickLinksAPI.getQuickLinks = function (success) {
//without this the watch in the directive doesnt get triggered
$timeout(function () { }, 100);
$http({
method: 'JSON',
url: '/devices/getquicklinkcounts'
}).success(function (response) {
quickLinksAPI.quicklinks = response;
quickLinksAPI.saveQuickLinks();
success(response);
});
}
The directive I'm using is here
Basically angularjs provides $timeout service to trigger function call after specified time, but as I know the use of $timeout is not strictly needed, basically people have habit of writing this I mean they need to trigger watch after specified interval. but in many cases $apply does the trick. The thing you need is $apply(). for your reference please check this
Also many times what happens is angulars $watch executes very faster than you expected so it may send incomplete updates or response In such cases $timeout plays the important role by delaying $watch. You can clear $timeout if $watch is fast enough to your need, which means $timeout is the way to explicitly trigger the $watch, where as $apply can do it by itself. So the use of $timeout or $apply is depends on how your requirement is. Hope this clears you. Good luck.
This is because you are not working with the $http promise in the right way.
Following code is not tested, but shows how you could solve your problem. Note the return $http. Here you return the promise and handle the promise in your code.
$scope.update = function () {
quickLinksServices.quickLinksAPI.getQuickLinks().then(function(data){
$scope.links = data;
}
);
};
quickLinksServices.factory('quickLinksAPIService', function ($http, $timeout) {
quickLinksAPI.getQuickLinks = function (success) { return $http({
method: 'JSON',
url: '/devices/getquicklinkcounts'
});
});
}
If you can't make it work this way because of design decisions, you still have to work with promises ($q). If you want to know more about promises here is a good thread on stackoverflow.

Categories

Resources