I want to combine multiple requests to one, so it won't do n requests but one.
Problem is that I don't know how to do a resolve to an object of names, which are variables that are injected in ctrl.
For example, now I have resolved:
resolve: {
regions : ["$http", function($http){
return $http({...});
}],
coordinators: ["$http", function($http){
return $http({...});
}],
....
}
What I want (not working) are same variables(n names) and only one request, which return n values to an object of names:
resolve: {
[regions,coordinators,...] : ["$http", function($http){
return $http({... return values;});
}]
}
Any ideas how?
You can achieve this by having one service endpoint that gathers your several pieces of information into an object literal:
function gatherResolves() {
return { region1.promise, region2.promise, region3.promise };
}
Then in the resolve you have just one call:
.state('foo', {
resolve: regions: ['$http', function($http) {
return $http.gatherResolves({ ... });
}]
})
Once in the controller, and injected into it, you can then split the injectable into the various pieces you want via destructuring (assuming you're using ES6) or manually assigning the object literal back into variables with your individual calls.
constructor(regions) {
this.splitRegions(this.regions);
}
splitRegions(regions) {
this.region1 = regions['region1'];
this.region2 = regions['region2'];
}
I might have mixed up some syntax as I'm mixing ES5/ES6 I think. But you get the idea, it is possible to distill it all down to 1 resolve call. The real question is, why, as you're just shifting logic around. Also, one of the benefits is that child states inherit resolves, so you can structure your code to make use of this. Having just one resolve with everything in it means you're carrying that around on all child states of that parent.
Related
I have a page in which there is a drop down, My goal is when I select any value from drop down and click on Next,It is switching to Next page using ngRouteProvider and doing $http.post , I need to pass data as payback load.
.when('/xyz/step2', {
templateUrl: partial('replacStep2.html'),
controller: 'xyzCtrl',
resolve: {
cardTypeDetails: ['$http', function ($http) {
return $http.post(appContext('xyz.json'),{});
}]
}
})
You can create a factory and set a variable inside it, later you can inject that factory inside your resolve block and access your variable through it.
resolve: {
cardTypeDetails: ['$http','sharedFactory', function ($http,sharedFactory) {
console.log(sharedFactory.sharedVariable); //use it like this
return $http.post(appContext('xyz.json'),{});
}]
}
and factory
angular.module('demoApp').factory('sharedFactory',function(){
return {
sharedVariable:null
};
});
Or in same way you can use $rootScope to pass variables (but not great for performance if you do for large no. of variables)
Passing variables from one controller to another with $rootscope
My controller is getting somewhat busy and finding the function that I'm looking for is becoming cumbersome. I'd like to have individual files for some of my controller functions. How can I achieve this? Is there something like ng-include for the controller to use?
You might use the mixin pattern. This allows you to extend your controllers with other controllers. As a result you have now two different controllers, while both get executed at the same time:
app.controller('AnyController', ['$scope'
function ($scope) {
return {
myFunction: function() {
// do something
}
}
}
]);
app.controller('MyMainController', ['$scope'
function ($scope) {
var anyThing = $controller('AnyController as anyThing',{$scope: $scope}),
$scope.anyThing = function() {
// those two options are now both valid:
anyThing.myFunction();
$scope.anyThing.myFunction();
}
}
]);
Now you are able to move your seperate controllers into different files.
EDIT: As asked, I'll explain a bit more efficiently !
I've been sitting in front of an annoying problem recently, which is that whenever I update a value inside a directive, the controllers I'm not currently "in" are the only ones to be updated properly.
Scenario example: Profile page is made of two controllers. Navbar_controller which is just currently displaying the user name :
<div ng-if="Auth.isAuthenticated">Hello, {{Auth.getCurrentUser().name}}</div>
The second controller , Profile_controller is here to update user values. This is a simple function in the angular first controller, which updates CurrentUser:
$scope.updateUser = function (type, form) {
if (!$scope.modif)
return ;
$http.put('/api/users/' + Auth.getCurrentUser()._id + '/update', {type:type, modif:$scope.modif})
.success(function (data, status) {
$scope.user = Auth.setNewUser(data);
})
.error(function () {
console.log("error");
});
};
When I update, for example, the name. I can see that the database has been modified properly. And indeed, navbar_controller got the update because a new name is printed in the div. However, Profile_controller doesn't get the update: the name printed in the profile page didn't change.
Here are the two basic functions in Auth.service.js :
getCurrentUser: function() {
return currentUser;
},
// 'user' is the data retrieved in http put request dot success
setNewUser: function(user) {
currentUser = user;
$rootScope.$broadcast(); // Navbar_controller is updated with or without this line
return currentUser;
}
Anyway, if I look at the navbar and its controller, which is calling Auth.getCurrentUser() method, the user values are instantly modified. I'e been using an ugly method consisting in modifying the controller values manually or by refreshing the page... But this isn't the way to go, right ?
There must be something with "$rootScope.$broadcast();", but I'm really new to Angular and other questions on stackoverflow are too specific to help me understand properly.
Thank you !
Your question was a little difficult to understand, but I think the problem is that you are reference a changing object in your various controllers. Here is an example to explain:
Service:
var myObject = { ... };
return {
getObject() { return myObject; }
setObject(obj) { myObject = obj; }
};
Controller 1:
$scope.myObjA = Service.getObject();
Controller 2:
$scope.myObjB = Service.getObject();
Now on initialisation both controllers will be referencing the same object, so if you changed a property inside either controller (eg. $scope.myObjB.name = 'bob';), then the other controller would also see the name.
However if you changed the object itself in a controller (eg. Service.setObject(newObj);), then the controller will be referencing the new object, while the other controller will still be referencing the old one.
You can fix this by wrapping your service object in a container:
var cont = {
user: ...
};
function getContainer() { return cont; }
function setNewUser(user) { cont.user = user; }
Then inside your controllers, get the container (not the user):
$scope.cont = Service.getContainer();
And inside your html:
<div>{{cont.user.name}}</div>
Now when you update the user, all attached controllers will be updated.
Well I'd try to change and store the user information in $rootScope, for your scenario could be a good fit.
getCurrentUser: function() {
$rootScope.currentUser===undefined ? 'no User': $rootScope.currentUser;
},
setNewUser: function(user) {
$rootScope.currentUser = user;
//$rootScope.$broadcast(); no need to broadcast
return getCurrentUser();
}
in that way currentUser will be updated in different scopes as needed!
I'll quote AnuglarJs FAQ regarding to $rootscope:
$rootScope exists, but it can be used for evil
Occasionally there are pieces of data that you want to make global to
the whole app. For these, you can inject $rootScope and set values on
it like any other scope. Since the scopes inherit from the root scope,
these values will be available to the expressions attached to
directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly,
like you would (hopefully) use with global variables in any language.
In particular, don't use it for code, only data. If you're tempted to
put a function on $rootScope, it's almost always better to put it in a
service that can be injected where it's needed, and more easily
tested.
Conversely, don't create a service whose only purpose in life is to
store and return bits of data.
FILE [myapp.js]
$stateProvider.state('view6', {
resolve: {simpleObj: function(){ return {'value':"hello"}; }}
controller:'MyCtrl6'
});
Fails to pass resolved dependency to MyCtrl6 when MyCtrl6 exists in a separate file/module.
FILE [ controllers.js ]
angular.module('myApp.controllers', [])
.controller('MyCtrl6',['$scope',function($scope,simpleObj) {
console.log(simpleObj.value);
}]);
OUTPUT: 'undefined'
However the following works:
$stateProvider.state('view6', {
resolve: {simpleObj: function(){ return {'value':"hello"}; }
controller:function(simpleObj){console.log(simpleObj.value);}
});
Please forgive cut 'n paste omissons but I think this should convey the issue at question.
Thanks-
It seems that there is a wrong type of arguments:
.controller('MyCtrl6',['$scope',function($scope,simpleObj) {
while we should see
.controller('MyCtrl6',['$scope', 'simpleObj' ,function($scope,simpleObj) {
which also should answer why this is working:
controller:function(simpleObj){
because we do have used different notation to pass the object into Controller
Seems I was calling the controller within the partial template
div ng-controller='MyCtrl3'
Apparently this can create some chaos with resolves when you are defining the controller in the stateProvider route definition.
Who knew?
I was reading this article: http://eviltrout.com/2013/06/15/ember-vs-angular.html
And it said,
Due to it’s lack of conventions, I wonder how many Angular projects
rely on bad practices such as AJAX calls directly within controllers?
Due to dependency injection, are developers injecting router
parameters into directives? Are novice AngularJS developers going to
structure their code in a way that an experienced AngularJS developer
believes is idiomatic?
I am actually making $http calls from my Angular.js controller. Why is it a bad practice? What is the best practice for making $http calls then? and why?
EDIT: This answer was primarily focus on version 1.0.X. To prevent confusion it's being changed to reflect the best answer for ALL current versions of Angular as of today, 2013-12-05.
The idea is to create a service that returns a promise to the returned data, then call that in your controller and handle the promise there to populate your $scope property.
The Service
module.factory('myService', function($http) {
return {
getFoos: function() {
//return the promise directly.
return $http.get('/foos')
.then(function(result) {
//resolve the promise as the data
return result.data;
});
}
}
});
The Controller:
Handle the promise's then() method and get the data out of it. Set the $scope property, and do whatever else you might need to do.
module.controller('MyCtrl', function($scope, myService) {
myService.getFoos().then(function(foos) {
$scope.foos = foos;
});
});
In-View Promise Resolution (1.0.X only):
In Angular 1.0.X, the target of the original answer here, promises will get special treatment by the View. When they resolve, their resolved value will be bound to the view. This has been deprecated in 1.2.X
module.controller('MyCtrl', function($scope, myService) {
// now you can just call it and stick it in a $scope property.
// it will update the view when it resolves.
$scope.foos = myService.getFoos();
});
The best practise would be to abstract the $http call out into a 'service' that provides data to your controller:
module.factory('WidgetData', function($http){
return {
get : function(params){
return $http.get('url/to/widget/data', {
params : params
});
}
}
});
module.controller('WidgetController', function(WidgetData){
WidgetData.get({
id : '0'
}).then(function(response){
//Do what you will with the data.
})
});
Abstracting the $http call like this will allow you to reuse this code across multiple controllers. This becomes necessary when the code that interacts with this data becomes more complex, perhaps you wish to process the data before using it in your controller, and cache the result of that process so you won't have to spend time re-processing it.
You should think of the 'service' as a representation (or Model) of data your application can use.
The accepted answer was giving me the $http is not defined error so I had to do this:
var policyService = angular.module("PolicyService", []);
policyService.service('PolicyService', ['$http', function ($http) {
return {
foo: "bar",
bar: function (params) {
return $http.get('../Home/Policy_Read', {
params: params
});
}
};
}]);
The main difference being this line:
policyService.service('PolicyService', ['$http', function ($http) {
I put an answer for someone who wanted a totally generic web service in Angular. I'd recommend just plugging it in and it will take care of all your web service calls without needing to code them all yourself. The answer is here:
https://stackoverflow.com/a/38958644/5349719