web-dev-qa-db-fra.com

Shim Twitter Bootstrap pour RequireJS

Les RequireJS docs disent que pour prendre en charge les anciennes versions d'IE, vous devez configurer enforceDefine: true.

Donc, si vous voulez prendre en charge Internet Explorer, détecter les erreurs de chargement et avoir du code modulaire via des appels directs define () ou une configuration shim, définissez toujours enforceDefine sur true. Voir la section suivante pour un exemple.

REMARQUE: si vous définissez enforceDefine: true et que vous utilisez data-main = "" pour charger votre module JS principal, ce module JS principal doit appeler define () au lieu de require () pour charger le code dont il a besoin. Le module JS principal peut toujours appeler require/requirejs pour définir les valeurs de configuration, mais pour charger les modules, il doit utiliser define ().

Étant donné que Twitter Bootstrap n'est pas un module AMD, je dois le caler pour qu'il fonctionne. C'est ainsi que je le configure;

<script type="text/javascript">
    var require = {
        paths: {
            "bootstrap": "../bootstrap",
            "jquery": "../jquery-1.8.2"
        },
        shim: {
            "bootstrap": ["jquery"]
        },
        enforceDefine: true
    };
</script>

Plus tard, lorsque mon module veut bootstrap comme dépendance, je me retrouve toujours avec un message d'erreur;

Error: No define call for bootstrap

http://requirejs.org/docs/errors.html#nodefine

Si j'ai bien compris les documents, enforceDefinedevrait ignorer les shims mais ce n'est pas le cas.

Qu'est-ce que je fais mal ici?

37
Johan Alkstål

Selon les documents, cette erreur est levée si "le script faisait partie d'une configuration de shim qui spécifiait une propriété de chaîne globale dont le chargement peut être vérifié et cette vérification a échoué."

Pour résoudre ce problème, vous devez ajouter une valeur d'export dans la configuration de shim afin que RequireJS puisse vérifier si le script a été correctement chargé. Dans le cas de Bootstrap c'est un peu délicat car Bootstrap n'exporte pas une variable globale propert seulement un tas de plugins jquery, mais vous pouvez utiliser n'importe lequel des ces plugins comme valeur d'exportation, par exemple $.fn.popover:

{
    paths: {
        "bootstrap": "../bootstrap",
        "jquery": "../jquery-1.8.2"
    },
    shim: {
        "bootstrap": {
          deps: ["jquery"],
          exports: "$.fn.popover"
        }
    },
    enforceDefine: true
}
37
Karolis

Au lieu de faire la magie avec shim, j'ai converti le bootstrap JS en module:

define([ "jquery" ], function($) {
  // bootstrap JS code
});

Tout le reste que j'ai trouvé dans les forums et sur stackoverflow n'a pas fonctionné pour moi car je reçois jQuery du CDN. Je suppose que j'ai touché le problème comme décrit dans le document requireJS sur http://requirejs.org/docs/api.html

Ne mélangez pas le chargement CDN avec la configuration shim dans une version. Exemple de scénario: vous chargez jQuery à partir du CDN mais utilisez la configuration shim pour charger quelque chose comme la version stock de Backbone qui dépend de jQuery. Lorsque vous effectuez la génération, assurez-vous d'inclure jQuery dans le fichier créé et ne le chargez pas à partir du CDN. Sinon, Backbone sera inséré dans le fichier construit et il s'exécutera avant que la jQuery chargée avec CDN ne se charge. Cela est dû au fait que la configuration shim retarde simplement le chargement des fichiers jusqu'à ce que les dépendances soient chargées, mais n'effectue aucun habillage automatique de define. Après une construction, les dépendances sont déjà intégrées, la configuration shim ne peut pas retarder l'exécution du code non-define () 'd plus tard. Les modules define () 'd fonctionnent avec du code chargé CDN après une construction car ils encapsulent correctement leur source dans la fonction de définition d'usine qui ne s'exécutera pas avant le chargement des dépendances. Donc, la leçon: la configuration de shim est une mesure provisoire pour le code non modulaire, le code hérité. Les modules define () 'd sont meilleurs.

La conversion du bootstrap en un module AMD ordinaire et la suppression de la configuration de shim l'ont résolu pour moi. Seul inconvénient: vous ne pouvez pas récupérer bootstrap du bootstrap CDN.

14
Patrick Hammer

J'utilise cette configuration dans mon projet:

startup.js

require.config({
    paths: {
        /* other paths are omitted */
        'bootstrap': '../libs/bootstrap'
    },
    shim: {
        'bootstrap/bootstrap-slider': { deps: ['jquery'], exports: '$.fn.slider' }, 
        'bootstrap/bootstrap-affix': { deps: ['jquery'], exports: '$.fn.affix' },
        'bootstrap/bootstrap-alert': { deps: ['jquery'], exports: '$.fn.alert' },
        'bootstrap/bootstrap-button': { deps: ['jquery'], exports: '$.fn.button' },
        'bootstrap/bootstrap-carousel': { deps: ['jquery'], exports: '$.fn.carousel' },
        'bootstrap/bootstrap-collapse': { deps: ['jquery'], exports: '$.fn.collapse' },
        'bootstrap/bootstrap-dropdown': { deps: ['jquery'], exports: '$.fn.dropdown' },
        'bootstrap/bootstrap-modal': { deps: ['jquery'], exports: '$.fn.modal' },
        'bootstrap/bootstrap-popover': { deps: ['jquery'], exports: '$.fn.popover' },
        'bootstrap/bootstrap-scrollspy': { deps: ['jquery'], exports: '$.fn.scrollspy'        },
        'bootstrap/bootstrap-tab': { deps: ['jquery'], exports: '$.fn.tab' },
        'bootstrap/bootstrap-tooltip': { deps: ['jquery'], exports: '$.fn.tooltip' },
        'bootstrap/bootstrap-transition': { deps: ['jquery'], exports: '$.support.transition' },
        'bootstrap/bootstrap-typeahead': { deps: ['jquery'], exports: '$.fn.typeahead'  },
    }
});

require(['domReady', 'app'], function(domReady, app) {
    domReady(function() {
        app.init();
    });
});

puis dans mon code j'utilise ceci:

define(['jquery', 'underscore', 'backbone', 'text!templates/photos-list.html'], function($, _, Backbone, html) {
    var PhotosListView = Backbone.View.extend({
        viewImageFullscreen: function(e) {
            e.preventDefault();

            require(['bootstrap/bootstrap-modal', 'text!templates/photo-modal.html'], function(modal, htmlModal) {
                 var modalTemplate = _.template(htmlModal, options);
                 $('body').append(modalTemplate);

                 // setup
                 $(selector + '_modal').modal({
                     backdrop: true,
                     keyboard: true,
                     show: false
                 }).css({
                     'width': function() { return ($(document).width() * 0.55) + 'px'; },
                     'margin-left': function() { return -($(this).width() * 0.5); }
                 });

                 // trigger `modal` 
                 $(selector + '_modal').modal('show');
             }); // require() call
         // ...
11
lexeme

@lexeme & @benjaminbenben que diriez-vous d'envelopper ce concept dans un plugin RequireJS qui crée la cale, nécessite jQuery et renvoie également jQuery afin que vous n'ayez pas besoin de l'inclure manuellement?

Pour utiliser un composant bootstrap, vous utiliseriez simplement:

define(['bootstrap!tooltip'], function($){
  $('[data-toggle="tooltip"]').tooltip();
});

Et vous utiliseriez ceci require-bootstrap-plugin pour le faire fonctionner.

4
Jasper Moelker