Get rid of jQuery for .css() modification and use AngularJS directly - javascript

I am using an angularJS directive to get full-height elements, and it's working great. The problem is that I load the whole jQuery library just for using it once : on the $(element).
I've been playing around with angular.element or element[0] or event angular.element(element[0]) but I cannot make it work. The element appears undefined or nothing happens at all.
We should be able to select that element right ? Here's my code. Thanks a lot
app.directive('fullHeight', ['$window', function ($window) {
return {
restrict: 'A',
link: function (scope, element) {
scope.initializeWindowSize = function () {
$(element).css('min-height', $window.innerHeight);
};
scope.initializeWindowSize();
angular.element($window).bind('resize', function () {
scope.initializeWindowSize();
});
}
};
}]);

I load the whole jQuery library just for using it once
If jQuery is available, angular.element is an alias for the jQuery function. If jQuery is not available, angular.element delegates to Angular's built-in subset of jQuery, called "jQuery lite" or jqLite.
You don't need to load the library, just use element, as it is the jqLite-wrapped element that this directive matches.
element.css('min-height', $window.innerHeight);
However important note Angular's jqLite
css() - Only retrieves inline-styles, does not call `getComputedStyle(). As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.

You can use element.style.minHeight = $window.innerHeight

element is already wrapped within angular's jqLite. element within the link function in a directive refers to the main wrapper in the template. You can then use element[0].querySelector('selector_here') to get specific elements within.

Related

javascript disabled attribute not working in angularJS?

I have written the following line to add an disable attribute to an element in angular
angular.element(document.getElementsByClassName("pit")).setAttribute('disabled');
but it is not working and throwing an error:
angular.element(...).setAttribute is not a function
angular.element returns a jQuery/jQuery lite wrapped element, not the raw DOM element.
You can set it using jQuery methods:
angular.element(document.getElementsByClassName("pit")).attr('disabled', true);
This is not really the angular way though. Consider adding a service to provide values to ng-disabled, or directive to manage the disabled state.
angular.element returns a jQuery or jqLite Object, both has an attr() function that you can use instead, so:
instead of:
angular.element(document.getElementsByClassName("pit")).setAttribute('disabled');
you should write:
angular.element(".pit").attr('disabled', 'disabled');
Angular has built in jQuery, so you could just add
$(".pit").attr('disabled', true);
angular.element(document.querySelector(".pit")).setAttribute('disabled',true);
This statement will work only if the element is an input type
angular.element(document.getElementsByClassName("pit")).attr('disabled', true);
Or use ng-disabled directive. this is the best way to disable an element in angular.

Adding ngInit to all the elements with specified css class

I have a single page application (angularjs + angular-ui-router) and there are many elements with 'someCssClass'.
I need to add a global handler (at window object), that handles all these elements init event. Is there a way to do this, except manually adding ngInit (or some like this) for each element in each view?
I end up with directive and linkoption.
app.directive('someCssClass', function () {
return {
restrict: 'C',
link: function initHandler(scope, element, attrs){}
};
});
Example on JSFiddle.
Angular directive has restrict parameter, which could be class. As you already have classes on elements — you could use them.
And $emit/$on for events, with what data you need.

Get Elements with AngularJS without referencing the document

I have been developing an advanced web-based GUI in AngularJS. Recently, I decided to use the call document.getElementsByClassName() (I hate using element collecting methods, but here I had to use one) and my boss flipped his lid for accessing the document element. He says that I "need to use only Angular calls for everything", even for element collection! Is there an "Angular way" to collect elements by class name? If so, which way is better to use within the Angular framework? Please provide reasons why. Thanks!
UPDATE: Why I need to use an element collector...
So, I really wish I didn't have to do this, but I do...
I am using a third-party directive that I found online called the Bootstrap DateTimePicker. Its pretty cool and very nice looking, yet it might have a bug...
First, I make a directive bound to an attribute, stating that the element I pass in is meant to be a "DateTimePicker". I then pass that element to the DateTimePicker function.
When invoked, this function creates a new div with absolute positioning and appends it to the body of the page.
Now, I open a dialog in my GUI that has a table in it. On each row of the table, I have two DateTimePickers: one for end-date and one for start-date.
My problem is that, once I leave my screen and the elements which the DateTimePickers were bound to are destroyed, the DateTimePickers still remain! If I open the dialog box again, it creates a ton more of these divs as well!
Until I could determine a true solution to this issue, I decided to use the element collector as a temporary quick-fix. I grab all of the elements with the datetimepicker class and perform a:
elem[i].parentNode.removeChild(elem[i]);
Not having your exact use case but knowing that you are attempting to aggregate elements by class name in your controller makes me agree with you boss. Think of the controller as an object which exposes data and and services to your declarative html page. The data is bound into the markup for presentation and possible modification. THe services are usually wrapped in functions on your controller which are then tied to event handling directives like ng-click or ng-change. These services should operate exclusively on your data and never touch the DOM. If you need to modify a DOM element in your declarative markup then that should be done through directives like ng-class etc.
In any case, It would be useful to know what you are trying to accomplish so as to give you a better idea of the "angular way" to approach the problem.
Well, I have my answer. This does not solve the question "Grab all elements with a certain class name without touching the document element" yet it does solve my problem and eliminates my need to use document.getElementsByClassName.
First of all, it turns out that every element using the DateTimePicker directive have an element.datetimepicker("remove") function.
I use a directive for each DateTimePicker:
components.directive('DateTimePicker', function() {
// Requires bootstrap-datetimestamp.js
return {
restrict: 'E',
replace: true,
scope: {
dateTimeField: '='
},
template:
'<div>' +
'<input type="text" readonly data-date-format="yyyy-mm-ddThh:ii:ssZ" data-date-time required/>'+
'</div>',
link: function(scope, element, attrs, ngModel)
{
var input = element.find('input');
input.
datetimepicker(
{
//stuff
})
.on('changeDate', function(ev)
{
//more stuff
});
...
Directive drastically shortened for the sake of your eyeballs...
I then need to remove the DateTimePicker and the input it is bound to from the DOM on destruction of the dialog box that the input is a child of. To do so, I added this to my directive:
scope.$on("$destroy",function handleDestroyEvent()
{
input.datetimepicker("remove");
input = null;
});
And it works! The DateTimePicker gets removed, the DateTimePicker's handles to the input are cleaned up, and I've marked my input for the GC! WooHoo! Thanks everybody!
If you include jQuery in your project before AngularJS, Angular will use jQuery instead of jqLite for the angular.element function. This means you should be able to use jQuery's selectors for finding / referencing DOM elements.

Finding a specific element tag in angularJS

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.

directive not properly being targeted by jQuery plugin

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();

Categories

Resources