AngularJS error when wrapping jQuery plugin in a directive - javascript

I'm working on a directive for AngularJS that builds a taggable element and utilizes TagsInput
Here's a working fiddle: http://jsfiddle.net/mgLss/
Not sure why but when I add that directive to my application IT works fine, but anything else below it running angular fails and I get this error message:
Error: node is undefined
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compile/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3746
bootstrap/</<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:932
Scope.$eval#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7808
Scope.$apply#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7888
bootstrap/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:930
invoke#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:2788
bootstrap#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:929
angularInit#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:904
#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:14397
f.Callbacks/o#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
f.Callbacks/p.fireWith#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
.ready#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
B#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
I've spent the last hour on IRC but cant get any acknowledgment of my question so here's hoping Stack will come to the rescue as it has so many times before.

This is something related to the plugin you are using, it manipulates the dom in a way angular does not like it, I didn't to go into the source code the point you to the root of the issue, to be honest. But here is a way (an ugly one) to fix it.
<div ng:controller="TestCtrl">
{{ hello }}
<div><taggable default="changed"></taggable></div>
</div>
​
Just wrap that directive within another DOM element, making sure the plugin is isolated.
http://jsfiddle.net/mgLss/33/

Building on #fastreload's answer, a slightly less ugly solution, which does not require changing your HTML:
// as per #fastreload, wrap input in a div to prevent Angular from getting confused
template: "<div><input type=\"text\"></div>",
link: function(scope, elm, attrs) {
elm.children().tagsInput({ // note children()
You should also include Angular last in your fiddle (under Manage Resources) (and in your code in general), then elm is automatically a wrapped jQuery element, rather than a wrapped Angular element, and hence we don't need to use $(elm) in the link function.

Related

After using $sce.trustAsHtml, ng-click not working

I am trying to print to the screen custom html using angular. I am using $sce.trustAsHtml in combination with ng-bind-html to accomplish this. The goal is not only to be able to print this custom html, but that it will retain directives such as ng-click and they will be usuable. Examples I have seen in articles such as follows are promising:
AngularJS render HTML within double curly brace notation
However in my implementation I find that although the html renders correctly including references to ng-click, the directive doesn't seem to work anymore when trying to click on the link I am using it on; here is some sample code:
$scope.htmlExpression = $sce.trustAsHtml("<a ng-click='test();'>Click Me</a>");
$scope.test = function() {
console.log('Hello World!');
}
<div>
<p ng-bind-html="htmlExpression"></p>
</div>
As everything renders fine and nothing appears lost in translation when analyzing the source; I am left feeling as if I have left something out. Any help is appreciated.
Use https://docs.angularjs.org/api/ngSanitize and bind the html. If this does not work, $digest to reboot the digest cycle.

Using the same directive in a directive [angularjs]

I have a need to use the same directive within a directive, depending on a conditional param. However, when ever i try to do it, it seems to go to an endless loop. As i understand, it tries to pre-load the templates and that causes an endless recursion and at the end, it just throws me the following error:"RangeError: Maximum call stack size exceeded".
I have created an example in fiddle.. as you can see in the example, when the param's value is "1", it creates the error (even when the second level param is valued as "2" so it shouldn't have real recursion issues in the controller/app).
https://jsfiddle.net/qh9nh1gx/
"custom-directive"'s template:
<div>
<div ng-if='info==1'><div custom-directive info='2'></div></div>
<div ng-if='info==2'>DONE,single.</div>
</div>
Thanks
I have found 2 options to deal with the issue, the first one, is exactly what Jju described - creating a new "compiler" method (it can be grabbed from the url he sent).
The second option - always using an additional template for the "recursive" parts of the directive. For example, in my directive, i had a "ng-repeat" part that depending on the items value, it could request to display the directive again. while i used "ng-include" to have the other directive, it worked.
<div ng-repeat="item in items" ng-include="'inline-possibly-recursive-template"'></div>
in that template, you can call the directive again without any issues..
I hope that it will anyone else that will stumble into that issue.
You can look into https://stackoverflow.com/a/19065910/1680674 that describe a common approach to create directive that use himself inside

ngTransclude in AngularJS

I am new to AngularJS, and in learning stage, i have come across ngTransclude, when i start reading the document http://docs.angularjs.org/api/ng/directive/ngTransclude, i could able to get , what that really does,
Can any of you guys, please tell me what actually ngTransclude does, And what is the change when it is included, when when it is not included in directive.
Please give me a clear understanding of ngTransclude, and how important is, and when we can use it.
I Just Want To See It Working:
http://plnkr.co/edit/liRDIEy9sWoSPSHJ1oln?p=preview
Explanation Of The Example
Think of "ngTransclude" as saying "keep whatever is inside of this element, inside of the element, even after I rewrite it". It is most used with directives. Take the following as an example, I defined a directive "emphasize-text" which really just is going to wrap whatever I am provided in an "h1" element. The following is that directive:
app.directive("emphasizeText", function () {
return {
restrict: 'E',
transclude: true,
template: '<h1 ng-transclude></h1>',
}
});
Now, to use this on a page, the following html will work quite well:
<!-- transclude will keep text of course -->
<emphasize-text>Example With Just Text</emphasize-text>
<!-- transclude also keep html tags intact -->
<emphasize-text><i>Example With Italics Tag</i></emphasize-text>
<!-- translcude will even keep angular variables intact! -->
<emphasize-text>
<div>This is a first div</div>
<div>This is a {{secondVariable}} div</div>
</emphasize-text>
If you take a look at this plunker (http://plnkr.co/edit/liRDIEy9sWoSPSHJ1oln?p=preview). Use a modern browser and "view source" on the output and you'll see what is happening. The tags are maintained exactly, angular just wraps them in "h1"! Perfectly what we wanted!
The key here is you can modify the semantics, behavior or really anything you want in html! Personally, I feel this is best suited for custom controls - not for something simple like I used it for. In fact, my example is probably a really bad one because future programmers would know what "h1" is much faster than they will know what "emphasize-text" will turn into. That said, you get the idea.
Happy customizing!

Angular JS - Evaluation Timing

I have the following problem in angularjs. I want to use an UI libary that injects some html code itself (Metro UI CSS) and I have troubles to getting the execution order right.
A simple example: http://metroui.org.ua/hint.html
If I declare in html:
<span data-hint="My hint"></span>
The UIjs will create the html elements needed for the hint display. Nofurther script code has to be added. Well actually when you load the js the following code gets executed: $('[data-hint]').hint();
Since the angular created html doesn't exist when I load the javascript, it doesn't work at first at all.
I believe I need an angular directive to solve the problem (and in parts it does) - I created the fowlling directive:
app.directive('hints', function() {
return {
link: function() {$('[data-hint]').hint()}
};
});
The following does work, even if this is in html created by angular:
<span hints data-hint="the text for my hint">test</span>
The following doesn't work (at least it doesn't behave the way I'd like to):
<span hints data-hint="{{something}}">Test</span>
The hint text will display literally {{something}} and not whatever is behind the angular expression. I tried already to create template like, but the result is still the same:
app.directive('hints', function() {
return {
template: '<span data-hint="{{something}}">Test</span>',
link: function() {$('[data-hint]').hint()}
};
});
Any hints on how to solve that problem would be greatly appreciated.
The main problem seems to be that if you attach the hint() in the link function, jquery takes the old value before angular has evaluated it. One option would be to wrap $timeout(function(){..}) around element.hint(), but I use that hack too much already, and it doesn't solve another problem: the hint needs to update when the $scope changes (if it depends on the $scope). To solve that problem we can add a $watch function and update the hint value when needed.
So, in conclusion:
/* This directive triggers automatically on data-hint as well */
app.directive('hint', function($timeout) {
return {
link: function(scope, element, arguments) {
/* set the value of the hint, then attach the metro-hint widget */
element.data('hint' , arguments.hint).hint();
/* watch for changes in the value so the hint gets updated */
scope.$watch(function(){
return arguments.hint;
}, function() {
element.data('hint' , arguments.hint);
});
}
};
});
(Tested with jquery 1.10.2, jquery-ui 1.10.3 and angular 1.2.6)

Angularjs animate ngClick + ngShow with custom directive (mySlide)

I'm trying to get the same ultimate functionality as ng-click + ng-show, except that I want the show to slide in instead of suddenly appear by toggling display: block/none;. I've got the jQuery animate I need, and I've set up the ng-click. I've got 2 problems, but the second might be a result of the first:
Problem 1
ng-click does not change the value of aside_users. I saw SO#12202067 which seems to be a similar situation, but I don't understand how/why their custom directive works and the native ng-click doesn't.
I see the scope: { … } after restrict: 'A',, but that appears to make $scope values available within the newly-created DOM element (my elements already exist and show up just fine, but no triggers/events are happening).
infobox.html
<aside
class="users"
ng-include src="'views/users.html'"
my-slide={"direction":"left","condition":"aside_users"}
></aside>
<i
class="icon icon-user"
ng-click="aside_users=!aside_users"
ng-init="aside_users=false"
></i>
The above code is a $compile'd template and elsewhere within the template I print out the value of the $scope parameter aside_users (prints false).
Problem 2
my-slide doesn't seem to be initiated/triggered (the logging of 'elm: ', elm doesn't appear in Chrome's console). I verified that directives.js is linked in my index.html page.
EDIT I remembered to link directives.js in index.html, but I forgot to add it to the resources array in app.js.
Plunkr
P.S. I'm not sure if <aside attr={object}> is strictly valid, but legitimate browsers seem to accept it in test cases (didn't bother to check IE). My alternate plan is to use 2 attributes: <foo my-slide="direction" my-condition="boolean"></foo>

Categories

Resources