I am new to Angular, so if you ask the question: "Why don't you...?" The answer is...because I didn't know I could.
Have a factory make an API call, then inject that factory into a parent controller that will have scope over the entire page. Then have child controllers nested and inherit from the parent controller.
Here is what I have so far. I may be WAY off here, and if that is the case, please tell me. I am working on this alone, and have no help, so any help is welcomed.
Thank you.
var app = angular.module('myApp', []);
app.factory('myFactory', function($http){
var MyFactory = function(){};
MyFactory.getParams = function(){
return $http.get('/getparameters');
.success(function(data){
var roomname = data.roomname;
})
MyFactory.getRoom(roomname);
};
MyFactory.getRoom = function(room){
return $http.get('/my/api/' + room);
};
});
app.controller('RoomCtrl', function($scope, myFactory){
$scope.data = myFactory;
});
You don't need to use resource, you need use promise;
EDITED
I tried to make more clear for you
app.factory('apicall', ['$q','$http',function($q, $http){
function getRoom(options){
var promise = $http.get(options.paramUrl)
.success(function(data){
//handle your error
return $http.get(options.roomUrl + data.roomname);
})
.error(function(msg){
return $q.when(msg)
})
return promise;
}
return {getRoom:getRoom};
}])
if you want call your factory in where you want
app.controller('RoomCtrl', ['$scope','apicall',function($scope, apicall){
var options = {
paramUrl:'/getparameters',
roomUrl:'/my/api/'
}
apicall.getRoom.all(function(result){
$scope.data = result;
})
}]);
The $q service helps you to handle combination of two asynchronous calls
app.factory('apicall', function($q, $http) {
var deferred = $q.defer();
$http.get('/getparameters')
.success(
function(data) {
var roomname = data.roomname;
$http.get(baseURL + 'room/' + roomname)
.success(
function(roomData) {
deferred.resolve(roomData);
}
)
.error(
function() {
deferred.reject();
}
)
})
.error(
function() {
deferred.reject();
}
);
return deferred.promise;
});
And here is controller where you can use your service
app.controller('someCtrl', function(apicall) {
apicall.then(
function(roomData) {
//success
},
function() {
//error
}
)
})
I usually separate different API calls in different factories that return a $resource.
For example, let's say we have 2 different API calls that point to different resources:
yourwebsite.com/user/:userId - returns data about the user
yourwebsite.com/photo/:photoId - return data about some photo
In angular, you would split these in 2 different factories:
"use strict";
angular.module("App.services").
factory("User",["$resource", function($resource){
return $resource("yourwebsite.com/user/:userId"),{userId:"#userId"});
}]);
and second
angular.module("App.services").
factory("Photo",["$resource", function($resource){
return $resource("yourwebsite.com/photo/:photoId"),{photoId:"#photoId"});
}]);
In your controller, you would use them like so:
angular.module("App.controllers").controller("TestController",["User","Photo", function(User, Photo){
User.get({
id:10
}).$promise.then(function(data){
console.log(data); //returns the data for user with the id=10
});
Photo.get({
id:123
}).$promise.then(function(data){
console.log(data);
});
}]);
Usually $resource maps a CRUD API(what I posted above is a basic example of a GET call).Check out the documentation on $resource - It already has the basic GET, PUT, POST, DELETE functions.
I would recommend using $http if you have only 1 simple operation on that URL and not all 4 of them. You can then inject $http in your controller and do the request there instead of creating a factory for it.
If you have 2 requests and they are chained(second one depends on the data received from the first one) you have to wait until the first one is resolved. With $resource you can do this in the following way:
angular.module("App.controllers").controller("TestController",["User","Photo", function(User, Photo){
var user_promise = User.get({
id:10
}).$promise.then(function(data){
console.log(data); //returns the data for user with the id=10
return data;
});
var photo_promise = Photo.get({
id:123
}).$promise.then(function(data){
console.log(data);
return data;
});
user_promise.then(photo_promise).
catch(function(error){
console.log(error); //catch all errors from the whole promise chain
});
}]);
Related
I'm trying to load some data through $http to prefill a profile form. Unfortunately all the examples I find online use the $scope-approach rather than 'Controller as'-approach (which I'm using to make future transition to Angular 2 easier). These examples assign the $http response to the '$scope' variable, which is not possible when using 'this'.
After a lot of fiddling I managed to get it to work by adding a temp variable
var temp = this;
to which I can assign the $http response when it successfully returns.
angular.module('angularUserApp')
.factory('ajax',['$http',function($http){
return {
getProfile: function(){
return $http.get('/ajax/user/profile')
.then(function(response){
return response.data.data.profile;
});
}
}
}])
.controller('userProfileCtrl', ['ajax', function (ajax) {
var temp = this;
ajax.getProfile().then(function(response){
temp.profile = response;
});
}]);
Is there a more elegant approach?
Your approach for using controllerAs is correct. Just a few advices though, better alias the this to a variable vm, which stands for viewModel instead of temp, and name your service semantically: userService instead of ajax:
angular.module('angularUserApp')
.factory('userService', function ($http){
return {
getProfile: function () {
return $http.get('/ajax/user/profile')
.then(function (response){
return response.data.profile;
});
}
}
})
.controller('userProfileCtrl', function (userService) {
var vm = this;
userService.getProfile().then(function (response) {
vm.profile = response;
});
});
One of the design ideas of angular is that you can use $scope. Simply inject $scope into your controller, like you did with your ajax service and set the profile response to the $scope variable ($scope.profile = response;).
As a result you would get something like:
angular.module('angularUserApp')
.factory('ajax',['$http',function($http){
return {
getProfile: function(){
return $http.get('/ajax/user/profile')
.then(function(response){
return response.data.data.profile;
});
}
}
}])
.controller('userProfileCtrl', ['ajax', '$scope', function (ajax, $scope) {
ajax.getProfile().then(function(response){
$scope.profile = response;
});
}]);
// In your template
<div ng-controller="userProfileCtrl">
<div ng-bind="profile.name"></div>
</div>
I have just about given up with this. But I have a $resource that uses a query() to retrieve a list of items. I then have an Interceptor that runs over those items in order to insert an $interval.
The $interval at a specific point will then get an item again using the $resource's get() function. But its this get() that is not working.
The call happens but I cannot get the response back into the template.
myServices.factory('Items', ['$resource',
function($resource) {
return $resource("/items", {}, {
'query': {
interceptor: MyInterceptor,
url: "/items",
isArray: true
},
})
}]);
myServices.factory('MyInterceptor', function($q, $interval, $injector, $rootScope) {
return {
'response': function(response) {
angular.forEach(response.resource, function (item) {
$interval(function () {
item.something = 1 + item.something;
if(item.something == 10)
{
item = $injector.get("Mine").get({slug: item.id});
}
});
});
return response;
}
};
});
I thought that this line item = $injector.get("Mine").get({slug: item.id}); would work, but it doesn't. The new item is not changed in the template.
So I changed it to something like this, which did not work either;
$injector.get("Mine").get({slug: item.id}, function(data){
item = data;
});
I have tried with $q and $promise too, but I had no luck with those either. Finding decent examples on those subjects was tough too.
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
Based on the above statement I will give an answer.
First things first, remove the interceptor. You won't need it. Instead use a service.
Write a service called ProcessedItems.
angular.module('app')
.service('ProcessedItems', ['Items', '$q', function(Items, $q){
return {
query: function() {
var defer = $q.defer();
Items.query()
.$promise
.then(function(response){
angular.forEach(response.resource, function(i)){
i.s = 1 + i.s;
if(i.s == 10) {
i = $injector.get("Mine").get({slug: i.id});
i.$promise.then(function(){
defer.resolve(response);
}, function(){
defer.reject();
});
};
};
});
return defer.promise;
}
};
}]);
After this service is set up, in your controller you can do
angular.module('app')
.controller('AppController', ['$scope', 'ProcessedItems',
function($scope, ProcessedItems){
$scope.items = [];
ProcessedItems.query().then(function(pitems){
$scope.items = pitems;
});
});
What this will essentially do is first process the data completely and then display it in the view.
Hi Everyone I'm sure there is a simple solution to this problem but I can't seem to find it myself...
I have a controller that assigns data to the $scope.items variable by calling a service defined as GetDataService
$scope.items = GetDataService.getData('/getBroadcastSourceList/1');
The service is set up as follows:
angular.module('appName')
.service('GetDataService', function($http, WebServiceURL) {
this.getData = function(ServiceParameter) {
$http.get(WebServiceURL + ServiceParameter)
.then(function(res){
return res.data;
});
};
});
I have stepped through my code in the GetDataService and can see that res.data does contain all relevant data however this data is not sent to $scope.items in my controller.
I have also checked that all is well on my controller side by changing my service as follows:
angular.module('appName')
.service('GetDataService', function($http, WebServiceURL) {
this.getData = function(ServiceParameter) {
return [{
Day: "Monday",
Language: "English "
}]
};
});
This does populate $scope.items so the issue must be somewhere here:
$http.get(WebServiceURL + ServiceParameter)
.then(function(res){
return res.data;
});
What am I doing wrong?
$http service is aync in nature so you need to assign data in a callback
GetDataService.getData('/getBroadcastSourceList/1').then(function(data) {
$scope.items=data;
})
Also your service does not have a return, add it.
this.getData = function(ServiceParameter) {
return $http.get(WebServiceURL + ServiceParameter)
You are not retuning nothing on the getData method of your service try to return a promise
angular.module('appName')
.service('GetDataService', function($http, $q WebServiceURL) {
this.getData = function(ServiceParameter) {
var defer = $q.defer();
$http.get(WebServiceURL + ServiceParameter)
.then(function(res){
defer.resolve(res.data);
}, function (err) { defer.reject(err)});
return defer.promise;
};
I am fairly new to Angular and have seen a few different ways to do many different things. I try to subscribe to Todd Motto's style guide. Example 1 is code written by my pairing partner and Example 2 is how I wrote it.
We would appreciate any feedback on wrong or right things either example is doing.
Thanks.
Example 1
angular
.module('app.services')
.service('FriendsService', function($q, $http, Auth){
var baseUrl = 'https://graph.facebook.com/v1.0'
this.all = function() {
var defer = $q.defer();
$http
.get(baseUrl + '/me/friends', { params: { access_token: Auth.currentUser.accessToken } })
.then(function(response){
defer.resolve(response.data.data)
});
return defer.promise;
};
});
Example 2
angular
.module('app.services')
.service('FriendsService', function($http, Auth){
var baseUrl = 'https://graph.facebook.com/v1.0'
var getAll = function() {
return $http
.get(baseUrl + '/me/friends', { params: { access_token: Auth.currentUser.accessToken } })
.then(function(response){
return response.data.data;
});
};
return {
all: getAll
}
});
Usage of FriendsService
FriendsService.all()
.then(function(data){
$scope.friends = data;
});
$http uses $q internally. Effectively the examples are nearly identical.
However, Example 1 doesn't catch any rejections by $http. It creates a redundant promise which wraps $http incompletely.
Example 2 would generally be preferred.
You might want to consider error-handling as well. I suggest using a .catch() block, as espoused by K. Scott Allen in his blog post:
http://odetocode.com/blogs/scott/archive/2014/04/21/better-error-handling-in-angularjs.aspx
Example:
var getAll = function() {
return $http
.get(...)
.then(function(response){
return response.data;
})
.catch(genericErrorHandler(e));
};
Note that the calling code will need to check that the value of "data" is not undefined (you can use angular.isDefined() for this).
I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.
I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.
Here is what I have thus far.
app.service('UserService', ['$http', function($http) {
this.users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(this.users);
}
};
promise.then(function() {
// this.users is undefined here
console.log('this.users');
});
}]);
Any help is greatly appreciated. Thank you.
Try using
var users = [];
rather than
this.users = [];
and see what
console.log(users);
outputs in each of those cases.
Your service is oddly defined, but if you have a return in it you can access it from any controller:
app.service('UserService', ['$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(users);
users = data.data;
}
};
return {
getUsers: function(){
return users;
}
}
}]);
so in your controller, you can use:
var myUsers = UserService.getUsers();
UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave
// your service should return a promise
app.service('PickerService', [$http', function($http) {
return {
getFiles: function(){
return $http.get('files.json'); // this returns a promise, the promise is not executed here
}
}
}]);
then in your controller do this:
PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
$scope.myDirectiveData = returnValues.data;
});
this does not have scope anymore where you are trying to use it do this instead:
app.service('UserService', [$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
console.log(users);
}
};
promise.then(function() {
console.log(users);
});
}]);
all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.
I think what your asking for is a solution along the lines of defining your service like this:
angular.module('app')
.service('User', function($http, $q) {
var users = null;
var deferred = $q.defer()
return {
getUsers: function() {
if(users) {
deferred.resolve(users);
} else {
$http.get('users.json');
.success(function(result) {
deferred.resolve(result);
})
.error(function(error) {
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Then in one Each controller you would have to do this:
angular.module('app')
.controller('ACtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in BCtrl
$scope.users = users;
});
});
angular.module('app')
.controller('BCtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in ACtrl
$scope.users = users;
});
});
NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.
All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations