web-dev-qa-db-fra.com

Webpack: comment faire angular auto-detect jQuery et l'utiliser comme angular.element au lieu de jqLite?

J'utilise Webpack pour construire un projet Angular 1.4. Le projet utilise plusieurs plugins jQuery, qui sont enveloppés dans des directives angular. Ces directives utilisent en interne angular.element, Impliquant probablement que angular.element est le vrai jQuery, pas jqLite.

Je veux angular pour détecter automatiquement jQuery et l'utiliser à la place de jqLite. J'ai essayé d'exiger jquery localement dans mon module de point d'entrée app.js: require('jquery') et pour exposer jQuery globalement avec require(expose?$!expose?jQuery!jquery).

Quoi que je fasse, angular.element Fait référence à jqLite.


Ma recherche a abouti à plusieurs constats:

  1. Même lorsqu'il est importé en tant que module CommonJS, Angular s'assigne à une variable globale window.angular, Donc je n'ai pas besoin de expose avec Webpack: - Angular s'assigne-t-il à `window.angular` globalement, lorsqu'il est chargé en tant que module CommonJS? .
  2. ProviderPlugin ne semble pas faire l'affaire: il n'expose pas jQuery à l'espace de noms global; à la place, pour chaque module qui dépend du nom global jQuery, il y insère require('jquery'). Je ne suis pas sûr à 100%, mais il semble que Angular n'accède pas directement à jQuery à partir de l'espace de noms global, mais essaie d'accéder à window.jQuery Dans bindJQuery, donc cette approche n'aide pas: Exposez jQuery à un objet Window réel avec Webpack .
  3. Pour la même raison que ProviderPlugin, imports-loader Semble impropre: Angular veut window.jQuery, Pas seulement jQuery.
  4. Avec expose-loader, Jquery arrive à l'objet window. Mon problème était que Babel hisse toutes ses importations en haut du module dans le code résultant. Par conséquent, bien que require(expose?jquery!jquery) était avant import angular from "angular" Dans les fichiers source, dans le bundle require("angular") est en haut du fichier, avant jquery, donc au moment Angular est importé, jquery n'est pas encore disponible. Je me demande comment utiliser les chargeurs Webpack avec la syntaxe d'importation ECMA6.
  5. Il a été suggéré d'utiliser la syntaxe import au lieu de la syntaxe require avec jquery: import "jquery" Ou import $ from "jquery", Pas require(jquery): (Petr Averyanov: Comment utiliser la syntaxe des chargeurs Webpack (importations/exportations/exposition) avec les importations ECMAScript 6? ). le code source de jquery est enveloppé avec un wrapper spécial , qui identifie la façon dont jquery est requis (avec AMD/require, CommonJS ou globalement avec l'instruction <script>). Sur cette base, il définit un argument spécial noGlobal pour le tissu jquery et crée window.jQuery Ou non, en fonction de la valeur de noGlobal. Depuis jquery 2.2.4, sur import "jquery"noGlobal === true Et window.jQuery N'est pas créé. IIRC, certaines anciennes versions de jquery ne reconnaissaient pas import comme import CommonJS et ajoutaient imported jquery à l'espace de noms global, ce qui permettait à angular de l'utiliser).

Détails: voici mon 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"
      });
});
39
Boris Burkov

J'ai obtenu cette réponse de john-reilly :
Le cas mystérieux du webpack angular et jquery

bob-sponge la réponse n'est pas tout à fait juste - le plugin Provider fait en fait un remplacement de texte sur les modules qu'il traite, nous devons donc fournir window.jQuery (qui est ce que angular recherche) et pas seulement jQuery.

Dans votre webpack.config.js vous devez ajouter l'entrée suivante à vos plugins:

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

Cela utilise le webpack ProviderPlugin et, au point de webpackification (© 2016 John Reilly) toutes les références dans le code à window.jQuery sera remplacé par une référence au module webpack qui contient jQuery. Ainsi, lorsque vous regardez le fichier fourni, vous verrez que le code qui vérifie l'objet window pour jQuery est devenu ceci:

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

C'est vrai; webpack fournit Angular avec jQuery tout en pas en plaçant une variable jQuery sur la window. Neat hein?

43
studds

