I got the following problem: My customer wants my angular directive to change the looks completely on focus. That means for me, I will need to replace the template completely when the user clicks that element. I was not able to find an answer in internet, so decided to ask.
I tried to pass the "unfocused" template in the template section of the directive, and bind to the focus event in link like this:
post: function (scope, element, attrs) {
element.bind('focus',function(event){
element.html('<span..../span>');
$compile(element)(scope);
});
}
But it produces an error (input is the "unfocused" template)
Uncaught Error: [$compile:multidir] Multiple directives
[ngModel, ngModel] asking for 'ngModel' controller on: <input....
The solution needs to be compatible with Angular 1.2 which I have to use.
ng-if will not render the element at all if the condition is false.
In your comment you have the input nested in the span, so the span is the root element. You can have both, the span and the input as top level elements in your template.
<input ng-if="focusCondition"/>
<span ng-if="!focusCondition"></span>
Then the element that is rendered should be the root element.
You can use ng-switch directive and display only those templates which have to be shown on focus.
Related
I'm attempting to find the parent tr element when I use clicks a button on a datepicker calendar. Since I don't want to use jQuery in the form of a script tag (edit) in my Angular app, and this isn't possible using strictly CSS, I created the directive below. The elm.find is able to find and alter the css of the button correctly, so I know that I've found the element I'm looking for, however now I need to travel up the DOM.
I'm used to jQuery syntax, which doesn't work, and I haven't been able to find anything effective on the interwebs. Any chance someone could help me out with the syntax?
/* Linker for the directive */
var linker = function (scope, elm, attrs) {
elm.on('click', function() {
elm.find('table tbody tr button.active').parent('td').css('background-color', 'red');
});
};
EDIT
This is a directive that needs to be placed on a uib-datepicker element (Angular UI Bootstrap) in order to change alter the background-color for an entire row. The framework doesn't come with this functionality and the HTML isn't generated until the page loads.
I need to attach the directive to the element below, find the selected item and then work back up the DOM to find the parent tr.
<uib-datepicker highlightselectedrow class="well well-sm" ></uib-datepicker>
.parent will look exact the upper element only. I'd say that rather use .closest so it will search in parent till it gets td
elm.find('table tbody tr button.active')
.closest('td').css('background-color', 'red');
What about taking a more angular approach than psuedo jQuery? This is based on the ngStyle Angular doc:
<div ng-style="myStyle">Test</div>
<div ng-click="myStyle={'background-color':'red'}">Click Me</div>
You would then place the click event on whatever element you want (td). The ng-style can be moved to what you want affected (tr).
I've seen a javascript solution that goes a little something like this:
var select = document.getElementById('selectId')
select.click();
Is there an AngularJS approach/best practice to the same thing? (Off the top of my head, you'd wrap the above code in an ng-click)
Yes there is. Here's the angular equivalent of what you have in JavaScript
angular.element('#selectId').trigger('click');
Working example
Any DOM manipulation in angular should occur inside of a directive.
View
<div id="selectId" clickMe>content</div>
Inside of a directive the link function triggers after the view is compiled. The second parameter in the link function is the element which the directive is placed on, this gives performance benefits since there is no need to traverse the dom. It is a JQlite element which you can directly call methods on.
Directive
app.directive('click-me', function(){
return{
link(scope, el, attr){
$(el).trigger('click');
}
}
});
so here is the desire: I need to add a directive to any html element and pass it an id. This id is then used to populate a method that submits a form that has that id on the page.
Some devs have used a button some have used an anchor tag so I do not want the type of element to be part of the method. It is intended to be highly re-usable.
I already have it working like this:
angular.module("App").directive("submitform", submitFormDirective);
function submitFormDirective(){
return {
restrict: 'A',
scope: true,
link: function($scope, $element, $attrs){
var selector = $attrs.submitform;
$element.on('click', function(){
angular.element(selector).submit();
});
}
}
}
But this uses jquery for the "on" method. I want a fully angular method. Or one that does not "fight" with the framework or use jquery.
The way I am doing it now I only need to apply this attribute to the element. submit-form="#id-of-form" And I would like to keep that functionality becasue it is clean and concise. So I need to be able to add the ng-click to the element dynamically without using the limited template function which would limit the element being effected, inside the submitFormDirective.
The problem is that the $attrs.$set('ngClick', 'submitForm()') runs after the digest and $compile does not seem to work for me. I need to be able to add the click and run the compile on only this element or use a different phase that will happen before the app compile will run. But I have tried using compile to att the attr in conjunction with link adding the method to the scope to no avail. Seems strange that you would not be able to add a event from a directive. I must be missing something. Can someone help?
Background:
I am using a custom CMS where I have limited access to the code base. So, in a few cases I plan to make some DOM manipulations using JavaScript.
Problem:
I have a container directive and the container has plain old HTML items, but am not able to mark the items as being directives from the server side. Also, the plain old HTML items contain sub-content that are directives.
Example:
Here is the before:
DIV[container-directive]
DIV.some-item-in-html
DIV[some-directive-in-the-content]
DIV.some-item-in-html
DIV[some-directive-in-the-content]
...
Here is what the DOM should look like afterwards:
DIV[container-directive]
DIV[container-item] <-- This is what needs to be inserted
DIV.some-item-in-html
DIV[some-directive-in-the-content]
DIV[container-item] <-- This is what needs to be inserted
DIV.some-item-in-html
DIV[some-directive-in-the-content]
...
Question:
Does anyone have suggestions on the best approach to injecting DOM element that are directives in-between a nesting of directives using JavaScript?
Some thoughts:
I think manipulate the DOM in advance of the compilation by angular, but I wonder if there is a way to do this within Angular's framework.
Another option is from the container directive's post-linking function, I could wrap the HTML items in directive elements called "container-item" and then $compile the items manually. So, I tried this but I get an error related to the items already having directives inside with transcluded content. Something about the "ngTransclude" being unexpected. I think this is related to the inner directives already having been processed.
I would go with your first option and manipulate the DOM ahead of angular compilation.
You can do this within a directive that contains the elements that you want to manipulate.
For example:
app.directive('body', function() {
return {
restrict: 'E',
compile: function(element, attr) {
// find the inner element and wrap it
$('.some-item-in-html', element).wrap('<div class="container"></div>');
}
}
});
Parent directives are always compiled before child directives, so you can change the DOM of the children within the compile property, and not have to worry about recompiling or re-linking directives.
[EDIT]
Thanks to Biagio for the following edit.
This method shouldn't be used with a directive with a template because the element would be assigned to the template and not the child elements.
Another alternative is doing the DOM manipulation in a function that runs at the start of the angular lifecycle.
For example:
app.run(function(){
// find the inner element and wrap it
$('.some-item-in-html').wrap('<div class="container"></div>');
});
For example, UI Bootstrap has a directive called 'typeahead' that suggests values for a field. Let's say I want to make a directive that I can use as an attribute for an element that will cause colours to be suggested for that element.
Here's an attempt that fails...
Directive:
angular.module('myApp')
.directive('suggestcolors', function () {
return {
compile: function compile(element, attrs) {
attrs.$set("typeahead", "color for color in ['red', 'blue', 'green']");
}
};
});
View:
<textarea ng-model="foo" suggestcolors></textarea>
When I inspect the textarea, the attribute has been set, but it doesn't do anything. The same thing happens if I move the change to attrs to the link function. Setting the typeahead attribute directly in the view works as expected:
<textarea ng-model="foo" typeahead="color for color in ['red', 'blue', 'green']"></textarea>
(But I want to be able to insert this attribute dynamically, for reasons of DRY. My actual use case is more complicated than this.)
A similar question was asked here (about dynamically adding ng-click behaviour in the compile step), but never directly answered.
After the compilation, AngularJS only calls $compile for all of the child elements. The element itself is not automatically recompiled, so adding a directive at this stage will not have effect. In your case, I think you can change it from a compile function to a link function (so you get a scope argument), and call $compile(element)(scope) yourself.
See this Fiddle where I have a directive that adds style="color: red", and another directive that "dynamically" adds that directive. It doesn't work, unless I call $compile afterwards.
HTH!