Attaching jQuery events to angular components when they load - javascript

I'm coding a website using Angular JS 1.5 and making heavy use of the new component directive; So far, it is great. I love the components system.
But I'm having a bit of trouble getting jQuery 3.0 and angular components to talk to each other. Take this code, for instance;
(function($){
$(document).ready(function() {
$('[animate-bounce]').velocity({
translateY: '10px'
}, {
loop: true
}).velocity('reverse');
});
})(jQuery);
This works fine on a normal page, but because of the nature of how component works, it doesn't apply to components that are instantiated after $(document).ready. That makes sense, of course. They don't exist, so it can't wire up to them.
I know about the component lifecycle; Specifically, the $onInit function. I know that I can hook the jQuery into that. But I'm wanting certain jQuery code to apply to any component that gets activated, and I wanted to try and keep the activation of it out of the component code.
Is this possible to do? Is there a 'global' $onInit for component that will let it run jQuery bindings when any component loads? Or is there a replacement for the old live() binding in jQuery that will trigger when new DOM is added?

Ciel, your are attempting jQuery in Angular app where entire angular life cycle does not allow jQuery to interfere. Though it is not impossible to achieve but I will recommend to not use it due to following reasons.
jQuery events and data bindings will not handled by Angular so forget about events and data removal.
Handling JQ events extensively will kill the purpose of using Angular as framework.
-Workaround -
Try using Angular component as much as possible, you may give a hand using link and compile functions while creating an Angular component.
Hope this will hep

Related

Trigger jQuery function after page has loaded in AngularJS

I just started learning AngularJS and I'm not really familiar with the terminologies in AngularJS yet. Here's my concern, I created a Loading Animation that shows up when I go to pages, but then I want to stop the Loading Animation as soon as all the contents of the pages has loaded. It's fairly easy to do in plain jQuery since I can just trigger $(window).load() but it doesn't seem to work in angular. $(document).ready() seems to work but that's not what I actually need since it gets triggered even though the images are not finished loading yet. I already tried $scope.init inside my controller as well as $window.onload inside my controller but I still can't make it work.
Hotfix answer
Actually you do not need to wait for any of these events in angular.
Just use $().. in your angular controller - the site was already loaded. (similar to the window.load() event. In case jquery does not find the html elements please try to wrap it with $timeout( function() { ... } );
Recommendation
Please do not! There are angular ways to animate stuff which fits better than crazy $('#id'). logic. This will break in growing applications.
I would recommend you forget about jQuery when you are working on AngularJS application. So figure out how to work everything out in AngularJS way - move all jQuery logic that you have to angular controller and avoid direct DOM manipulation (jQuery way).
Also, there's a good tutorial online if you are moving to AngularJS from jQuery:
https://gabrieleromanato.name/introduction-to-angularjs-for-jquery-developers
If you need a solution for switching pages go with ui-router - it will give you even more flexibility with loading pages and resolving properties for different pages:
https://github.com/angular-ui/ui-router
And in this particular case you can use simple boolean property on $scope and show preloader div based on that:
<div ng-show="showOverlay" class="loader" />
then inside your controller you could put something like:
$scope.showOverlay = true;
and then after your page logic is loaded and promises are resolved you could just hide that preloader with:
$scope.showOverlay = false;
To illustrate I created simple fiddle with preloader for you. Also keep in mind there are tons of different ways of implementing this but this is simple one that should work for almost any case:
https://jsfiddle.net/pegla/ng1mn8qp/3/
There's also one answer here on stack overflow that could help you:
How to execute angular controller function on page load?

UX effects in Angular 2

I wan't to add some interactions to my Angular 2 project to enhance its user experience. I know how to interact with DOM, or change the status of element property. It is possible to write code for each of my component. But there are some examples which will be used site wide, for which I don't want to repeat the code everywhere I want to use it.
A simple example will the fade in when scroll elements. I know how to achieve this in a particular controller, but I need help to make this behaviour global without code repetition.
I Javascript / jQuery, we can have a master js file included which will have the event listeners bound to the elements, which is available for all pages. How to achieve similar in Angular?
This can mostly be done with Directives. Taking your example, you would create a [scroll-fade] Directive:
#Directive({
selector: '[scroll-fade]'
})
export class ScrollFade {
}
You'd then need to listen for the global scroll event, with #HostListener('window:scroll') and apply your styles to the :host element.
You would then use it by applying it to the elements you want affected:
<div class="scroller" scroll-fade></div>
If you need something more complex, you could always build a Shared Module where you would create reusable components, without repeating the code - which you could then transform into a library and share back with the community.
Theres an answer here on StackOverflow that explains how to.

jQuery document ready with Knockout.js

I just got thrown into the Umbraco ASP.NET CMS for my latest project, I'm not sure if this is how it across the board but for my setup Knockout.js is doing all the templating.
I'm not too keen on knockout.js but so far it's been pretty straight forward except for when I start adding in some jQuery stuff, the problem I'm having is jQuery is firing before knockout has finished populating the page with all the elements.
The only solution that's worked for me thus far is all my jQuery stuff is wrapped in the setTimeout() function, which obviously is no good.
What's the most efficient way to make jQuery and Knockout work together so jQuery doesn't before knockout is done?
I recently had the same issue with the jSignature plugin and my Knockout view. I needed the KO view to have fully rendered before I invoked jSignature, otherwise it didn't size itself correctly.
I fixed it with a template binding and an afterRender callback function to invoke the jQuery work.
Here's the KO docs:
http://knockoutjs.com/documentation/template-binding.html
Here's a quick jsfiddle showing how you can use it:
http://jsfiddle.net/PCbFZ/
The trick is that you can use the afterRender callback of the template binding without actually using a template itself. Instead, you wrap all your existing HTML in a div that will invoke the afterRender callback:
<div data-bind="template: {afterRender: initApp}">
<!-- your existing HTML here -->
</div>
initApp is the function that does the jQuery work.
I think that should generally do what you need, though if your HTML is particularly complex, or you have many views you need to render inside the one page, you might need to do a bit more work. Let me know how you get on - maybe I can try to help a bit more if this doesn't quite fix your issue as easily as it did mine!
Update - following the comment from JACL below - here's an extended version of the fiddle showing this technique also working with ko-if. Each time you show/hide the 'widget' using the checkbox, a different random colour is applied to indicate the afterRender function doing its work.
http://jsfiddle.net/PCbFZ/15/
You might use:
$(window).load(function(){ /* code */ }); instead of $(document).ready();
Perhaps window.load instead of document.ready will do the trick

