web-dev-qa-db-fra.com

Comment charger des modèles démarrés dans Backbone.js avec AMD (require.js)

La documentation de Backbone.js suggère de charger les modèles démarrés de cette façon: 

<script>
var Accounts = new Backbone.Collection;
Accounts.reset(<%= @accounts.to_json %>);
var Projects = new Backbone.Collection;
Projects.reset(<%= @projects.to_json(:collaborators => true) %>);
</script>

Mais ceci est un motif qui ne peut pas être utilisé dans l’approche AMD (en utilisant require.js)

La seule solution possible consiste à déclarer une variable globale stockant des données JSON et à utiliser cette variable ultérieurement dans les méthodes d'initialisation appropriées.

Y a-t-il un meilleur moyen de le faire (sans globals)?

53
opengrid

Nous amorçons les données de manière à ne pas polluer l’espace de nom global. Au lieu de cela, il utilise require.js exclusivement. Il vous aide également à fournir la configuration initiale de l'application en fonction des variables du modèle.

Dans votre page rendue

<script src="require.js"></script>
<script>
define('config', function() {
  return {
    bootstrappedAccounts: <%= @accounts.to_json %>,
    bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
  };
});
</script>
<script src="app.js"></script>

globals.js

Ce fichier vérifie la configuration et s’étend en utilisant les données renvoyées.

define([
  'config',
  'underscore'
], function(config) {

  var globals = {
  };
  _.extend(globals, config);
  return globals;

});

config.js

Ce fichier est nécessaire si vous souhaitez pouvoir charger l'application, que vous ayez défini config dans la page.

define(function() {
  // empty array for cases where `config` is not defined in-page
  return {};
});

app.js

require([
  'globals',
  'underscore',
  'backbone'
], function(globals) {

  if (globals.bootstrappedAccounts) {
    var accounts = new Backbone.Collection(globals.bootstrappedAccounts);
  }
  if (globals.bootstrappedProjects) {
    var projects = new Backbone.Collection(globals.bootstrappedProjects);
  }

});
71
dlrust

On dirait que vous pouvez utiliser la fonction require.config () ou le "require" global avec l'option "config" afin de transmettre des données à un module via la dépendance spéciale "module". Voir http://requirejs.org/docs/api.html#config-moduleconfig :

Il est généralement nécessaire de transmettre les informations de configuration à un module. Cette les informations de configuration sont généralement connues dans le cadre de l'application, et il doit y avoir un moyen de transmettre cela à un module. Dans RequireJS, cela se fait avec l'option config pour requirejs.config (). Modules peut alors lire cette information en demandant la dépendance spéciale "module" et en appelant module.config ().

Ainsi, pour les modèles d'amorçage, nous avons, dans la page HTML de niveau supérieur:

<script>
var require = {
    config: {
        'app': {
            bootstrappedAccounts: <%= @accounts.to_json %>
            bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
        }
    }
};
</script>
<script src="scripts/require.js"></script>

Ensuite, dans le module d'application (app.js), nous avons:

define(['module'], function (module) {
    var accounts = new Backbone.Collection( module.config().bootstrappedAccounts );
    var bootstrappedProjects = new Backbone.Collection( module.config().bootstrappedProjects );
});

Ici "module" est une dépendance spéciale fournie pour ces types de cas.

Ceci n’a pas été testé, mais il semble à peu près sûr de la documentation.

31
Brave Dave

Dans RequireJS, cela est fait avec l'option de configuration pour requirejs.config(). Les modules peuvent ensuite lire ces informations en demandant la dépendance "module" et en appelant module.config(). Exemple:

index.html

<script>
  var require = {
    config: {
      'app': {
        'api_key': '0123456789-abc'
      }
    }
  };
</script>
<script src="js/libs/require.js" data-main="js/main"></script>

main.js

require( ['app'], function(App) {
  new App();
});

app.js

define( ['module'], function(module) {
  var App = function() {
    console.log( 'API Key:', module.config().api_key );
  };

  return App;
});

Notez simplement que le nom de l'objet de configuration doit correspondre à le nom du module. Dans mon exemple, le nom du module était app. Par conséquent, le nom de l'objet de configuration devait également s'appeler app. Dans le module, vous devrez inclure ['module'] en tant que dépendance et appeler module.config()[property name] pour récupérer les données de configuration.

Lisez la documentation à ce sujet: http://requirejs.org/docs/api.html#config-moduleconfig

11
Tom Doe

Certaines des réponses ici m’ont rapproché de mon problème similaire, mais rien n’a été dit. En particulier, la réponse la mieux classée et acceptée qui semblait me donner une mauvaise condition de course où parfois l'objet factice se chargeait en premier. Cela s’est également produit 100% du temps lorsqu’il a été utilisé avec l’optimiseur. Il utilise également des noms de chaîne explicites pour le module, ce que la documentation obligatoire vous conseille expressément de ne pas faire. 

Voici comment je l'ai travaillé. Semblable à Brave Dave, j’utilise l’objet config pour capturer les paramètres (dans mon cas à partir d’une page jsp) comme

<script type="text/javascript">
    var require = {
        config: {
            options : { 
                bootstrappedModels : ${models}
            }
        }
    }
</script>

Notez en particulier que mes paramètres sont dans un objet appelé options. Ce nom n'est pas optionnel! Bien que la documentation ne le mentionne pas, voici comment require chargera votre configuration (ligne 564 dans requirejs 2.1.1):

config: function () {
    return (config.config && config.config[mod.map.id]) || {};
},

Le point clé est qu'il doit y avoir une propriété sur l'objet config avec la clé mod.map.id qui se résout en 'options'.

De là, vous pouvez maintenant accéder aux modèles comme

define(['module'], function(module){
    console.log(module.config().bootstrappedModels);
    //...
});
6
Ollie Edwards

Vous pouvez ajouter une fonction loopy à la fin de votre module AMD pour vérifier le moment où la méthode init est définie (afin qu'elle puisse être renseignée après body ou chargée à partir d'un include). De cette façon, le module est garanti et l'initialisation peut être effectuée dès qu'il est prêt.

require(...,function (...) {
   //define models collections, etc..

   var initme = function () {
     if(document.initThisModule) 
       document.initThisModule();
     else
       setTimeout(initme, 10);
   }();
});
3
Justin Alexander

Je ne suis pas (trop) familier avec l'approche AMD, mais au lieu d'utiliser une variable globale, pourquoi n'ajoutez-vous pas le JSON au dom.

par exemple:

var json = ...,
$jsonContainer = $(json).wrap("<script id='json-container' type='text/javascript'>").appendTo($("body"));

Ensuite, à la place d'une balise de script intégrée, comme le suggère la documentation du backbone, dans le document ready:

$(function(){
    MyCollection.reset($("#json-container").html());
    ...
});
3
idbentley

Pourquoi ne pas faire quelque chose comme ça:

<script>
define('Models', ['backbone'], function(Backbone) {
    var Models = {
        Accounts: new Backbone.Collection,
        Projects: new Backbone.Collection
    };

    Models.Accounts.reset(<%= @accounts.to_json %>);
    Models.Projects.reset(<%= @projects.to_json(:collaborators => true) %>);

    return Models;
});
</script>

Ensuite, vous pourrez utiliser Models dans d'autres modules comme celui-ci:

var models = require(['Models']);
models.Accounts.doWhatYouNeed();

ou ca:

define(['any', 'dependencies', 'and', 'Models'], function(a, b, c, Models) {
   // Models will be available here
});
2
Yauheni Leichanok

Comme décrit ci-dessus, le 'module de données' (ou config, ou ce que vous voulez appeler) peut être inclus dans un fichier qui est déjà généré de toute façon (par exemple, index.html) mais je pense que c'est plutôt moche. 

Une autre solution consisterait à le déclarer dans son propre fichier de module, mais cela nécessiterait un aller-retour supplémentaire vers le serveur dans les environnements de production. Dès que vous souhaitez créer et optimiser vos dépendances requirejs, le module de données ne peut pas être inclus car il est généré de manière dynamique lors du chargement de la page.