!!Mise à jour

Apparemment, vous devez toujours utiliser les commonJs requis pour angular dans l'exemple ES6.

import $ from "jquery"

window.$ = $;
window.jQuery = $;

var angular = require("angular");

ci-dessous est la réponse originale



Je veux proposer une solution plus simple. Faites simplement de jQuery une fenêtre globale pour que angular puisse le reconnaître:

var $ = require("jquery")

window.$ = $;
window.jQuery = $;

var angular = require("angular");

ou dans votre cas (HORS DATE):

import $ from "jquery"

window.$ = $;
window.jQuery = $;

import angular from "angular";

J'espère que ça aide :)

7
Lukas Chen

Dans votre cas, il vaut mieux utiliser ProviderPlugin . Ajoutez simplement ces lignes à votre configuration de webpack dans la section plugins et jquery sera disponible dans votre application:

    new webpack.ProvidePlugin({
         "$": "jquery",
         "jQuery": "jquery"
    })
3
Bob Sponge

Donc, je donne ma solution, qui est en fait un mashup de la réponse @BobSponge et des astuces/commentaires de @ Bob. Donc, rien d'original, juste montrer ce qui fonctionne pour moi (dans un projet n'utilisant pas Babel/ES6, BTW) et essayer d'expliquer pourquoi cela fonctionne ...

L'astuce (finale) est en effet d'utiliser le exposer le chargeur .

Comme expliqué dans leur page, nous devons mettre module.loaders:

{ test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },

De plus, dans la liste plugins, j'ai:

        new webpack.ProvidePlugin(
        {
            $: 'jquery',
            jQuery: 'jquery',
            _: 'lodash',
            // [...] some other libraries that put themselves in the global scope.
            //angular: 'angular', // No, I prefer to require it everywhere explicitly.
        }),

qui trouve réellement les occurrences globales de ces variables dans le code, et les requiert en variables locales à chaque module. Cela facilite la migration d'un projet existant (de RequireJS vers Webpack) comme je le fais ... Je pense que nous pouvons nous passer de ce plugin si vous préférez être explicite dans vos importations.

Et, surtout (je pense), au point d'entrée de la demande, je les demande dans l'ordre que je veux. Dans mon cas, j'ai créé un bundle fournisseur, c'est donc l'ordre dans ce bundle.

require('jquery');

require('lodash');
// [...]

var angular = require('angular');
// Use the angular variable to declare the app module, etc.

Webpack ajoutera les bibliothèques au bundle correspondant dans l'ordre où il voit les requires (sauf si vous utilisez un plugin/chargeur qui les réorganise). Mais les importations sont isolées (pas de fuite globale), donc Angular n'a pas pu voir la bibliothèque jQuery. D'où la nécessité de expose. (J'ai essayé window.jQuery = require('jquery'); à la place, mais cela n'a pas fonctionné, il est peut-être trop tard.)

2
PhiLho

Il y a cet article japonais je veux utiliser le jQuery pas jQLite dans webpack + AngularJS qui semble parler du même problème (je ne connais pas encore le japonais btw). J'ai utilisé google pour traduire en anglais, les crédits vont à cither pour cette belle réponse.

Il propose quatre façons de résoudre ce problème:

  1. Attribuer directement à la fenêtre (pas vraiment cool)

    window.jQuery = require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  2. Utilisez le expose-loader (ok, mais pas trop cool)

    npm install --saveDev expose-loader
    

    webpack.config.js

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    usage:

    require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  3. Utilisez exposer-chargeur (mieux)

    npm install --saveDev expose-loader
    

    webpack.config.js

        module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/angular\.js$/,
                loader: "imports?jQuery=jquery"
            }, {
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    usage:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  4. Utilisez ProviderPlugin (Meilleure solution)

    C'est en fait la même chose que réponse acceptée de studds ici

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        plugins: [
            new webpack.ProvidePlugin({
                "window.jQuery": "jquery"
            })
        ],
    };
    

    usage:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    

Je pensais partager cela ici car nous avions exactement le même problème. Nous avons utilisé le expose-loader solution dans notre projet avec succès. Je suppose que le ProvidePlugin qui injecte directement jquery dans window est aussi une bonne idée.

2
GabLeRoux