An approach to use jQuery Plugins with AngularJS


Lets face it, we can not completely get rid of jQuery and its plugins ecosystem, even though Angular has a built-in subset of jQuery under the name jQLite. At one point or another, we often need some sort of jQuery plugins in our application and we can/should not port entire plugin into Angular world in order to use it but we can avoid the plugin initialization code to be scattered across.

How?

Simply by writing a directive for it.

I would like to give you a small demo of Toolbar.js which is a jquery plugin I found recently on geekli.st. This is how we create a tooltip style toolbar in jQuery:

<!-- Click this to see a toolbar -->
<div id="format-toolbar" class="settings-button">
    <img src="http://paulkinzett.github.com/toolbar/img/icon-cog-small.png">
</div>

<!-- Our tooltip style toolbar -->
<div id="format-toolbar-options">
	<a href="#"><i class="icon-align-left"></i></a>
	<a href="#"><i class="icon-align-center"></i></a>
	<a href="#"><i class="icon-align-right"></i></a>
</div>
<!-- Typical jQuery plugin invocation -->
$('#format-toolbar').toolbar({
	content: '#format-toolbar-options', 
	position: 'left'
});

Enter the dragon a.k.a Angular

We’ll keep our markup intact by just adding a custom attribute named `toolbar-tip` – which will be an angular directive we’ll going to write soon. So our markup will change to:

<div id="format-toolbar1" class="settings-button" toolbar-tip="{content: '#format-toolbar-options', position: 'top'}">
	<img src="http://paulkinzett.github.com/toolbar/img/icon-cog-small.png">
</div>

One thing to notice here is that I’ve moved all the options of a toolbar into an HTML so that we can use the same directiv anywhere else with different options/settings.

Finally,

<script>
var App = angular.module('Toolbar', []);

App.directive('toolbarTip', function() {
	return {
		// Restrict it to be an attribute in this case
		restrict: 'A',
		// responsible for registering DOM listeners as well as updating the DOM
		link: function(scope, element, attrs) {
		    $(element).toolbar(scope.$eval(attrs.toolbarTip));
		}
	};
});
</script>

Woohoo! Is not that awesome?? Everything is simple, maintainable and testable!!!

Demo

http://jsfiddle.net/codef0rmer/TH87t/

Advertisements

