Optimizing code in angular directive - angularjs

In angular directive init function i am declaring constant and utilizing the constant in html. I have multiple directives utilizing the same constant i want to keep the constants a reusable one.
In the below example i am using scope.sName and scope.sType in both test and test1 directive how can i have IT IN A COMMON PLACE AND REUSE IT.
app.directive('test', function() {
return {
restrict:'A',
scope: { someVal: '='}
link: function(scope, element, attrs) {
scope.sType = InvertoryConstant.serviceType;
scope.sName = InvertoryConstant.serviceName;
}
}});
app.directive('test1', function() {
return {
restrict:'A',
scope: { someVal: '='}
link: function(scope, element, attrs) {
scope.sType = InvertoryConstant.serviceType;
scope.sName = InvertoryConstant.serviceName;
}
}});

You can use the $provider.constant method in AngularJS for creating DRY constants:
angular.module('projectApp', [])
.constant('EXAMPLE_CONSTANT','exampleConstant')
You can inject EXAMPLE_CONSTANT into any of your modules, as a dependency

Related

Directive within another directive - scope var undefined

I'm trying to generate a smart-table directive from within a custom directive I've defined:
<div ng-controller="myContrtoller">
<containing-directive></containing-directive>
</div>
The directive definition:
angular.module('app')
.directive('containingDirective', function() {
return {
restrict: 'E',
replace: true,
template: '<table st-table="collection" st-pipe="scopeFun"></table>',
link: function(scope, elem, attrs) {
scope.scopeFun = function () {
// solve the misteries of life
}
}
}
});
As you can see my directive tries to replace the element by the template generated by the st-table directive, using the st-pipe directive depending on the first, briefly:
ng.module('smart-table')
.controller('stTableController' function () {
// body...
})
.directive('stTable', function () {
return {
restrict: 'A',
controller: 'stTableController',
link: function (scope, element, attr, ctrl) {
// body
}
};
})
.directive('stPipe', function (config, $timeout) {
return {
require: 'stTable',
scope: {
stPipe: '='
},
link: {
pre: function (scope, element, attrs, ctrl) {
var pipePromise = null;
if (ng.isFunction(scope.stPipe)) { // THIS IS ALWAYS UNDEFINED
// DO THINGS
}
},
post: function (scope, element, attrs, ctrl) {
ctrl.pipe();
}
}
};
});
Problem:
The st-pipe directive checks the scope var stPipe if it is defined or not by: if (ng.isFunction(scope.stPipe)). This turns out to be ALWAYS undefined. By inspecting I found two things:
From the stPipe directive, the value supposed to be scope.stPipe that is my scopeFun defined within my containingDirective is undefined on the scope object BUT defined within the scope.$parent object.
If I define my $scope.scopeFun within the myContrtoller I don't have any problem, everything works.
Solution:
I did find a solutions but I don't know what really is going on:
Set replace: false in the containingDirective
Define the scope.scopeFun in the pre-link function of containingDirective
Questions:
Why is the scopeFun available in the stPipe directive scope object if defined in the controller and why it is available in the scope.$parent if defined in the containingDirective?
What is really going on with my solution, and is it possible to find a cleaner solution?
From the docs: "The replacement process migrates all of the attributes / classes from the old element to the new one" so what was happening was this:
<containing-directive whatever-attribute=whatever></containing-directive>
was being replaced with
<table st-table="collection" st-pipe="scopeFun" whatever-attribute=whatever></table>
and somehow st-table did not enjoy the extra attributes (even with no attributes at all..).
By wrapping the containingDirective directive template within another div fixed the problem (I can now use replace:true):
<div><table st-table="collection" st-pipe="scopeFun"></table></div>
If someone has a more structured answer would be really appreciated

Angular. restrict: 'A' directive. Pass object

Is there a way to pass configuration object into custom directive which defined as a attribute-directive?
I've got an object in Controller that I want to send to directive:
$scope.configObject = {
count: 5,
mode: 'fast',
getData: myService.getData // function of external service that avaliable in controller
}
In my View I declare directive:
<div class='list-class' my-list='configObject'></div>
Directive looks like:
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = angular.fromJson(attrs.myList);
}
}
i've tried to get config object using angular.getJson - but it doesn't work for functions (it's possible to get only count and mode). Is .getJson() the incorrect way to get config?
Also (I guess it's not even possible) - is there a way to get config object avoiding accessing to
attrs.myList
directly? I mean if I change initializing of directive from
.directive('myList', function() { ... }) to
.directive('myCustomList', function() { ... })
shall I change accessing to
attrs.myCustomList
because view would look like
<div class='list-class' my-custom-list='configObject'></div>
you can pass it using isolate scope if you want
return {
restrict: 'A',
scope: { config : '=myList' }
link: function(scope, elem, attrs) {
//access using scope.config
}
}
or as already answered you can parse it from attrs
$parse(attrs["myList"])(scope);
and yes if you change the directive to myCustomList, you will have to change the code
scope: { config : '=myCustomList' }
or
$parse(attrs["myCustomList"])(scope);
You can use $parse service to fetch the config object.
(function(){
var directiveName = 'myList';
angular.module('YourModule').directive(directiveName,['$parse',function($parse){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = $parse(attrs[directiveName])(scope);
}
};
}]);
})();
You can $eval the attribute
link: function(scole, element, attrs) {
var config = scope.$eval(attrs['myList']);
}

