I have a directive called "resizable" that adds functionality to an element to make it resizable.
I want to conditionally apply this directive to an element from a controller.
When I add this directive at run-time using attr, the attribute for the "resizable" directive is added, but the directive doesn't execute.
Here's an illustration: http://jsfiddle.net/HB7LU/3995/
What I'm expecting is that when $('span').attr('resizable', ''); is called, the class funky should be added to the span. Why doesn't it?
You need to use the $compile directive to notify angular of the changes. Basically the logic goes like this: "I've changed something within my element that you should be concerned about. Please compile the DOM against my scope":
function Controller($scope, $compile) {
// Apply resizable directive to span
var span = $('span');
span.attr('resizable', '');
$compile(span)($scope);
}
Documentation here
NOTE: You SHOULD NOT use jQuery natively within a controller (... or anywhere really). It may select span tags outside of your element of interest. You SHOULD try writing a directive to wrap the logic that applies your resizable directive.
Related
I'm trying to set a directive to my inputs accordingly.
So let's say I have this input:
<input type="foo" id="myinput" maskvalue> </input>
This works fine on my app, the problem is when I try to insert "maskvalue" dynamically using the "setAttribute" it doesn't work, here's what I'm trying to do right now:
element = document.getElementById('myinput');
if(element){
element.setAttribute('maskvalue', "");
}
This code will successfully insert my directive to the dom element, but will take no effect. It seems that is impossible to insert a custom directive to the dom when it was already loaded, am I wrong?
But that's basically the question, how can I insert a directive dynamically to a dom element?
Thanks
Use Angular's $compile command to Angular-ize this. Basically, you need to notify Angular that you have made a change so it can do its magic.
function MyController($scope, $compile) {
element = document.getElementById('myinput');
if (element) {
element.setAttribute('maskvalue', "");
$compile(element)($scope);
}
}
Make sure you do this in an Angular controller that has $compile and $scope injected into it.
I'm working with angularJS and would like to remove/modify the class of a specific child element (who's class/id is unknown because it is being added to the parent element dynamically).
I understand that using angular you can say something like:
angular.element('someknownParentClass').addClass('newClass');
However, I want to do something similar to:
angular.element('.someknownParentClass').find('i').addClass('newClass');
The class 'someknownParentClass' is a class assigned to an 'a' tag, and inside this tag, I have an 'i' tag with a glyphicon icon class that I would like to change from inside a specific function. It seems like this method isn't working. I know angular's jqLite has a children() attribute, but i'm a little unsure of how to use this or if it would be useful in this case, or maybe using jQuery with angular would be my best option (from what I understand, that's different than jqLite). Any suggestions?
I'm assuming you're doing this in a directive, in which case you can do something like:
var elem = angular.element('.someknownParentClass');
var iElems = elem.children('i');
iElems.addClass("newClass");
If you're more comfortable in jQuery, I don't see a problem in using it in an angular directive instead. According to the docs, angular.element is an alias for jQuery:
https://docs.angularjs.org/api/ng/function/angular.element
$('.someknownParentClass').find('i').addClass("newClass");
You shouldn't need to use angular.element if you are doing things within a directive. If you want to use angular.element in code, you are probably better off using jQuery. I put together a sample jsfiddle that binds a directive to a pre-defined class (some-known-parent-class) and searches for all the "i" elements under it and adds newClass class to it:
var mod = angular.module("myApp", []);
mod.directive("someKnownParentClass", function () {
return {
restrict: "C",
link: function (scope, element, attrs) {
element.find("i").addClass("newClass");
}
}
});
This way, you are decoupling the direct DOM manipulation (i.e. angular.element) through using a directive to do the manipulation. Fiddle here.
I have many repeated content elements in a single view. Within each content element, there's an anchor. When a user mouses over this anchor, I want to toggle a class on a sibling element within that particular content element.
Here's a simple example of what I want to do:
<div class="content-element">
<div ng-class="visibleClass">
I should have class 'visible' when the user mouses over the link within content-element div.
</div>
<a ng-mouseover="" ng-mouseleave="" href="#">Mouseover</a>
</div>
I initially wrote a controller to handle this, but the controller's $scope is tied to the entire view, not a single content-element, so this turned out to not be a graceful solution.
There are many 'content-elements' that are not generated with angular, but are just repeated in the template.
I'm fairly new to angular and trying to wrap my head around this new way of thinking. I can definitely solve this problem easy writing some javascript (capture the event, get the target, get the sibling, etc.) but this doesn't seem like the proper way to do it with angular.
So... what's the appropriate angular way to do this? Should I be writing a custom directive?
Simply create a directive with a new scope and have something like this in the HTML:
<div class="content-item">
<div class="" ng-class="{someClass:hovered}">My transparency should change.</div>
<a ng-mouseover="hovered = true">Mouseover me.</a>
</div>
PLUNKER
Note that if you use ngRepeat, it creates isolate scopes automatically and you don't need the directive.
Directive which founds siblings of the element on mouseover event. You can do what you want with the siblings then:
app.directive('mousiee',function(){
return{
restrict: 'A',
link: function(scope,elem,attrs){
var siblings;
elem.on('mouseover',function(){
siblings = $(elem.parent()).siblings();
console.log(siblings);
});
}
};
});
http://plnkr.co/edit/gWkNpiHMUEUBwuug9C3q?p=preview
(Note that I've added jQuery to your index.html)
I've been playing around a bit with this slick jQuery datpicker and wanted to wrap it up as a directive that I could use inside my angular app. The code for the directive is very simple for right now:
directive('datePicker', function() {
return {
restrict: 'E',
template: '<input type="text" class="topcoat-text-input--large full" placeholder="Select Date">',
link: function (scope, element, attrs) {
element.pickadate();
}
}
As you can see I'm simply targeting the element parameter with the necessary pickadate() jQuery call. The input is properly being targeted as when I click on it I am provided with the datepicker interface and can interact with it no problem. However, when I select a date no information is being populated into the input element. Am I missing something obvious that will allow the date being selected from the control to set the value of the input?
I've done a little bit of debugging and in the link function the element parameter seems to be wrapping the actual input in some way (there seems to be a childNodes array property that holds the <input> tag). Could this be why I'm getting the popup for the picker but the selected value isn't being set as the input's value?
element is the original element in your markup. By default it is not replaced and template is used for innerHtml.
You could use replace:true option in directive or element.find('input').pickadate()
Either of these should resolve visual issue of seeing date. One important thing to note however when you use ng-model and change a value from external code like a jQuery plugin, will need to use the plugin select ( or whatever it is called in pluginAPI) callback to trigger scope.$apply(). This infomrs angular a change was made from code external to angular and to update internals
I suppose you should add replace: true to your directive definition - Angular is targeting the <date-picker> directive element instead of the input.
Instead you can also try this: element.children().first().pickadate();
I wrote an attribute restricted Angular directive (restrict:'a') that adds features to textarea. It makes no sense to apply it to any other type of element.
Adding a if (element.nodeName == 'TEXTAREA') { is really dirty and unreliable.
I tried to add require: textarea to it but it does not work and I get this error: Error: No controller: textarea
Question: Is there any cleaner way to properly apply this restriction?
EDIT: Additional constraint following the first answer.
I want to avoid using a template in this directive as I want to be able to use several directive of this type. Here is an example of what I'd like:
<textarea splittable shared mergeable></textarea>
When using your own directive (eg my-textarea) with restrict: 'E', replace: true, any additional attributes will get carried over to the root-element of the directive, even other attribute directives. So:
<my-textarea splittable class="foobar"></my-textarea>
could be rendered as:
<textarea splittable="" class="foobar"></textarea>
with splittable being executed.
demo: http://jsfiddle.net/LMq3M/
So I think using your own directive is realy the cleanest way to handle this.
In my opinion there is no need to add such controls as they just tend to add complex code when the real issue is human error.
Just document what the purpose and usage is for the directive.
The idea is you give your directive a unique name, like myDirective and you can use it in HTML as such.
<body>
<my-directive></my-directive>
</body>
The directive will replace the tag with the template you provided in the directive controller. Which could be in your case a simple textarea element with added properties.
I recommend watching this video to clearly grasp the concept of directives.
http://www.youtube.com/watch?v=xoIHkM4KpHM