How to dynamically inject service into directive in angularjs - angularjs

My directive is to build out a scaffolded table with a delete button. I have a service set up named Post, so when I call the directive I'm including a servicename property on the element. How do I dynamically inject my service based on my service name attribute I'm including? Or is there a better way to do this?
var lassoDirectives = angular.module('lassoDirectives', []);
lassoDirectives.directive('autoTable', ['parse', function(parse) {
return {
restrict: 'AE',
scope: {
data: '=',
modelname: '='
},
templateUrl: 'http://local.angular.com/webroot/app/templates/directives/auto-table.html',
controller: function($scope) {
console.log(parse);
$scope.deleteItem = function(id) {
console.log(id);
// rocket = modelname.query();
//console.log(rocket);
}
$scope.check = function() {
parse.get("Posts", 123, function(response) {
console.log(response.toString());
});
}
}
};
}]);

I'm not sure that is the best way to go around it. You should probably have a single service that is charge of all the data related to this lassoDirective, so you could just inject it as normal.
But you may be able to use the $injector provider like...
.directive('autoTable', function($injector) {
return {
restrict: 'EA',
scope: {
servicename: '='
},
controller: function(scope) {
var service = $injector.get(scope.servicename);
scope.deleteItem = function(Id) {
del = service.delete({Id: Id});
items = service.query();
};
}
}
});
If you want your service to perform differently depending on the directive clicked, it may be better to just pass your service additional parameters relating to the directive? Not sure what your situation is so ignore this common if N/A.

Related

Unable to call Angular Directiive Method on Button Click