74 thoughts on “An approach to use jQuery Plugins with AngularJS

  1. This is exactly what I’m looking for! Coz I have a navbar that I want to initialize with Twitter Bootstrap’s jquery affix plugin and I have no idea where to enter the initialization code. Initially I thought I’d have to mess with angular config run function.

    This is great stuff.

  2. I’ve been using an identical approach myself. I prefer to add the braces for the options in the directive though -> scope.$eval(“{” + attrs.toolbarTip + “}”). The HTML looks a bit tidier that way.

    Also, if you’re already referencing jQuery in your solution, make sure you’re loading it before AngularJS. This way Angular will pick up that jQuery is present. So now instead of “$(element).toolbar(scope.$eval(attrs.toolbarTip));” you can write “element.toolbar(scope.$eval(attrs.toolbarTip));” as element will already be a jQuery object.

    Also, your JSFiddle is currently broken. Here is a working one:
    http://jsfiddle.net/gavinfoley/TH87t/57/

  3. This is an interesting example. I did not previously realize that the camel case directive name (in this case toolbarTip, is mapped to the dashed attribute toolbar-tip in the HTML. Took me a while to figure out that was happening. Kind of makes sense but not very obvious.

    • Yeah that was intentionally done by Angular Team because in javascript we can not use hyphenated function name whereas in html we can use camelcase tag/attribute name but is not valid html.

      • Sorry again, but for me it’s extremely surprising that the author of a book about Angular JS, writes something so wrong like that. “was intentionally done by Angular Team because in javascript we can not use hyphenated function name”. It is wrong and to test it is sufficient to open the browser console and try. JavaScript does support function names with hyphens and even spaces, they just need to be called by name! o[“some_crazy Name”] = function () { console.log(“Hello”); }. I know already that most probably my comments won’t pass moderation. Kind Regards, Roberto

      • Hi Roberto, thanks for your comments. Your assumption is correct that we can use hyphenated name for functions but only being object properties. That means you can not define function foo-bar() {} in JavaScript. What I meant by those statements in the post is that angular expects directive definition as a constructor which supposed to be newed.

  4. App.directive(‘toolbarTip’

    and

    <div toolbar-tip…

    Should not it be the exactly same string?

    I am a newbie on angular, i need to understand everything.

    Thanks

    • No because thats how angular works! As per HTML spec, the attribute should be hyphenated but javascript does not support hyphenated variable/function name and hence angular team has decided to use hyphens while declaring and camelcase while defining a directive.

    • Hi, thanks for the reply and not taking bad my comments. But still, when you define in an HTML template for Angular to call a given function, that function must be defined in the scope. Nothing would disallow to define a function in the scope “with-hyphens” in its name. It is Angular JS that doesn’t support this – and not JavaScript -, because during its expression parsing, it tries to perform a mathematical operation when finding a “-” sign. Angular makes the assumption that if it finds a “-“, then some maths is involved. I just verified this by debugging inside Angular.

  5. How would you handle the toolbar item´s click events? Seems that the plugin replace/move the html whenever its called so you cannot use ngcllicks on em.

    • @insane, It’s very rare that you need angular event handlers in jQuery plugins. In this particular example, the toolbar’s click event is handled by the jQuery plugin itself – angular directive just creating a wrapper around it to help us use it declaratively in angular.

  6. Hi,
    I have some problem with angularJs + Jquery Steps Plugin.
    The problem is after implementing Jquery steps as directive , ng-model seems to be not working. Can any one tell me what I am missing? Do I need to do some extra stuff to make data binding work again??

  7. Hi , thanks for the article. I am not able to figure out how to create a directive to initialize a jQuery iviewer – the jQuery code is as follows

    $(“#viewer2”).iviewer(
    {
    src: “test_image2.jpg”,
    zoom_min:’100′,
    zoom_max:’400′
    });

    and i have just made a angular skeleton but don’t know what to do next

    var vm = angular.module(‘vModule’,[]);
    vm.directive(‘vModuleConfig’,function(){
    return {
    restrict:’A’,
    link:function(scope,element,attrs){
    }

    }
    });

    Can you please help. Thanks

    • Hi Gaurav,

      I’m not sure about your requirement but by looking at your code, you are almost there. Just move $('#viewer2')... code into the link method, replace $('#viewer2') with element, and apply the directive on <div id="viewer2" v-module-config></div>

  8. Hello, I am trying to integrate a third party Jquery plugin into our Angular JS UI. The third party API is here https://github.com/smartystreets/jquery.liveaddress . Can you please give me some ideas as to how I can integrate this?

    Our Angular JS UI has multiple controllers associated with multiple views on a one-one relationship.

    I tried integrating the above JQuery plugin into our codebase, by calling the plugin, from the controllers and even though it works functionally, that is autolookup and verification work great for a particular field, but the data doesn’t persist and verification icon behaves erratically on the various views! Any help is appreciated. Thank you!

  9. I tried integrating the Jquery API plugin as per instructions given in the documentation of the API. I am calling

    var ss = jQuery.LiveAddress({
    key: “19756771859510015”,
    waitForStreet: true,
    debug: true,
    target: “US”,
    autoVerify:”true”,
    addresses: [{
    id:”myID”,
    freeform: ‘#headdressname’
    }]
    });

    in each of the controllers that have the address model mapped into them. and I see that is works erratically. Nope no demo out there unfortunately I have the code on my local machine only

  10. Thank you for sharing excellent informations. Your web site is very cool. I am impressed by the details that you have on this web site. It reveals how nicely you understand this subject. Bookmarked this website page, will come back for extra articles. You, my pal, ROCK! I found simply the info I already searched everywhere and simply could not come across. What a perfect website.

  11. Hello, I am trying to integrate a third party Jquery plugin into my Angular JS UI. The third party API is here https://github.com/jquery-textfill/jquery-textfill . Can you please give me some ideas as to how I can integrate this? I tried to do the same approach that you used to include the toolbar plugin in your code angularjs but it didn’t work for me.
    Thanks in advance for your help

  12. Hello codefOrmer,

    I first want to thank you for this. it realled helped me out. I have two questions for you.

    1. First one: how did you know about this? all the tutorials i have watched or read for angularjs do not mention it. and i do not think it is on the official angularjs website. Have you read it in a book or did you just came up with this by knowing how angular works.

    2. i got the directive to work. however the plugin has a function that does not seem to work.
    i have a plugin for a calendar. the calendar now shows up with the directive however, the calendar has a function that is supposed to return that i cannot seem to have it worked as well. i created a second directive for that as well.

    you can see what i did here. Your help will be appreciated.
    https://plnkr.co/edit/mJhTodl1DPA61VZvhrjB?p=preview

    Thanks again.

    • The original plugin is called as
      $(‘#datetimepicker1′).datetimepicker({
      defaultDate: “11/1/2017″
      });
      and i made the atttribute as cal-directive=”{defaultDate: ’12/12/2017’}”

      but it has another function to get the date of the calendar and that function in jquery is:
      $(‘#datetimepicker1’).data(“DateTimePicker”).date()._d;

      and i made the directive attribute as cal-directive-data=”DateTimePicker” but i dont think it works. i might be missing some info.

      • Thanks for the compliment. Well, I came up with it but that’s also a pattern I’d seen throughout many angular plugins I had used and explored.

        I think you only this type of directive cal-directive=”{defaultDate: ’12/12/2017’}” and can give provision to set the date in data-attribute as well, which you can read in your directive’s link function this way $(element).data(“DateTimePicker”).date()._d;

        Can you create minimal plunk example for me to understand the issue better?

  13. Hello CodeF0rmer,
    Thank you for taking the time to get back to me.

    I had another question for you. You might have been coding for a while. Nowadays what is your stack and what frameworks do you usually go back to often?

    Regarding the jquery/angularjs, i have simplified the plunk.
    https://plnkr.co/edit/1dWW72XK3VyeY0o53j77?p=preview

    the plugin is called with jquery as $(‘#datetimepicker1’).datetimepicker({defaultDate: “11/1/2017”});
    and i have made the directive accordingly to your direction. and it works. the date shows in the input field. Before nothing was showing.

    What i want to do next is be able to get the time chosen and log it somewhere else. I tried ng-value and ng-model on the input field where the date and time is shown but it is like the input cannot see the value shown by the calendar unless i type in it.

    The jquery plugin however comes with it is own function to get the time chosen $(‘#datetimepicker2’).data(“DateTimePicker”).date()._d;

    and this is the one i am not sure if it can be transformed in a directive with angular. As what i have tried so far is not working.

    so i am wondering.
    1. can $(‘#datetimepicker2’).data(“DateTimePicker”).date()._d; be transformed as well as a directive to be able to get the time chosen.

    or

    2. is there another way to use jquery with angularjs that would help in this case.

    or

    3. is there a way with angularjs to get the data shown in the input field so it can be stored in a variable.

    I thank you for your time.

    Jo

  14. Hello CodeF0rmer,

    Thank you very much for taking the time to help out. I can see that directive are really powerful. but i also learned of the limitation of jquery and angularjs. i think in the future i will look very hard for a angular plugin first before using a jquery plugins.

    Last very question. any resource on angularjs that you would share or a front end framework that you think goes a long way.

    Thanks again.

    Blessings.

    • It’s your choice – can not say about future though. But for lightweight things, try VueJS or PreactJS. For big projects, try GlimmerJS or ReactJS or Angular (though I like the angular community more).

      • Thank you CodeF0rmer… i have seen quite about about Vue and React around the internet. will dfntly delve into them.

        Thanks again. have a good one.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s