jQuery, jQueryUI (and plugins) and AngularJS - how do they all fit together?

I have this website (in Hebrew): http://www.iping.co.il (if you could have a look at it maybe with google translate and see what it does it could be great but not a must).
It basically a website that shows your IP, and gives you a set of tools (like ping, whois check, open port checks...).
I've built it a while back and I was using jQuery and jQuery UI to do all the work (like opening dialogs, call the server, change the DOM, show a progress bar...).
Now I'm working on rebuilding it - I'm rebuilding using ASP.NET MVC 5, HTML5 and Bootstrap3. I figured it's a great little website to test new things I've been reading about lately. And one of those things I would like to try and implement (after reading much about) is AngularJS.
As far as I know, AngularJS is not meant to change the DOM directly, but use directives and 2 way bindings to do so.
I have a lot of code, and plugins that I use that uses jQuery and jQuery UI to (for example the dialogs, the progress bar and so on... things that I haven't figured out how to do with AngularJS). It seems that if I use the jQueryUI progress bar and update it from from AngularJS that I'm breaking some rules here and that it's probably dirty and not the way it should be written.
So my question is, what is the correct way to work when and build a rich UI when using AngularJS? is jQuery and jQueryUI even still relevant? if so, is there a correct way to use them (maybe DI somehow?)?
I've searched and found something called AngularJS UI - but it's not as rich as jQueryUI.
Thank you
Using plugins within directives is fairly simple in concept.
<div my-directive></div>
Following is a very minimialistic directive with just enough code to initialize a plugin. The returned function is equivalent to link in a more defined directive
angular.module('myApp').directive('myDirective',function(/* dependencies*/){
/* element is a jQuery object when jQuery is included in page before angular.js*/
return function(scope,element,attrs){
/* can use attributes or scope to pass options to plugin if needed*/
element.someJqueryPlugin();
}
});
This would be equivalent to writing in jQuery only:
$(function(){
$('[my-directive]').someJqueryPlugin();
});
If you want to use AngularJS and Bootstrap I suggest you take a look at these directives:
http://angular-ui.github.io/bootstrap/
Once you load the modules, you can set up say a progress bar this way:
<progressbar max="max" value="dynamic">
<span style="color:black; white-space:nowrap;">{{dynamic}} / {{max}}</span>
</progressbar>
You shouldn't even need to include JQuery if you only need the Bootstrap components.

attaching behavior to elements created by knockoutjs

Take, for example, the situation using knockoutjs:
<div id="container" data-bind="foreach:containers">
<li class="complex-element">...</li>
</div>
Now I want to attach some complex behavior to complex-element. How do I do that? I can't just attach events directly to complex-element, as they're created and removed dynamically at runtime to match the view model. So, as I see it:
I can riddle my html with data-bind="click:..."s and data-bind="mouseenter:..."s but I would rather avoid this if I could. Maybe I'm too rooted in my old MVC ways, but adding open(), select(), or dragStart() functions and isOpen, isSelected or isDragging observable flags to my view model just makes a mess and my intuition tells me that as the app gets bigger that view model is going to become unmanageable. I'd rather keep my data and my presentation separate if possible.
Or I can use jquery delegation to attach events to something that stays fixed. Something like:
$("#container").on('click', '.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
But this wouldn't work as the app gets more complex because what happens if the container is itself wrapped in an element only shown on login (<div id="wrapper" data-bind="if:isLoggedIn">...</div>). I might as well just bind all events to the body and that's a recipe for disaster.
I found a very cool article on knockmeout.net (http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html) that advocates using custom knockout bindings to drive complex behavior, and this seems to be an awesome solution for widget-like behaviors like autocompletes or date-pickers, but what about just simple old fashioned controllers... would this work?
I guess, after all that, my question is pretty simple: Has anyone used Knockoutjs on a really large web application? And how did you go about it?
You could use a different jquery bind:
$(document).on('click', '#container>.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
This would work if #container does not exist.
There is some work going on in the community to make the knockout bindings unobtrusive and easy to write. For now, knockout offers you dataFor() which can be seen here: Using unobtrusive event handlers or you can use a little library like this: Introducing the Knockout.Unobtrusive Plugin
If you need to execute some arbitrary js on some rendered elements from the foreach you could use the afterAdd callback
<div id="container" data-bind="foreach: { data: containers,
afterAdd: somefunction }">
<li class="complex-element">...</li>
</div>
I have used KO on a really large web app and the way I went about managing complexity was
Split out viewmodels into separate using namespaces within a single master namespace.
Write custom bindings for complex reusable behavior.
Created entity model classes that encapsulated most of the important business logic, akin to backbone models.
Ensured that the viewModels were entirely view specific and view related (helped by point 3)
Kept all jquery selectors to a bare minimum, I feel dirty writing any global delegates but sometimes they are needed.
Hope this helps.

Categories