Adding Angular directive using decorator - javascript

I'm using Angular 1.4.8 with Angular UI. What I'm trying to do is decorate the ui-sref directive so it will highlight a menu element (by setting the CSS class 'active') if the current $state.name matches the ui-sref state.
I test to see if the element is descendent from my nav header, and if it is, I want to add an ngClass attribute to the element. For right now, I just want to make them all highlight; I can add the test for matching the state later. The true will be replaced with the actual test. Right now I just want the active class set
.config(['$provide', ($provide: angular.auto.IProvideService) => {
return $provide.decorator('uiSrefDirective', [
'$delegate', ($delegate) => {
var originalUiSref = $delegate[0];
var originalUiSrefLink = originalUiSref.link;
originalUiSref.compile = () => (scope, element, attrs, uiSref) => {
var topBar = $('nav.top-bar');
if (topBar.length > 0 && $.contains(topBar[0], element[0])) {
element.parent().attr('ng-class', '{ active: true }');
}
originalUiSrefLink.call($delegate, scope, element, attrs, uiSref);
};
return $delegate;
}
]);
}])
The original DOM element:
<a ui-sref="requests">Requests</a>
After adding the decorator, this is what I see in my browser's DOM:
<a ui-sref="requests" ng-class="{ active: true }" href="/requests">Requests</a>
Great! I can see the added attribute in the DOM, but my browser is ignoring it. It's almost as though it's getting added after Angular scans the DOM for directives. If I change the code to:
element.parent().addClass('active');
... then it works fine. What am I doing wrong?

Amy you do not need to implement this directive as angular-ui in fact already have it, please check ui-sref-active

Related

Angular js : Unable to create directive