How to bind content of tag into into directive's scope?

Say I have a directive like such:
<my-directive>This is my entry!</my-directive>
How can I bind the content of the element into my directive's scope?
myApp.directive('myDirective', function () {
return {
scope : {
entry : "" //what goes here to bind "This is my entry" to scope.entry?
},
restrict: "E",
template: "<textarea>{{entry}}</textarea>"
link: function (scope, elm, attr) {
}
};
});
I think there's much simpler solution to the ones already given. As far as I understand, you want to bind contents of an element to scope during initialization of directive.
Given this html:
<textarea bind-content ng-model="entry">This is my entry!</textarea>
Define bind-content as follows:
directive('bindContent', function() {
return {
require: 'ngModel',
link: function ($scope, $element, $attrs, ngModelCtrl) {
ngModelCtrl.$setViewValue($element.text());
}
}
})
Here's a demo.
I may have found a solution. It relies on the transclude function of directives. It works, but I need to better understand transclusion before being sure this is the right way.
myApp.directive('myDirective', function() {
return {
scope: {
},
restrict: 'E',
replace: false,
template: '<form>' +
'<textarea ng-model="entry"></textarea>' +
'<button ng-click="submit()">Submit</button>' +
'</form>',
transclude : true,
compile : function(element,attr,transclude){
return function (scope, iElement, iAttrs) {
transclude(scope, function(originalElement){
scope.entry = originalElement.text(); // this is where you have reference to the original element.
});
scope.submit = function(){
console.log('update entry');
}
}
}
};
});
You will want to add a template config to your directive.
myApp.directive('myDirective', function () {
return {
scope : {
entry : "=" //what goes here to bind "This is my entry" to scope.entry?
},
template: "<div>{{ entry }}</div>", //**YOU DIDN'T HAVE A TEMPLATE**
restrict: "E",
link: function (scope, elm, attr) {
//You don't need to do anything here yet
}
};
});
myApp.controller('fooController', function($scope){
$scope.foo = "BLAH BLAH BLAH!";
});
And then use your directive like this:
<div ng-controller="fooController">
<!-- sets directive "entry" to "foo" from parent scope-->
<my-directive entry="foo"></my-directive>
</div>
And angular will turn that into:
<div>THIS IS MY ENTRY</div>
Assuming that you have angular setup correctly and are including this JS file onto your page.
EDIT
It sounds like you want to do something like the following:
<my-directive="foo"></my-directive>
This isn't possible with ELEMENT directives. It is, however, with attribute directives. Check the following.
myApp.directive('myDirective', function () {
return {
template: "<div>{{ entry }}</div>", //**YOU DIDN'T HAVE A TEMPLATE**
restrict: "A",
scope : {
entry : "=myDirective" //what goes here to bind "This is my entry" to scope.entry?
},
link: function (scope, elm, attr) {
//You don't need to do anything here yet
}
};
});
Then use it like this:
<div my-directive="foo"></div>
This will alias the value passed to my-directive onto a scope variable called entry. Unfortunately, there is no way to do this with an element-restricted directive. What is preventing it from happening isn't Angular, it is the html5 guidelines that make what you are wanting to do impossible. You will have to use an attribute directive instead of an element directive.

How to append a custom angular directive into another custom angular directive?

I have two custom angular directives and one appends the second repeatedly. The problem is that although the tag is appended, the template of the directive is not. When I manually put it in, it works.
See this jsfiddle: http://jsfiddle.net/HB7LU/5555/
Here is the code where the appending takes place:
myApp.directive('formList', function () {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
element.append('appended <my-form></my-form>'); // apended<my-form></my-form> will appear but not the contents of <my-form>
};
}
};
});
You have to use $compile service to manually compile your my-form directive like this:
myApp.directive('formList', function ($compile) {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
var newForm = $compile('<span>appended </span><my-form></my-form>')(scope);
element.append(newForm);
};
}
};
});
Example JSFiddle: http://jsfiddle.net/9L3whcqc/

$log (or other services inside AngularJS directives)

I have the following directive I use to initialize the timeago plugin.
Directives.directive('timeago', function() {
return function(scope, element, attrs) {
$(element).attr('title', scope.post.utc_posted);
$(element).timeago();
}
});
How could I use/pass $log inside the function I'm returning?
You can just inject it the normal way. BTW element is already a jQuery variable and does not need $(element) - providing you're loading jQuery before Angular.
Directives.directive('timeago', function($log) {
return {
link: function(scope, element, attrs) {
element.attr('title', scope.post.utc_posted);
element.timeago();
}
}
});

Resources