Une troisième option pourrait être de l’ajouter à l’un des fichiers que vous servez (par exemple, le fichier requirejs optimisé), mais je n’ai aucune idée de ce qui pourrait être fait.

2
godspeedelbow

Ansewr par @dlrust fonctionne mais il ne peut pas étendre param et passer plus d’un endroit dans le code. Si vous essayez de faire quelque chose comme ceci dans votre modèle de rendu:

<script>
    define('config', function() {
        return {
            bootstrappedAccounts: <%= @accounts.to_json %>,
            bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
        };
    });
</script>

et dans un autre fichier ajouter des données

<script>
    define('config', function() {
        return {
            goods: <%= some data %>,
            showcaseList: <%= some json or array %>
        };
    });
</script>

c'était écrasé (NOT EXTEND !!!). Dans la configuration ne seront que les dernières données déclarées.

Ma solution: utiliser le modèle Backbone avec set/get data.

app.js

define("App", [], function() {
    window.App = {
        // модели
        Model: {},
        // коллекции
        Collection: {},
        // виды
        View: {},
        // роутеры
        Router: {},
        // модальные окна
        Modal: {},
        // UI компоненты
        UI: {}
    };
    return window.App;
});

global.js

define(["App", "underscore", "backbone"], function(App, _, Backbone) {
    "use strict";

    // модель глобальных данных
    App.Model.Global = Backbone.Model.extend({
        defaults: {}
    });

    return new App.Model.Global;    
});

index.php

<!DOCTYPE html>
<html>
    <head>
        <!--HEAD_START-->
        <script type="text/javascript" data-main="/app/init" src="/app/require/require.js"></script>
        <!--HEAD_END-->
    </head>

    <body>          
        <div id="tm-inner-wrap">
            <div id="loader"><i class="uk-icon-refresh uk-icon-spin"></i></div>
            <!--HEADER_START-->
            <?= $this->includeTpl('header_view'); ?>
            <!--HEADER_END-->

            <!--CONTENT_START-->
            <div>your html content data</div>
            <!--CONTENT_END-->

            <!--FOOTER_START-->
            <?= $this->includeTpl('footer_view');?>
            <!--FOOTER_END-->

            <script>
                require(["global"], function(Global) {
                    Global.set("notifyList", <?=json_encode($this->notifyList);?>);
                });
            </script>
        </div>
    </body>
</html>

un autre modèle

someTemplate.php

<div class="tm-inner-body">
    <div class="uk-container uk-container-center">
        // content data
    </div>
</div>

<script>    
    require(["global", "module/index"], function(Global) {
        Global.set("goodList", <?=json_encode($this->goodList);?>);
    });
</script>

index.js

require(["App", "core", "jquery", "uikit!uikit-addons-min", "underscore", "backbone", "global", "module/good/goodView"], function(App, Core, $, UIkit, _, Backbone, Global, goodView) {
    "use strict";

    // Global.get("notifyList"); its too able

    App.Collection.Good = new Backbone.Collection(Global.get("showcaseList")["items"]);

    // вид списка товаров
    App.View.GoodList = Backbone.View.extend({
        // елемент
        el: ".tm-good-list",
        // init
        initialize: function() {
            this.collection = App.Collection.Good;
            // список товаров
            this.drawList();
        },
        // отрисовка списка
        drawList: function() {
            this.$el.empty();
            this.collection.each(function(item, index) {
                this.$el.append(this.drawItem(item));
            }, this);
        },
        // отрисовка елемента
        drawItem: function(data) {
            var good = new goodView({model: data});
            return good.render().el;
        }
    });

    App.View.Index = Backbone.View.extend({
        el: "body",
        // пользовательские события
        events: {
            //
        },
        // init
        initialize: function() {
            var $this = this;
            if(Global.get("showcaseList")) new App.View.GoodList();
        }
    });

    new App.View.Index();
});

Structure du fichier:

 file structure

1
Vitaliy