Can I access compiled node data in a custom AngularJS directive? - angularjs

I'm trying to write a directive that checks the value of the content inside it to decide if certain classes should be applied to the element, and to format the content (probably by forcing the content into a filter if the directive is applied).
I'd like the syntax to call the directive to be as follows
<div my-directive>{{foo}}</div>
and output something like <div ng-class='a?b:c'>{{foo | myFilter }}</div>
I know it's possible to do this by using <div my-directive='foo'></div> but I want to know if my preferred way is possible for consistency across the application.
When I try to access element.text() inside the post-link function, I just get {{foo}} instead of the rendered value. Is there a way to access the value of the directive node's content after it's been bound to HTML?

If you'd like to get the value that your {{foo}} expression evaluates to inside your directive, you could use a combination of $eval and the $interpolate service, like so:
var fooValue = scope.$eval($interpolate(elem.text()));
Here's a JSFiddle to demonstrate.

Related

How do I know if directive is enclosed in a container with ng-if = false?

I am developing a custom directive and now I need to handle ng-if. This is easy enough when ng-if is on the directive it self. However when ng-if is on an enclosing container element my compile function fails with run time errors.
Question is: what is the best and most efficient way to know, inside the compile function of a directive, that the directive is enclosed within a container with ng-if is false OR an expression that evaluates to false.
For example:
<div ng-if="false">
<my-directive></my-directive>
</div>
And another example:
<div ng-if="somescopeValue > 300">
<my-directive></my-directive>
</div>
Maybe the answer is using jquery to traverse the dom and find a parent with
an ng-if attribute but I was wondering if there is another more "angular" way of doing this.
Your directive shouldn't rely on knowing whether it is encapsulated in a ng-if expression. That breaks the principle of 'Separation of Concerns' and deteriorates code re-use. Consider redesigning your custom directive in such a way that it needs not be aware of its host elements, in the same fashion that all components (should) work.
If you are using same scope as parent controller or directive then you are able to access same variable in your directive(here my-directive) also. If this is not a case then you need to write code to access parent element in link function of my-directive. You will get directive element in link function as a parameter.
I'm not sure how the error happens, because when your directive is inside ng-if="false", your directive won't get called at all - all compile, link and controller function in the directive will only run as soon as your directive gets added in DOM.
Perhaps your error can be solved by initializing something in case your directive is not added.

How to differentiate when to use angular expression inside any of it's directive?

I am confused about when to use expressions and when not to use inside default AngularJS directives such as ng-src, ng-href and other.
According to my understanding when we use angular directive we have just use scope variable names to bind it's value. Following expression work properly.
<link ng-href="{{BASIC_PATH + '/relative-path-url/image.png'}}"/>
But consider an case of ng-model directive, following example is not valid way to bind variables.
<span ng-model="{{BASIC_PATH}}"></span>
Every time when I have to use angular expressions with directives, I used to write code in both format and then test.
So what is the basic fundamental way to use expressions with angular directives.
Using the {{ }} tells Angular to evaluate the variable and pass its value to the directive.
So if your controller contains the line:
$scope.myVar = "test";
Then this line:
<input ng-model="{{myVar}}">
Would basically be compiled to:
<input ng-model="'test'">
Therefore, the way to remember which convention you should use, is to ask yourself if the directive wants the variable itself, or the value of the variable.

Angular: how to scope a function call to directive instead of controller?

<my-dir ng-show="isVisible()"></my-dir>
isVisible will call isVisible in the controller.
What if I want it to call isVisible inside the my-dir directive instead?
Note: my-dir in my application is a tree control that recursively calls itself using $compile so there may be many of them nested inside each other. Using a singleton service may not work because of asynchronicity.
EDIT: in retrospect I the correct answer was to create a filter for my directive. What can I say, Angular is a different way of doing things.
If you're using a directive you have full control of the element. Just do this: <my-dir check-visible="true"></my-dir>
Then in the directive's link function you can just go: if(attrs.checkVisible) isVisible();
Then you can show or hide the element however you like.

directive uses value comes from ngRepeat in the same element tag

a simple custom directive uses the value coming from ngRepeat, this directive is located on the same element that has the ngRepeat.
Please see plunker
http://plnkr.co/edit/HHDT8q4srsr5ZRYJAhDR?p=preview
the directive works fine, the {{item.locked}} has value you can see them when displaying outside the tag. But the directive doesn't seem to be able to determine what is the value in {{item.locked}}.
When you are using attrs.ciProductLocked it just grabs the string in the html. You want to evaluate a variable so you can do something like this:
// in the directive
if(scope.$eval(attrs.ciProductLocked)){
el.addClass('ui-state-disabled');
}
// in the dom
ci-product-locked="item.locked"
This evaluates the string item.locked in the context of the scope, and will get you the actual value.
Here is the updated code http://plnkr.co/edit/rEslCHqfdy8H82e0o4KJ?p=preview.
Hope this helped!
There are a few things going on with your example that make it a little confusing.
First of all, if you're only looking for a way to add a CSS class to an item based on a boolean, you should use ng-class:
<li ng-class="{ ui-state-disabled: item.locked }" ...>
Secondly, if you're trying to have the value of a scope variable passed into a directive, it's a bit easier to use a new scope and bind the attributes - the angular directive guide has comprehensive info here.
Third, when binding a scope value to an attribute, you'll probably want to use the value uninterpreted:
<li ci-product-locked="item.locked" ...>
This will be bound dynamically if you've declared the ci-product-locked attribute as being passed through to your directive in your linking function.

Two way databinding not working on directive

I have a directive and I want to change the ng-model value given with this directive...
I'm setting scope: {ngModel: '='} and I'm changing the ngModel value (on click event) inside my directive but I can't see changes on my external/original object.
This plunker shows the problem...
There are a few things wrong here, all of them common mistakes.
Event handlers registered through jQuery using $(...).on(...) will be executed outside of angular context, so angular will not know when things have updated. To address this, you must wrap the contents in a scope.$apply call like so
$('#aaa').on('click', function() {
_scope.$apply(function(){
_scope.ngModel = 'Other Value';
updateTemplate();
});
});
This will update the binding to the input with ng-model. In fact you can avoid having to do this by using the ng-click directive.
With angular, you do not need to update templates like this yourself using .html(...). Binding is one of the major features of the framework. Instead of having the update function, you can use interpolation by putting an expression inside of {{ ... }} and your DOM will be updated when your model is. For example when defining the directive you can use
template: '<div id="aaa">{{ngModel}}</div>'
to set your template and {{ngModel}} will show the current value of ngModel.
ngModel is not just any attribute, it is a powerful directive. If you need your own directive to be able to declare the current model valid or invalid, or to interact with forms then you should use this through the require property on your controller (see here).
If you don't need those features then you should be calling your attribute something different to avoid conflict.
I have updated the plunker to include these points.

Resources