I am trying to create directive but my directive function is not getting called.
index.html
<div ng-repeat="que in questions.Cars">
<question-dir>print from direcetive</question-dir>
</div>
ConytrolDirective.js
(function () {
"use strict";
angular
.module("autoQuote")
.directive('questionDir',["questionDir"] );
function questionDir()
{
return {
template : "<h1>Made by a directive!</h1>"
};
}
}());
There are several mistakes in your code.
You should have function name instead of "questionDir"
.directive('questionDir',[questionDir] );
Use kebab case(- separated words) while using directive name
`<question-dir>print from direcetive</question-dir>`
Additionally you need to refer <script src="controlDirectives.js"></script> on your index.html page.
Demo here
I don't see many mistakes in your code but it would have helped many to stop assuming only if you had posted your code sample link too.
But one primary change you have to make is : Change your directive definition function invoking from
.directive('questionDir', ["questionDir"])
to
.directive('questionDir', questionDir)
See this working fiddle which has just added questions.Cars into $rootScope to make answer look more relevant to your query.
To preserve content of your directive element :
What i see in your question is : Element <question-dir> has child content / inner text print from direcetive in it and it gets overridden by Directive Element <h1>Made by a directive!</h1> completely.
That's the default nature of Angular : Element's (to which the directive is being applied) children will be lost to Directive Element. If you wanted to perserve the element's original content/children you would have to translude it.
Use transclude : true in Directive Definition Object and add <div ng-transclude></div> where you want directive Content / text print from direcetive to be included.
Code snippet :
function questionDir() {
return {
transclude : true,
template: "<div ng-transclude></div><h1>Made by a directive!</h1>"
};
}
Check this one for transclude changes.
Check these 2 links for more information on Transclude :
Understanding the transclude option of directive definition
Understanding transclude with replace
You had a few things wrong in you code:
In your IIFE closure you need to pass in angular
You need to normalize the directive name to kabab case in your html element name: `'
You need to include the reference to the directory factory in your directive registration: .directive('questionDir', questionDir);
Missing module dependencies array: .module("autoQuote", []) -- you should only have this defined once with the dependancies, [] so ignore this if you already have it elsewhere in your code.
Missing restrict in the Directive Definition Object: restrict: 'E',
If you click on Run code snippet you will see this directive now works.
(function (angular) {
"use strict";
angular
.module("autoQuote", [])
.directive('questionDir', questionDir);
function questionDir()
{
return {
restrict: 'E',
template: "<h1>Made by a directive!</h1>"
};
}
}(angular));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="autoQuote">
<question-dir>print from direcetive</question-dir>
</div>
Finally, if you are using AngularJS 1.5 or greater you should consider using the component syntax for this.

get an element attributes values using Angular after using template

I am relatively new to Angular and this my first attempt at using a custom directive. I have a table I am working with and each cell has a data attribute. For example, "data-dept="service", and I would like to access this value before overwriting it with a directive. In my directive, I have set template = true, and this removes my data attribute.
I liked my website that I am working to better explain what I am attempting to do.
http://partnerportal-preprod.automate-webservices.com/list/#/hoursTable
The cells inside the table in the first row are clickable, but I would like to know if the user is clicking on service for example.
Update
I created plunker to better illustrate my problem.
I have a basic table that I am using a directive to replace a row in the table.
In doing so, I lose the attribute that I would need to set it as a key in my object for later use.
http://plnkr.co/edit/oXRM6lRkidnAHfBA4GsR?p=preview
HTML
<tr>
<td name="valueIneed" hello-world>John</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
Directive
app.directive('helloWorld', function($parse) {
return {
template: '<td><input type="text"></td>',
replace: true,
transclude: 'true',
scope: {
position: '='
},
link: function (scope,element,attrs,ngModel) {
console.log(attrs.attribute);
scope.clickMe = function () {
console.log('clicked');
scope.isChecked = true;
};
}
};
});
First remove replace: true.
Second to see the value of the name attribute:
console.log(attrs.name);
By using replace: true, the directive was replacing the element that had the name attribute.
replace:true is Deprecated1
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636

Use ng-show and etc. in directive "A"

I saw related asks, but dont understand anyway.
If I have directive: http://pastebin.com/QtAzGv62
And I need to add "ng-show" (or any other standart angular directive) functional to this directive (for related DOM element, that is ), that must depends on AuthService option (named "logged").
How?! :)
Since you are not binding to a particular 'element' you just need to make the methods available to your directive scope so change
elem.bind('click', function() {
AuthService.tryLogin();
});
to
scope.loggedIn = false;
scope.tryLogin = function(){
AuthService.tryLogin();
scope.loggedIn = true;
}
Then you can do in your directive html
<div ng-show="loggedIn">You dun logged in man!</div>

How to hide an element after $compile?

There is a directive:
.directive('location', function () {
return {
restrict : 'A',
scope : {},
replace : true,
templateUrl: 'common/components/location/location.html',
link : function (scope, element, attr) {……}
}
});
So it is used in another directive:
var scope = $rootScope.$new(true);
var directive = $compile('<div location></div>')(scope);
$document.find('body').append(directive);
directive.hide(); - not working
How to hide directive generated html after inserting it to body? Directive has "replace" set to true
http://plnkr.co/edit/e7fNua?p=preview
The line you've marked as wrong is actually working meaning that after directive.hide() if you print the element on console you'll see:
console.log(directive[0]);
-> <div location="" class="ng-scope" style="display: none;"></div>
This one is a bit tricky but easy if you carefully follow what you told angular to perform. Let's go through it step by step
$compile('<div location></div>')(scope); - here you're telling angular to compile given template and link it with given scope. Since you have specified templateUrl that must be fetched angular has not yet completed processing this element.
$document.find('body').append(directive); directive.hide(); - append the element to body and immediately hide it. If you print the directive[0] to console you'll see: <div location="" class="ng-scope" style="display: none;"></div>
Note that there is still no <section><p>Text</p></section> inside DOM
Angular completes fetching location.html template.
Since you have replace: true inside directive declaration the directive[0] is removed from DOM and replaced by compiled <section><p>Text</p></section>.
As a result you still see Text on page.
You can change this behaviour in various ways:
change directive declaration to replace: false
use inline template: '<section><p>Text</p></section>'
wrap template that you pass to $compile in another element: $compile('<div class="wrapped"><div location></div></div>')

How to wrap an element using an angular directive?

I'm trying to create an angular component that adds a prefix to an <input> element. Something like this:
The idea would be to use it like this:
<input type="text name="url" input-prefix="http://">
For this I need to wrap the <input> around a <div> container which will also include a <span> with the prefix and add some custom CSS.
I believe I need to use the directive compile function to achieve this because of the DOM manipulation, but I don't understand very well how it works and I haven't found much documentation.
The issue I found so far is that after manipulating the DOM on the compile function, the <input> appears to be completely unusable, I cannot even type in it. Here's my fiddle:
http://codepen.io/anon/pen/vokxy - Can't type on the <input> inside the green wrapper.
You don't manipulate the DOM in the compile phase, you do it in link phase (postLink actually), since the DOM isn't actually set up correctly until then. There's an example in the Developer Guide.
Moved your code to link method, here's a working example.
Example:
var app = angular.module('my-app', []);
app.controller("myController", function($scope) {
$scope.ctrl = this;
$scope.name = "w";
$scope.minLength = 3;
});
app.directive("inputPrefix", function() {
return {
restrict: 'A',
link: function(scope,element, attrs) {
var wrapper = angular.element(
'<div class="prefixed-input">');
element.after(wrapper);
element.removeAttr("input-prefix");
wrapper.append(element);
}
}
});
Live example: http://jsfiddle.net/choroshin/cnc5m/

Categories

Resources