I'm trying to call directive method on button click from the calling controller.
Here is the directive code:
myApp.directive("helloDirective", function() {
return {
restrict: "E",
template: '<input type="text" data-ng-model="model.msg" />',
scope: {},
bindToController: {
param: "="
},
controller: 'helloDirectiveController',
controllerAs: 'model'
}
})
.controller("helloDirectiveController", function() {
var self = this;
self.actions = {
get: function() {
return self.msg;
},
set: function(msgData) {
self.msg = msgData;
}
});
I have call the get and set method from controller..
myApp.controller("indexController", [function() {
var self = this;
self.helloParam ={};
self.get = function() {
//how to call the Directive get method from here
}
}]);
i tried to create a fiddle here
plnkr
The idea
For me, the cleanest solution (so far) for your problem is a solution used by Angular Material developers (I don't know if they were the authors, but I found it there). I've used it once in my project and it worked like a charm.
The idea is to create a global registry for directives' actions. Directives would be stored there by unique ids. We can also create a service dedicated for each directive, just in case we need some external logic.
The solution
1. Components registry
Firstly, we need a components registry. It can be a really simple service:
angular.module('app');
.service('$componentsRegistry', function() {
var self = this;
var components = {};
self.put = function(id, actions) {
components[id] = actions;
}
self.get = function(id) {
return components[id];
}
})
The components registry has methods for storing and getting components by ids. Of course, there might be much more methods and they might be more complicated, but this is just a simple example.
2. Service for our directive
Let's say we have a simple show-message directive, so we can create a $showMessageService:
angular.module('app')
.service('$showMessageService', ['$componentsRegistry', function($componentsRegistry) {
return function(id) {
return $componentsRegistry.get(id);
}
}])
For now, the only task of the service is to return our directive's actions. But it can be extended in the future, of course.
3. Directive's code
The last thing we need is our show-message directive:
angular.module('app')
.directive('showMessage', function($componentsRegistry) {
return {
restrict: 'E',
scope: {
directiveId: '#' // Unique id is passed from the view
},
template: '<div>{{ text }}</div>',
link: function(scope) {
scope.text = 'TEST TEXT';
// Create actions
scope.actions = {
set: function(value) {
scope.text = value;
}
}
// Store actions in the components registry
$componentsRegistry.put(scope.directiveId, scope.actions);
}
}
})
In a link function, we need to simply register our actions in components registry. We pass the unique id from the view so that developer has control over it inside views/controllers.
Example of usage
And now we can finally use the directive in our application. Here is a simple code which shows how to do that:
View
<div ng-controller="testController as test">
<show-message directive-id="testDirective"></show-message>
<button ng-click="test.changeText()">CHANGE</button>
</div>
Controller
angular.module('app')
.controller('testController', function(['$showMessageService', $showMessageService) {
var self = this;
self.changeText = function() {
$showMessageService('testDirective').set('NEW');
}
}])
I've Change The Directive Code, moved Controller code to link and it's working fine.
testApp.directive("helloDirective", function () {
return {
restrict: "E",
template: '<input type="text" data-ng-model="model.msg" />',
scope: {},
bindToController: {
param: "="
},
link: function (scope, element, attrs) {
var self = scope.model;
var assignMethod = function () {
if (self.param == undefined) {
self.param = {};
}
self.param.actions = {
get: function () {
return self.msg;
},
set: function (msgData) {
self.msg = msgData;
}
};
};
assignMethod();
},
controller: function () { },
controllerAs: 'model'
}
});
now i can call the directive get Method from calling controller like,
self.helloParam = {};
self.click = function () {
alert(self.helloParam.actions.get());
}

Passing an object as attribute to compiled directive on the fly

I have a angular element on the page which needs to communicate with the rest of the non angular page elements.
I am creating directive elements on the fly, and appending it to its target div. I am trying to pass that created directive an object (ajax object), which contains just attributes.
The issue is that I can't figure out how to pass just this ajax object to the directive, as $compile requires a scope. When the http finishes, and because i have to use = in the directive, the directives are being over-ridden.
Please see my plunk: https://plnkr.co/edit/brTWgUWTotI44tECZXlQ ( sorry about the images ). Click the <button> to trigger the directive.
(function() {
'use strict';
var CHANNEL = 'podOverlay';
angular.module('CavernUI', [])
.controller('CavernCtrl', function($scope,getItemService) {
$scope.model = {};
var _pods = $scope.model.pods = {};
function getData(selector) {
$(selector).each(function(i, pod) {
_pods[+pod.dataset.item] = {
$: $(pod)
};
});
Object.keys($scope.model.pods).map(function(key) {
getItemService.getItem(key).success(function(response) {
_pods[key] = angular.extend(_pods[key], response);
$scope.$broadcast(CHANNEL, _pods[key], $scope);
});
})
}
$scope.runPodCheck = function(selector) {
getData(selector);
}
})
.directive('podchecker', function($compile) {
var createOverlay = function(e,data,scope){
scope.data = data;
// can i just pass data rather than scope.data?
// If I pass the scope, then when another $broadcast happens
// the scope updates, wiping out the last scope change.
// Scope here really needs to be a static object that's
// created purely for the hand off. But I don't know if
// that can be done.
angular.element(data.$[0]).empty().append($compile('<overlay data="data"></overlay>')(scope));
}
return {
restrict: 'E',
scope: {
check: '&',
},
templateUrl: 'tpl.html',
link: function(scope,elm,attr){
scope.$on(CHANNEL,createOverlay);
}
};
})
.directive('overlay', function() {
return {
restrict: 'E',
scope: {
o: '=data' // here is the problem.
},
template: '<div class="overlay"><img ng-src="{{o.images.IT[0]}}"/></div>',
link: function(scope, elm, attr) {
}
}
})
.service('getItemService', ['$http', function($http) {
this.getItem = function(itemId) {
return $http({
method: 'GET',
url: 'https://www.aussiebum.com/ajaxproc/item',
params: {
id: itemId,
ajxop: 1
},
});
};
}]);
}());
Edits:
Expected ouput:
I'm not sure this is the best approach, but one way might be to manually create a new scope for each of the overlays.
So changed this:
var createOverlay = function(e,data,scope){
scope.data = data;
angular.element(data.$[0]).empty().append($compile('<overlay data="data"></overlay>')(scope));
}
to this:
var createOverlay = function(e,data,scope){
var overlayScope = scope.$new(false); // use true here for isolate scope, false to inherit from parent
overlayScope.data = data;
angular.element(data.$[0]).empty().append($compile('<overlay data="data"></overlay>')(overlayScope));
}
Updated Plnkr: https://plnkr.co/edit/wBQ1cqVKfSqwqf04SnPP
More info about $new()
Cheers!

How to deal with more than one scope in an Angular directive?

Using directives I ended stuck when I needed to have more than one scope.
I'm building a data visualization app with Mongoose Node, Express and D3JS.
Here's the directive
angular.module('prodsChart', [])
.controller('businessCtrl', ['$scope','$http', 'BusinessSrv', 'Products', function($scope, $http, $location, BusinessSrv, Products) {
Products.get()
.success(function(data) {
$scope.products = data;
});
BusinessSrv.getTotal()
.success(function(data) {
$scope.businessSales = data;
});
}])
.directive( 'saleProd', [
function () {
return {
restrict: 'E',
link: function (scope, element) {
// Building the chart here
}
And the HTML :
<sale-prod></sale-prod>
Is it good to inject that way the Services in the Directive ?
Now I have 2 set of data in two $scope.
How do I use them in the directive ?
You can inject the $rootScope into your directive:
angular.module('prodsChart', [])
.directive( 'saleProd', ['$rootScope', function ($rootScope) {
// your code here
}]);
and then use it everywhere within the directive.
In 1st and 2nd example directive is in controller's scope and controller's datasets transferred to the directive as attributes. In 1st example directive can modify controller's data. In 2nd example directive use controllers data as strings and creates 2 objects 'productsObj' and 'salesObj' and can't modify parent's scope. It depends on how you handle attributes in the directive and how to transfer them into it. Just click on items in 'Product list (directive)' section and you'll see result.
1st: http://plnkr.co/edit/v46oEGHvUnxMNYsKAeaW?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '=',
sales: '='
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="BusinessCtrl.products" sales="BusinessCtrl.sales"></sale-prod>
</div>
2nd: http://plnkr.co/edit/7CyIsqBNLbeZjyfbkGo9?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '#',
sales: '#'
},
link: function(scope, element, attrs) {
attrs.$observe('products', function(newVal) {
scope.productsObj = angular.fromJson(newVal);
});
attrs.$observe('sales', function(newVal) {
scope.salesObj = angular.fromJson(newVal);
});
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="{{BusinessCtrl.products}}" sales="{{BusinessCtrl.sales}}"></sale-prod>
</div>
3rd example is just a piece of code that show's how to inject service in directive and controller. I add it because in you example i didn't see service injection in directive:
(function(undefined) {
var app = angular.module('prodsChart', []);
var controller = function($scope, Business, Products) {
// controller logic
};
var directive = function(Business) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// here you can use all Business service logic
}
};
};
var serviceBusiness = function() {
// business service logic
};
var serviceProducts = function() {
// products service logic
};
app.controller('BusinessController', ['$scope', 'Business', 'Products', controller])
.directive('saleProd', ['Business', directive])
.service('Business', serviceBusiness)
.service('Products', serviceProducts);
})();
html:
<div ng-controller="BusinessController as BusinessCtrl"></div>
<sale-prod></sale-prod>
You could inject a Service into a directive and use it to bring data across the application or use $emit.
A less elegant solution would be using a .value() and deal with it everywhere in your application.

Outputting a Service Property Value Through a Directive in Angular

My goal is to output a value (from a service) through a element directive so that the html will look like this <msg msg="alertMsg"></msg> and out pops a value from the service.
Here is my code thus far:
app.directive("msg", ['MsgService', function(MsgService) {
return {
restrict: "E",
scope: {//something here to pass MsgService to template },
template: 'Message:{{MsgService.getAlertMsg()}}'
};
}]);
app.service('MsgService', function() {
this.alertMsg = 'default';
this.getAlertMsg = function(){
return this.alertMsg;
};
this.setAlertMsg = function(string) {
this.alertMsg = string;
};
});
HTML would parse/compile to...
<msg msg="alertMsg">Message: default</msg>
What other code do I need?
If a service wont work directly, Should I access it through a controller?
app.directive("msg", function() {
return {
restrict: "E",
scope: {
getMsg: '&msg'
},
controller: 'MsgController',
template:'Message:{{getMsg()}}'
};
}]);
app.controller('MsgController', ['MsgService' , function(MsgService){
this.getAlertMsg = function(){
return MsgService.getAlertMsg();
};
}]);
HTML would parse/compile to...
<msg msg="getAlertMsg()">Message: default</msg>
Sorry for any errors in code or function use, I'm fairly new to Angular.
You can use the link function of the directive. This function is called once for every rendered instance of your directive. It receives, among other things, the scope of your directive. You can extend your scope very easily with the result of calling the MsgSevice.getAlertMsg() service method:
var app = angular.module("app", []);
app.directive("msg", ['MsgService', function(MsgService) {
return {
restrict: "E",
scope: true,
template: 'Message:{{msg}}',
link: function (scope, $element, attrs) {
scope.msg = MsgService.getAlertMsg();
}
};
}]);
app.service('MsgService', function() {
this.alertMsg = 'default';
this.getAlertMsg = function(){
return this.alertMsg;
};
this.setAlertMsg = function(string) {
this.alertMsg = string;
};
});
Later on, I presume you will want to just display the alert message from the msg DOM attribute of the msg directive. Achieving this is much more simple, since AngularJS is already prepared for this common use case. The solution involves creating an isolate scope. The isolate scope can be populated with properties from the parent environment. One possibility is to use the value of a DOM attribute from your directive's element using the "#" syntax. In this case you won't even need the entire MsgService service:
app.directive("msg", function () {
return {
restrict: "E",
scope: {
"msg": "#"
},
template: 'Message:{{msg}}'
};
});
Simplest would be to set the service on your scope and use that in your template:
app.directive("msg", ['MsgService', function(MsgService) {
return {
restrict: "E",
scope: { },
template: 'Message:{{MsgService.getAlertMsg()}}',
link: function(scope, element, attrs) {
scope.MsgService = MsgService;
}
};
}]);

Learning Angular - is this the right way to link a div to a service?

I'm learning Angular and I'm wondering if there is a better way to link this directive to my service?
Mostly I'm wondering if storing a Guid as an Id in the model and then referring to that id in my service is a good way to link the directive and the service? Or is there a more "Angular" way to do this?
Directive:
myApp.directive("myDialog", ['$compile', function ($compile) {
return {
restrict: 'E',
scope: {
model: "="
},
template: "<div id='dialog_{{model.dialogId}}' ng-include='model.templateUrl' style='display: none'></div>",
replace: true
}]);
Service:
myApp.service("myDialogSvc", function () {
// This will be called by the controller
this.init = function (template) {
var model = {};
model.template = template;
var id = newGuid(); // Gets a new Guid -- defined elsewhere
model.dialogId = id;
return model;
}
this.open = function (model) {
$("#dialog_" + model.dialogId).dialog({
modal: true
});
}
});
this seems to be factory pattern. I have seen people using this approach in angular, imho, a decent appraoch

Resources