Reputation: 3542
I have two/more services with same signature. Can I dynamically inject then into the directive? something like below
var app = angular.module('app',[]);
app.factory('myData', function(){
return {
name : "myName",
id : 1,
create: function(){
//do something
}
}
});
app.factory('yourData', function(){
return {
name : "yourName",
id : 1,
create: function(){
//do something
}
}
});
app.directive('changeIt',function($compile){
return {
restrict: 'CA',
scope:{
data : '=' //or some oether syntax?
},
link: function (scope, element, attrs) {
scope.name = data.name;
}
}
});
Then I should be able to use the directive as below
<div class='change-it' data='myData'>{{name}}</div>
<div class='change-it' data='yourData'>{{name}}</div>
I would be adding more services with the same signature and I should be able to use the directive without changing, is it possible?
Upvotes: 6
Views: 21834
Reputation: 101
Here is a solution without the need for a parent controller or a factory of factories.
In the directive, inject the $injector service to retrieve the factory instance:
app.directive('changeIt',function(){
return {
scope:{
factoryName : '@'
},
controller: function ($scope, $injector) {
var factoryInstance = $injector.get($scope.factoryName);
$scope.name = factoryInstance.name;
}
}
});
Notice it's happening in the controller method. I could not make $injector.get() work in the link function.
Template :
<div class='change-it' factory-name="myData"> {{ name }} </div>
<div class='change-it' factory-name="yourData"> {{ name }} </div>
-- Edit --
Working solution within the link function :
app.directive('changeIt',function(){
return {
scope:{
factoryName : '@'
},
link: function (scope, element) {
var factoryInstance = element.injector().get(scope.factoryName);
scope.name = factoryInstance.name;
}
}
});
Upvotes: 10
Reputation: 14114
That's not possible that way. The best you can do is bind the directive scope to a function of its parent scope that returns an instance of your service:
app.directive('changeIt', function(){
return {
restrict: 'CA',
scope: { getDataFn : '&' },
link: function (scope) {
scope.name = getDataFn().name;
}
}
});
and then in your view:
<div class='change-it' get-data-fn='getMyData()'></div>
<div class='change-it' get-data-fn='getYourData()'></div>
Finally, you need to add getMyData()
and getYourData()
to the parent scope:
app.controller('Ctrl', function($scope, myData, yourData) {
$scope.getMyData = function() {
return myData;
};
$scope.getYourData = function() {
return yourData;
};
});
Plunker script here.
I can think of another approach, though: you could create an abstract factory and inject it into the directive, and then pass a parameter to the directive so it could tell the abstract factory to create the correct service. Something like this:
app.service('dataFactory', function(myData, yourData) {
this.create = function(type) {
if (type === 'myData')
return myData;
else if (type === 'yourData')
return yourData;
};
});
app.directive('changeIt', function(dataFactory){
return {
restrict: 'CA',
scope: true ,
link: function (scope, element, attrs) {
scope.name = dataFactory.create(attrs.type).name;
}
}
});
And now you need to pass the type to the directive:
<div class='change-it' type="myData"></div>
<div class='change-it' type="yourData"></div>
Plunker here.
Upvotes: 14