问题
After reading several articles on avoiding scope soup and referencing the Google Guidelines for building controllers I have been left with one burning question. How should I reference my injected dependencies within my controller?
My approach thus far is to put the services on my object but I'm not exactly happy with that as now my services are exposed to the outside world (the template markup). What is the correct approach to build a controller without directly referencing $scope, and have my injected dependencies available to the controller but not exposed publicly.
As you can see below my work around is to put $http on 'this' and then reference it in my prototyped functions. Not my ideal choice for the aforementioned reasons.
http://plnkr.co/edit/Tn6Jkk06Zdu92uTM0UdU?p=preview
DOCTYPE html>
<html>
<head>
<script data-require="angular.js@*" data-semver="1.3.0-rc2" src="https://code.angularjs.org/1.3.0-rc.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="textController as txtCtrl">
<h1>{{txtCtrl.greeting}}</h1>
<button ng-click="txtCtrl.getData()">Get Names</button>
<hr>
{{txtCtrl.names | json}}
<script>
var textController = function($http){
this.greeting="Hello World";
this.$http = $http;
}
textController.prototype.alert = function(){
alert(this.greeting);
}
/*retrieve test data from //filltext.com*/
textController.prototype.getData = function(){
var that = this;
this.$http({
method: 'GET',
url: 'http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}'
})
.success(function(data){
that.names=data;
})
.error();
}
angular.module("app",[])
.controller("textController",textController);
angular.bootstrap(document,["app"]);
</script>
回答1:
One way you could do is to create a closure variable using IIFE and keep reference of http service
outside the controller, and use it in the controller.
(function(){
var _$http; //<-- Here a private variable
......
/*retrieve test data from //filltext.com*/
textController.prototype.getData = function(){
var that = this;
_$http({ //<-- Use it
method: 'GET',
url: 'http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}' })...
//...
}
angular.module("app").controller("textController",textController);
})();
Plnkr
Or add a property to the constructor instead of its instance.
(function(){
var textController = function($http){
this.greeting="Hello World";
textController._$http = $http; //<-- Here set it
}
//....
/*retrieve test data from //filltext.com*/
textController.prototype.getData = function(){
var that = this;
textController._$http({ //<-- Use it
method: 'GET',
url: 'http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}'})...
//...
}
//...
angular.module("app").controller("textController",textController);
})();
Plnkr
Here is how you can explicitly annotate your dependencies (unless you use ng-annotate which will take of it while minification)
angular.module("app").controller("textController",['$http', 'blah', textController]);
Or
textController.$inject = ['$http', 'blah'];
However i would just leave the responsibility of making ajax calls and any data mapping to a service:-
Example:-
angular.module("app").service('UserService', ['$http', function($http){
this.getUsers = function(searchObj){ //Get the input and set your url
return $http({
method: 'GET',
url: 'http://www.filltext.com/?rows=10',
params: searchObj //<-- Pass params
});
}
}]);
And just inject userService in the controller.
var textController = function(userSvc){
this.greeting="Hello World";
this.userSvc = userSvc;
}
....
/*retrieve test data from //filltext.com*/
textController.prototype.getData = function(){
var that = this;
//Call service with argument
this.userSvc.getUsers({firstName:$scope.fn, lastName:$scope.ln}).success(function(data){
that.names=data;
})...;
}
....
angular.module("app").controller("textController",['UserService', textController]);
Plnk3
回答2:
I think you may be thinking too hard about this. If getData() needs to be accessible from our ( view ) html then it should be attached to the scope.
回答3:
In your use case I would use the injector manual to keep a reference to $http in a closure.
var $http = angular.injector(['ng']).get('$http');
Here is an updated plunk. This way you can keep the private things inside your controller.
EDIT: just noticed that is way similar to @PSL solution. Sorry, I didn't notice his first solution. However, I'm bringing the manual injector, so I'm keeping this answer if that's ok by you! EDITEDIT: Updated the plunker to be a bit more testable.
来源:https://stackoverflow.com/questions/26260444/how-should-i-reference-services-in-my-controller-functions-without-using-scope