简体   繁体   中英

How to inject service depending on directive attribute in angular?

I have the following directive

directiveModule.directive('typeahead', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: 'isolate',
        templateUrl: 'assets/partials/common/typeahead.html' ,
        controller: ["$scope","$element","$attrs","$transclude", "Event", 
        function ($scope,$element,$attrs,$transclude, Event){
            console.log(eval($attrs.service + ".query();"));

            $scope.search = function(){ console.log("Searching for:" + $scope.selectedValue) };
            $scope.selectedValue = "";
            $scope.providedItems = []; 
        }],     
    };
});

With the following template:

<div>
<input ng-change="search()" ng-model="selectedValue" ng-click="log()" autocomplete="off" type="text"/>
<ul class=".typeahead">
    <li ng-repeat="item in providedItems">{{eval(item + "." + descriptor)}}</li>
</ul>

and the following call inside my view:

I would like to let the service attribute be evaluated at runtime when injecting to the controller in the directive. So that where it now says

controller: ["$scope","$element","$attrs","$transclude", "Event", 
        function ($scope,$element,$attrs,$transclude, Event){

It would say something similar to:

controller: ["$scope","$element","$attrs","$transclude", eval($attrs.service), 
        function ($scope,$element,$attrs,$transclude, eval($attrs.service)){

However I can't access the $attrs from the scope inside the directiveModule call.

If there is any way to access a service declared in the view that would suffice. Please help!

One solution for this, would be creating and binding the controller yourself. All you need is to inject both $injector (in order to resolve the service dynamically) and $controller (in order to resolve the controller dynamically). And in your linking function you create the controller yourself:

link: function(scope, elm, attr) {
  $controller(function YourController($scope, dynamnicService) {
      dynamicService.query();
    }, {
      $scope: scope, 
      dynamicService: $injector.get($attr.service)
    }
  )
}

There is one thing important here. I'm considering, in this example, that your service is a string in the element attribute. If it's a string inside the scope, referred by the attribute, then you have to change the approach. You should $attr.observe the attribute, and on change, you should grab the service $injector.get(...) and pass it to the controller. You ould either create a this.setService method on the controller itself, or $scope.setService method, your call. I'd rather the controller, as this is related to accessing a service. Using this second approach, you don't need to create the controller by hand, you can simple expose this method and set the data from outside.

One more info, you should NEVER TOUCH THE DOM FROM YOUR CONTROLLER . So passing $element , $attr and $transculde to the controller is probably a bad idea, no matter what.

Take a look at the docs about $controller , $injector and directive .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM