Angular service causing an error - javascript

I'm trying to receive data using $http as a dependency injection, then assign that data to a promise using angular's $q service. I'm doing something wrong.. can't seem to locate where.
Service:
myApp.factory('githubApi', ['$http', '$q',
function($http, $q) {
var deferred = $q.defer();
//Declaring a promise that will or will not return a users github information.
this.accountInformation = function (){
return $http.get('https://api.github.com/users/joshspears3')
.then(function(response){
deferred.resolve(response);
return deferred.promise;
}, function(response){
deferred.reject(response);
return deferred.promise;
})
}
}
]);
Controller:
myApp.controller('githubCtrl', [ 'githubApi', '$q', '$scope',
function(githubApi, $q, $scope){
githubApi.accountInformation()
.then(
function (result) {
$scope.data = result;
}, function (error) {
// handle errors here
console.log(error.statusText);
}
);
}
]);
Directive:
myApp.directive('githubRequest', [
function() {
return {
scope: {},
restrict: 'E',
controller: 'githubCtrl',
templateUrl: 'public/views/partials/github-request.html'
}
}
]);
Partial (github-request.html):
<p class="component-example-header">Creating a service. Making a $http request to grab my personal Github information.</p>
<div class="service-message">
<p>Avatar:</p>
<img width="20%" src="{{data.avatar_url}}" alt="" />
<p>Username: <span ng-bind="data.login"></span></p>
<p>Followers: <span ng-bind="data.followers"></span>, Following: <span ng-bind="data.following"></span></p>
</div>
<hr>
This is the error that the browser is throwing:
Error: [$injector:undef] http://errors.angularjs.org/1.4.4/$injector/undef?p0=githubApi
at Error (native)
at http://localhost:9999/bower_components/angular/angular.min.js:6:416
at Object.$get (http://localhost:9999/bower_components/angular/angular.min.js:37:32)
at Object.e [as invoke] (http://localhost:9999/bower_components/angular/angular.min.js:39:96)
at http://localhost:9999/bower_components/angular/angular.min.js:40:410
at d (http://localhost:9999/bower_components/angular/angular.min.js:38:308)
at Object.e [as invoke] (http://localhost:9999/bower_components/angular/angular.min.js:39:64)
at Q.instance (http://localhost:9999/bower_components/angular/angular.min.js:80:151)
at K (http://localhost:9999/bower_components/angular/angular.min.js:61:140)
at http://localhost:9999/bower_components/angular/angular.min.js:68:475
It's saying that my githubApi service is undefined when I inject it into my dependencies for possibly my controller?

I think it's because your factory isn't returning anything
myApp.factory('githubApi', ['$http', '$q',
function($http, $q) {
return {
accountInformation: function (){
var deferred = $q.defer();
$http.get('https://api.github.com/users/joshspears3')
.then(function(response){
deferred.resolve(response);
}, function(response){
deferred.reject(response);
})
return deferred.promise;
}
}
}
]);
is how I usually have mine

You should be returning the promise in your service method instead, and the deferred object should be inside the method, also, the service should return an object:
myApp.factory('githubApi', ['$http', '$q',
function($http, $q) {
//Declaring a promise that will or will not return a users github information.
return {
accountInformation: function () {
var deferred = $q.defer();
$http.get('https://api.github.com/users/joshspears3')
.then(function(response){
deferred.resolve(response);
}, function(response){
deferred.reject(response);
});
return deferred.promise;
}
}
}
]);
You can also simplify this as the $http service already returns a promise:
myApp.factory('githubApi', ['$http',
function($http) {
//Declaring a promise that will or will not return a users github information.
return {
accountInformation: function () {
return $http.get('https://api.github.com/users/joshspears3');
}
}
}
]);

First, $http.get returns a $q promise already, there is no need to wrap it in a promise again.
Second, a factory creates a single instance of a service which is shared. Therefore the method for the factory should return that object. Assigning anything to this within the factory function itself will not expose that method / property:
myApp.factory('githubApi', ['$http', function ($http) {
var githubApi = {
accountInformation: function () {
return $http.get('https://api.github.com/users/joshspears3');
}
};
return githubApi;
}

Related

proper way to call http (RESTFUL WebAPI) in angularjs

I have following controller code
module.registerController('DemoCtrl', function ($scope, myFactory) {
myFactory.get(function (data) {
console.log(data); /// data is always undefined
});
});
and following the factory which is calling restful webapi
module.registerFactory('myFactory', ['$http',
function ($http) {
function get(callback) {
$http.get('mywebapiurl')
.success(function (response) {
//debugger; data comes back from server
callback(response);
}).error(function (response, status, headers, config) {
callback(response);
});
}
return {
get: get
}
}]);
The factory is calling webapi service and does gets the data back. However in controller the data doesnt get returned.
Am I missing something obvious here? Also not sure if this is the best way to call webservice in angularjs in controller using factory. Any inputs are most welcome.
Thanks,
You want to return a promise instead of passing a callback. As $http.get already returns a promise, you can just return that, or a derived promise that returns the response data directly. By the way, your factory looks like it should be a service instead:
angular.moudule('yourApp')
.service('myService', ['$http', myService]);
function myService($http) {
this.get = function(url) {
return $http.get(url)
.then(function transformData(response){
return response.data;
}).catch(function onError(rejectionResponse){
//Whatever you want to do here
});
}
}
This way myService.get will return a promise you can .then(), .catch() and .finally() on what you want, staying in the frameworks coding style. For example:
var squirrels = [];
myService.get('http://squirrelshop.com/api/squirrels')
.then(function(squirrelsData){
console.log('Got the squirrels!');
squirrels = squirrelsData;
}).catch(function(rejection){
console.warn('Couldnt fetch squirrels. Reason: ' + rejection);
});
controller code
module.registerController('DemoCtrl', function ($scope, myFactory) {
myFactory.get("url").then(function(d) {
console.log(d.data);
}
});
});
factory which is calling restful webapi
module.registerFactory('myFactory', ['$http',
function ($http) {
var apiFactory = {
get:function(url){
return $http.get(url);
}
}
return apiFactory;
}]);
Success and failure in factory
module.registerFactory('myFactory', ['$http',
function ($http) {
var apiFactory = {
get:function(url){
return $http.get(url).then(function(response){
// success
return responce.data;
},function(error){
//failure
return error;
};
}
}
return apiFactory;
}]);

How should I set data from an asynchronous service into a controller $scope?

All server data accesses in my page are performed by my RequestFactory provider.
(The RequestFactory uses $http to perform the actual server calls.)
My controller scope has a reference to the list of data returned from the RequestFactory.
My question is that since the RequestFactory calls are asynchronous, and the RequestFactory does not (and should not) have access to the controller scope, what is the proper way for the RequestFactory to hand off the data to the controller?
var requestFactory = {};
angular.module("myApp").factory("RequestFactory", function($http) {
requestFactory.fetch = function() {
$http.get(url).
success(function(data) {
controller.setData(data)
}).
error(function(data, status) {
console.info("fetch Failed. Error code: " + status + " Url:" + url);
}).finally(function() {
controller.setSubmitting(false);
});
};
return requestFactory;
});
You should return the promise from you factory. See below snippet.
.factory("RequestFactory", function($http) {
return {
fetch : function() {
return $http.get(url).then(function(result) {
return result.data;
});
}
}
});
In your controller you should use like
.controller('MainCtrl', function($scope, RequestFactory) {
RequestFactory.fetch().then(function(data)
$scope.foo = data;
});
});
You can do the following :
Service
(function(){
function Service($http){
function get(){
//We return a promise
return $http.get('path_to_url');
}
var factory = {
get: get
};
return factory;
}
angular
.module('app')
.factory('Request', Service);
})();
Controller
(function(){
function Controller($scope, $q, Request) {
var defer = $q.defer();
//Call our get method from the Request Factory
Request.get().then(function(response){
//When we get the response, resolve the data
defer.resolve(response.data);
});
//When the data is set, we can resolve the promise
defer.promise.then(function(data){
console.log(data);
});
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
As you know, $http service return promise, so after that, you can easily combining them.

Angular.js service factory $http.get not calling my service

I am trying to get data in service factory. Nothings happen.It executed code. But nothings happened. Dont know why?
Here is my service code:
'use strict';
app.factory('accountService', function($http, $location, $rootScope) {
return {
accounts: function() {
$http.get('http://localhost:29045/AccountOperation.svc/GetAccounts').success(function(data, status, headers, config) {
console.log(status);
var $promise = data;
console.log(data);
}).error(function(data, status, headers, config) {
});
}
}
});
Here is my controller(calling factory from here):
'use strict';
app.controller('accountController', function($scope, accountService, $rootScope) {
$scope.accounts = function() {
accountService.accounts();
}
});
Also i didnt get error.
On your account function you are not creating a promise or returning anything. Try:
app.factory('accountService', function($http, $location, $rootScope) {
return {
accounts: function() {
return $http.get('http://localhost:29045/AccountOperation.svc/GetAccounts');
}
}
});
This returns the promise and you can handle it anywhere you call the acccount function or you could create a promise inside the function and return it. then inside the success or error methods resolve or reject it.
I think you have to return your promise from the factory. Look at the following post under "Promises and Services"
http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/

AngualrJS $http returns undefined?

According to AngularJS, my $http call through a service from my controller is returning undefined?
What seems to be the issue here? I am trying to return the data called, but once passed to the controller the data becomes undefined?
JavaScript
var myStore = angular.module('myStore', [])
.controller('StoreController', ['$scope', 'dataService', function ($scope, dataService) {
$scope.products = dataService.getData();
}])
.service('dataService', ['$http', function($http) {
this.getData = function() {
$http.get('assets/scripts/data/products.json')
.then(function(data) {
return data;
});
};
}]);
HTML
<div class="content">
<ul>
<li ng-repeat="product in products.products">{{product.productName}}</li>
</ul>
</div>
I understand that $http, $q, and $resource all return promises, but I thought I had covered that with .then.
The problem could be that you are not returning the promise created by $http.get in your dataService.getData function. In other words, you may solve your undefined issue by changing what you have to this:
.service('dataService', ['$http', function($http) {
this.getData = function() {
return $http.get...
};
}
If you had multiple calls to $http.get within dataService.getData, here is how you might handle them.
.service('dataService', ['$http', function($http) {
this.getData = function() {
var combinedData, promise;
combinedData = {};
promise = $http.get(<resource1>);
promise.then(function (data1) {
combinedData['resource1Response'] = data1;
return $http.get(<resource2>);
});
return promise.then(function (data2) {
combinedData['resource2Response'] = data2;
return combinedData;
});
};
}]);
A much cleaner way, however, would be to use $q.all
.service('dataService', ['$http', '$q', function($http, $q) {
this.getData = function() {
var combinedData, promises;
combinedData = {};
promises = $q.all([
$http.get(<resource1>),
$http.get(<resource2>)
]);
return promises.then(function (allData) {
console.log('resource1 response', allData[0]);
console.log('resource2 response', allData[1]);
return allData;
});
};
}]);
You're problem does lie in the fact that you are not returning a promise but as you stated in #maxenglander's post you may have multiple http calls involved which means you should start creating and resolving your own promise using $q:
.service('dataService', ['$http', '$q', function($http, $q) {
return $http.get('assets/scripts/data/products.json')
.then(function(data) {
//possibly do work on data
return <<mutated data>>;
});
}];
or if you have multiple http calls and need to do some combination work you can do something $q.all:
.service('dataService', ['$http', '$q', function($http, $q) {
var p1 = $http.get('assets/scripts/data/products.json');
var p2 = $http.get('assets/scripts/data/products2.json');
return $q.all([p1, p2]).then(function(result){
//do some logic with p1 and p2's result
return <<p1&p2s result>>;
});
}];
then in your controller you will need to do:
.controller('StoreController', ['$scope', 'dataService', function ($scope, dataService) {
dataService.getData().then(function(result){
$scope.products = result;
});
}]);
What this allows in your service is now you can do complex calls like say call two webservices inside and wait till they are both complete then resolve the promise.
What I'm trying to express here is that you don't need to return the promise provided by the $http.get function, but since you are doing an async action you DO need to return some promise that will be later fulfilled and acted on.

Pass a promise into an Angular-UI state controller

Is it possible to pass a promise to a UI.Router $state from an outside controller (e.g. the controller that triggered the state)?
I know that $state.go() returns a promise; is it possible to override that with your own promise resolve this promise directly yourself or resolve it using a new promise?
Also, the documentation says the promise returned by $state.go() can be rejected with another promise (indicated by transition superseded), but I can't find anywhere that indicates how this can be done from within the state itself.
For example, in the code below, I would like to be able to wait for the user to click on a button ($scope.buttonClicked()) before continuing on to doSomethingElse().
I know that I can emit an event, but since promises are baked into Angular so deeply, I wondered if there was a way to do this through promise.resolve/promise.reject.
angular.module('APP', ['ui.router'])
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('myState', {
template: '<p>myState</p>',
controller: ['$state', '$scope', '$q', function ($state, $scope, $q) {
var deferred = $q.defer();
$scope.buttonClicked = function () {
deferred.resolve();
}
}]
});
}])
.controller('mainCtrl', ['$state', function ($state) {
$state.go('myState')
.then(doSomethingElse)
}]);
Update
I have accepted #blint's answer as it has got me closest to what I wanted. Below is some code that fleshes out this answer's idea a bit more. I don't think the way I have written this is a very elegant solution and I am happy if someone can suggest a better way to resolve promises from a triggered state.
The solution I've chosen is to chain your promises as you normally would in your controller, but leave a $scope.next() method (or something similar) attached to that scope that resolves/rejects the promise. Since the state can inherit the calling controller's scope, it will be able to invoke that method directly and thus resolve/reject the promise. Here is how it might work:
First, set up your states with buttons/controllers that call a $scope.next() method:
.config(function ($stateProvider) {
$stateProvider
.state('selectLanguage', {
template: '<p>Select language for app: \
<select ng-model="user.language" ng-options="language.label for language in languages">\
<option value="">Please select</option>\
</select>\
<button ng-click="next()">Next</button>\
</p>',
controller: function ($scope) {
$scope.languages = [
{label: 'Deutch', value: 'de'},
{label: 'English', value: 'en'},
{label: 'Français', value: 'fr'},
{label: 'Error', value: null}
];
}
})
.state('getUserInfo', {
template: '<p>Name: <input ng-model="user.name" /><br />\
Email: <input ng-model="user.email" /><br />\
<button ng-click="next()">Next</button>\
</p>'
})
.state('mainMenu', {
template: '<p>The main menu for {{user.name}} is in {{user.language.label}}</p>'
})
.state('error', {
template: '<p>There was an error</p>'
});
})
Next, you set up your controller. In this case, I'm using a local service method, user.loadFromLocalStorage() to get the ball rolling (it returns a promise), but any promise will do. In this workflow, if the $scope.user is missing anything, it will progressively get populated using states. If it is fully populated, it skips right to the main menu. If elements are left empty or are in an invalid state, you get taken to an error view.
.controller('mainCtrl', function ($scope, $state, $q, User) {
$scope.user = new User();
$scope.user.loadFromLocalStorage()
.then(function () {
var deferred;
if ($scope.user.language === null) {
deferred = $q.defer();
$state.go('selectLanguage');
$scope.next = function () {
$scope.next = undefined;
if ($scope.user.language === null) {
return deferred.reject('Language not selected somehow');
}
deferred.resolve();
};
return deferred.promise;
}
})
.then(function () {
var deferred;
if ($scope.user.name === null || $scope.user.email === null) {
deferred = $q.defer();
$state.go('getUserInfo');
$scope.next = function () {
$scope.next = undefined;
if ($scope.user.name === null || $scope.user.email === null) {
return deferred.reject('Could not get user name or email');
}
deferred.resolve();
};
return deferred.promise;
}
})
.then(function () {
$state.go('mainMenu');
})
.catch(function (err) {
$state.go('error', err);
});
});
This is pretty verbose and not yet very DRY, but it shows the overall intention of asynchronous flow control using promises.
The purpose of promises is to guarantee a result... or handle a failure. Promises can be chained, returned in functions and thus extended.
You would have no interest in "overriding" a promise. What you can do, however:
Handle the failure case. Here's the example from the docs:
promiseB = promiseA.then(function(result) {
// success: do something and resolve promiseB
// with the old or a new result
return result;
}, function(reason) {
// error: handle the error if possible and
// resolve promiseB with newPromiseOrValue,
// otherwise forward the rejection to promiseB
if (canHandle(reason)) {
// handle the error and recover
return newPromiseOrValue;
}
return $q.reject(reason);
});
Append a new asynchronous operation in the promise chain. You can combine promises. If a method called in the chain returns a promise, the parent promised will wall the rest of the chain once the new promise is resolved.
Here's the pattern you might be looking for:
angular.module('APP', ['ui.router'])
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('myState', {
template: '<p>myState</p>',
controller: 'myCtrl'
});
}])
.controller('myCtrl', ['$scope', '$state', '$q', '$http', 'someAsyncServiceWithCallback',
function ($scope, $state, $q, $http, myService) {
$scope.buttonClicked = function () {
$state.go('myState')
.then(function () {
// You can return a promise...
// From a method that returns a promise
// return $http.get('/myURL');
// Or from an old-school method taking a callback:
var deferred = $q.defer();
myService(function(data) {
deferred.resolve(data);
});
return deferred.promise;
},
function () {
console.log("$state.go() failed :(");
});
};
}]);
Perhaps one way of achieving this would be to return your promise from the state's resolve
resolve: {
myResolve: function($scope, $q) {
var deferred = $q.defer();
$scope.buttonClicked = function () {
deferred.resolve();
}
return deferred.promise;
}
}
There is also an example in resolve docs that may be of interest
// Another promise example. If you need to do some
// processing of the result, use .then, and your
// promise is chained in for free. This is another
// typical use case of resolve.
promiseObj2: function($http){
return $http({method: 'GET', url: '/someUrl'})
.then (function (data) {
return doSomeStuffFirst(data);
});
},

Categories

Resources