Skip to content

Instantly share code, notes, and snippets.

@Meligy
Created July 24, 2016 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Meligy/a26ed355a8db44b49174a55b9d26d575 to your computer and use it in GitHub Desktop.
Save Meligy/a26ed355a8db44b49174a55b9d26d575 to your computer and use it in GitHub Desktop.
Using jQuery with Angular 2 CLI Webpack Version – With No TypeScript Conflict

I wrote a post yesterday that talked about: [Using Webpack with Angular CLI directly from official Github source][post] [post]: https://www.gurustop.net/blog/2016/07/24/angular2-angularcli-load-cli-from-git-github-master-webpack

The post seemed to get a bit of traction on twitter, and among other feedback, I got an interesting question in a direct message:

Hi Meligy, Have you tried importing jQuery in Angular-CLI webpack branch project anytime??

Well, turns out I didn't. I expected it to be just a require(...) / import ... line away. With Webpack, you often don't need much more than that.

But then when I tried, I found a problem after another, which turned to be very specific to jQuery. It's an edge case, but for the library the community size of jQuery, it's worth showing:

A Working Configuration

First, install jQuery:

npm install jquery

Then install jQuery typings:

typings install dt~jquery --global --save

Adding a test

Then you can modify src/main.ts to include and test jQuery:

  • Add the import:

    import * as jQuery from 'jquery';
  • Change the bootstrap(AppComponent); line to:

    jQuery(() => bootstrap(AppComponent));

Running it

You can go ahead and run ng serve or ng build -prod. I saw what looked like a TypeScript error in the output, so, go run just that:

tsc -p src/

You'll get an error:

src/main.ts(11,12): error TS2345: Argument of type '() => Promise>' is not assignable to parameter of type 'string'.
typings/globals/jquery/index.d.ts(3218,13): error TS2403: Subsequent variable declarations must have the same type.  Variable '$' must be of type 'cssSelectorHelper', but here has type 'JQueryStatic'.

Explaining the problem

This error is because of a conflict between Angular Protractor and jQuery. Angular Protractor defines a global "$" that you use to write your Selenium element selectors nicer, etc. Normally you don't write real jQuery code in a Selenium test, but Protractor is included in the TypeScript definitions that are included in the entire application.

Working around it

A temporary workaround is to manage the typings files for the website (src folder) and End To End tests (e2e folder) separately.

Each of the the folders have a typings.d.ts file that has (possibly among other things), a typings reference to the root level typings folder:

/// <reference path="../typings/index.d.ts" />

The problem is that the typings/index.d.ts file looks like:

/// <reference path="globals/angular-protractor/index.d.ts" />
/// <reference path="globals/jasmine/index.d.ts" />
/// <reference path="globals/jquery/index.d.ts" />
/// <reference path="globals/selenium-webdriver/index.d.ts" />

Now, we can't have both angular-protractor and jQuery in the same file, but we cannot modify typings/index.d.ts directly because every time we run typings install, the file will be overridden (and many even add the typings folder to .gitignore).

What we can do though, is replace the reference to it from src/typings.d.ts and e2e/typings.d.ts. When we do, we need to account for path change, we'll need to prefix the paths with ../typings/ to point to their correct location.

In src/typings.d.ts, we replace the ../typings/index.d.ts reference line with:

/// <reference path="globals/jasmine/index.d.ts" />
/// <reference path="../typings/globals/jquery/index.d.ts" />
/// <reference path="../typings/globals/selenium-webdriver/index.d.ts" />

In e2e/typings.d.ts, we replace the ../typings/index.d.ts reference line with:

/// <reference path="../typings/globals/angular-protractor/index.d.ts" />
/// <reference path="../typings/globals/jasmine/index.d.ts" />
/// <reference path="../typings/globals/selenium-webdriver/index.d.ts" />

And that should be it. No errors n TypeScript compilation, and running ng serve, then opening http://localhost:4200 shows us that the app still works, with the bootstrap() call now executed from jQuery's ready call - which we added only to make sure it works!

Run ng build -prod and check the output in dist. You'll find that it also works well.

Remember, this is only a workaround!

A drawback of what we did is that every time we add typings for a new library using the typings command, we'll have to add a reference to it manually in src/typings.d.ts and/or e2e/typings.d.ts depending on fit. It's easy to forget the manual step and get confused.

Luckily though, this is an edge case. Most of the libraries will not have such conflicts (see, that's why everybody says modules are cool and globals are bad!). For most libraries, all you'll need to do is to require(..) / import ... a module from the library, and everything will happen magically. Thanks to Webpack, you'll not even need to setup vendor or systemjs config etc. [Check my previous post][post] for more information.

Other ideas

A few other starters seem to have the same problem. One other way to tackle it is to exclude the path typings/globals/angular-protractor from the Webpack config, but currently, I can't see this config exposed from the Angular CLI. I'm sure it'll be there once it's final etc., but it's not there now.

Another idea is to have a noConflict typings version of jQuery. There's a pull request to DefinitelyTyped registry to include that, but it's abandoned and closed at the moment. You can try calling the exact file via typings as a github~ file not dt~. But obviously you miss potential updates.

The best thing that can happen is that the Angular CLI would bring the separation of typings for src code and e2e tests built-in, which may or may not land in the CLI. Let's see!

Should you use jQuery with Angular 2 at all?

I think the answer is: avoid it if you can.

I'm currently working with a team that has it included, and it was mostly due to needing some UI widgets that are only available for jQuery. I'm hoping that this is going to change with more UI widgets coming standalone, and easy to wrap in Angular 2 components / directives. Many widgets are becoming available with the Angular 2 wrappers already, like the lovely [ng2-bootstrap][ng2-bootstrap] collection. [ng2-bootstrap]: https://valor-software.com/ng2-bootstrap/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment