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).
Related
EDIT: So it seems to be an issue with selectors? Does jqLite not support selectors or some reduced version of them?
find('input') and find('button') will return results but if I try to filter it with a ":first" or something then it returns no results.
I can't seem to get jqLite's find() to return any child inputs of my div.
I have a $watch on a boolean function that my ng-show uses. So when this div becomes visible I want to apply focus on the div element and then find the first input descendant and focus on that.
example div element that the directive watches:
<div myDirective="function()">
text and stuff
<button>
<another button>
</div>
<div myDirective="function()">
<input>
</div>
this is my helper function in my directive:
function highlightAndFocus(node) {
// focus the div
node.focus();
// get angular's jqlite wrapped element
var task = angular.element(node);
task.addClass('highlight');
// these return empty statements
console.log(task.find('input:first'));
console.log(task.find('button:visible:not("#cancel"):first'));
}
The angular documentation says it finds only by tag name but isn't that what "input" and "button" are?
https://docs.angularjs.org/api/ng/function/angular.element
What's going on here? It seems silly to include jquery just for this one usecase when it seems like it should be supported. I'm printing the var task and I can see the input child elements in the web console.
JQlite is not the same thing as jQuery. If jQuery is available on the page, Angular will use it, but if it isn't, jQlite is used instead.
The docs clearly say that jQlite's .find() only supports lookup by tag names, it doesn't to work with additional selectors (like :first).
You can use the standard DOM APIs instead, namely Element.querySelectorAll(), if you really need it.
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.
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?
So I have a navigation that I want to have a dropdrown menu with sub-menu functionality on it. I tried ng-show for showing and hiding the menus but then I would have to go back and click on item again to hide it. I want to be able to click to show, then click anywhere else to hide it.
trying to use ng-click but not sure where I should put my custom function that I want to retrieve regardless of the controller being used.
Would I put something like this in a directive? The function is not calling html or getting any content like a factory is generally for, it simply shows and hides content on different click events.
There are probably more complicated ways of doing this that might be more elegant, but I've found this to do the trick in the past. Assumes you're loading jQuery before Angular so $ is full jQuery. If you're finding yourself doing this with many different DOM elements though, it might be more effective to break this out into a factory that allows many objects to register themselves in this way with just one binding to body.
app.directive('clickOffHide',function() {
return {
...
link: function(.., elem, ..) {
$('body').click(function(evt) {
var $elem = $(elem);
var $target = $(evt.target);
if($elem.is($target) || $elem.has($target).length > 0) return;
$elem.hide();
});
}
}
});
You could try using ng-blur on the drop-down element.
<div ng-class="dropDown" ng-blur="functionToHideElement" tabindex="100">Products</div>
A div element can accept a blur event if it has a specified tabindex.
This way you can use a baked in Angular directive rather than creating your own.