Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack: how to make angular auto-detect jQuery and use it as angular.element instead of jqLite?

I'm using Webpack to build an Angular 1.4 project. The project makes use of several jQuery plugins, which are wrapped into angular directives. Those directives internally use angular.element, probably implying that angular.element is the real jQuery, not jqLite.

I want angular to auto-detect jQuery and use it instead of jqLite. I tried to require jquery locally in my entry point module app.js: require('jquery') and to expose jQuery globally with require(expose?$!expose?jQuery!jquery).

Still, whatever I do, angular.element refers to jqLite.


My research resulted in several findings:

  1. Even when imported as a CommonJS module, Angular assigns itself to a global variable window.angular, so I don't need to expose it with Webpack: Does Angular assign itself to `window.angular` globally, when loaded as CommonJS module?.
  2. ProviderPlugin doesn't seem to do the trick: it doesn't expose jQuery to global namespace; instead, for every module that depends on global name jQuery, it inserts require('jquery') in it. I'm not 100% sure, but looks like Angular doesn't access jQuery from global namespace directly, instead, it tries to access window.jQuery in bindJQuery function, so this approach doesn't help: Expose jQuery to real Window object with Webpack.
  3. For the same reason as ProviderPlugin, imports-loader seems unfit: Angular wants window.jQuery, not just jQuery.
  4. With expose-loader, jquery makes it to the window object. My problem was that Babel hoists all of its imports to the top of module in the resulting code. Hence, although require(expose?jquery!jquery) was before import angular from "angular" in source files, in bundle require("angular") is at the top of the file, before jquery, so by the time Angular is imported, jquery is not yet available. I wonder, how to use Webpack loaders with ECMA6 import syntax.
  5. There was a suggestion to use import syntax instead of require syntax with jquery: import "jquery" or import $ from "jquery", not require(jquery): (Petr Averyanov: How to use Webpack loaders syntax ( imports/exports/expose) with ECMAScript 6 imports?). jquery source code is wrapped with a special wrapper, which idenitifies how jquery is required (with AMD/require, CommonJS or globally with <script> statement). Based on that it sets a special argument noGlobal for jquery fabric and either creates window.jQuery or not, based on the value of noGlobal. As of jquery 2.2.4, upon import "jquery" noGlobal === true and window.jQuery is not created. IIRC, some older versions of jquery didn't recognize import as CommonJS import and added imported jquery to global namespace, which allowed angular to use it.

Details: here's my app.js:

'use strict';  require("expose?$!expose?jQuery!jquery"); require("metisMenu/dist/metisMenu"); require("expose?_!lodash"); require("expose?angular!angular");  import angular from "angular"; import "angular-animate"; import "angular-messages"; import "angular-resource"; import "angular-sanitize"; import "angular-ui-router"; import "bootstrap/dist/css/bootstrap.css"; import "font-awesome/css/font-awesome.css"; import "angular-bootstrap";  require("../assets/styles/style.scss"); require("../assets/fonts/pe-icon-7-stroke/css/pe-icon-7-stroke.css");  // Import all html files to put them in $templateCache // If you need to use lazy loading, you will probably need // to remove these two lines and explicitly require htmls const templates = require.context(__dirname, true, /\.html$/); templates.keys().forEach(templates);  import HomeModule from "home/home.module"; import UniverseDirectives from "../components/directives";  angular.module("Universe", [     "ngAnimate",     "ngMessages",     "ngResource",     "ngSanitize",     "ui.router",     "ui.bootstrap",      HomeModule.name,     UniverseDirectives.name, ]) .config(function($urlRouterProvider, $locationProvider, $stateProvider){     // $urlRouterProvider.otherwise('/');      // $locationProvider.html5Mode(true);      $stateProvider       .state('test', {         url: "/test",         template: "This is a test"       }); }); 
like image 231
Boris Burkov Avatar asked Mar 17 '16 16:03

Boris Burkov


People also ask

How to use jQuery in angular js?

You can import OnInit from Angular Core. import { Component, OnInit} from '@angular/core'; Then, you need to implement ngOnInit Lifecycle Hook. You can write jQuery code inside the method ngOnInit whether DOM is ready or not, as you would in traditional web application development.

What is angular element this scope ()?

The angular. element() Function in AngularJS is used to initialize DOM element or HTML string as an jQuery element. If jQuery is available angular. element can be either used as an alias for the jQuery function or it can be used as a function to wrap the element or string in Angular's jqlite.

What is angular jqLite?

jqLite is a tiny, API-compatible subset of jQuery that allows AngularJS to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most commonly needed functionality with the goal of having a very small footprint. To use jQuery , simply ensure it is loaded before the angular. js file.

Does AngularJS use Webpack?

A complete, yet simple, starter for AngularJS using Webpack. This workflow serves as a starting point for building AngularJS (1. x) applications using Webpack 2.


2 Answers

Got this answer from john-reilly:
The mysterious case of webpack angular and jquery

bob-sponge's answer is not quite right - the Provide plugin is actually doing a text replacement on modules it processes, so we need to provide window.jQuery (which is what angular is looking for) and not just jQuery.

In your webpack.config.js you need to add the following entry to your plugins:

new webpack.ProvidePlugin({     "window.jQuery": "jquery" }), 

This uses the webpack ProvidePlugin and, at the point of webpackification (© 2016 John Reilly) all references in the code to window.jQuery will be replaced with a reference to the webpack module that contains jQuery. So when you look at the bundled file you'll see that the code that checks the window object for jQuery has become this:

jQuery = isUndefined(jqName) ?   __webpack_provided_window_dot_jQuery : // use jQuery (if present)     !jqName ? undefined : // use jqLite     window[jqName]; // use jQuery specified by `ngJq` 

That's right; webpack is providing Angular with jQuery whilst still not placing a jQuery variable onto the window. Neat huh?

like image 55
studds Avatar answered Sep 20 '22 03:09

studds


!!Update

Apparently you still need to use the commonJs require for angular in the ES6 example.

import $ from "jquery"  window.$ = $; window.jQuery = $;  var angular = require("angular"); 

below is the original answer



I want to purpose a easier solution. Just make jQuery a window global so that angular can recognize it:

var $ = require("jquery")  window.$ = $; window.jQuery = $;  var angular = require("angular"); 

or in your case (OUT DATED):

import $ from "jquery"  window.$ = $; window.jQuery = $;  import angular from "angular"; 

I hope this helps :)

like image 39
Lukas Chen Avatar answered Sep 22 '22 03:09

Lukas Chen