wake-up-neo.com

So laden Sie bootstrappte Modelle in Backbone.js, während Sie AMD verwenden (required.js)

In der Backbone.js-Dokumentation wird empfohlen, bootstrapped-Modelle folgendermaßen zu laden: 

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

Dies ist jedoch ein Muster, das nicht im AMD-Ansatz verwendet werden kann (using requir.js)

Die einzig mögliche Lösung ist die Deklaration von globale Variable, die JSON-Daten speichert und diese Variable später in relevanten Initialisierungsmethoden verwenden.

Gibt es einen besseren Weg (ohne Globals)?

53
opengrid

Auf diese Weise booten wir Daten so, dass der globale Namespace nicht verschmutzt wird. Stattdessen werden ausschließlich requir.js verwendet. Außerdem können Sie die anfängliche App-Konfiguration basierend auf Variablen in der Vorlage bereitstellen.

In Ihrer gerenderten Seite

<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

Diese Datei prüft auf config und erweitert sie anhand der zurückgegebenen Daten

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

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

});

config.js

Diese Datei wird benötigt, wenn Sie die App laden möchten, unabhängig davon, ob Sie config auf der Seite definiert haben.

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

Sie können anscheinend die Funktion „required.config ()“ oder die Option „required“ mit der Option „config“ verwenden, um Daten über die spezielle Abhängigkeit „module“ an ein Modul zu übergeben. Siehe http://requirejs.org/docs/api.html#config-moduleconfig :

Es ist üblich, Konfigurationsinformationen an ein Modul zu übergeben. Das Konfigurationsinformationen sind normalerweise als Teil der Anwendung bekannt, und Es muss eine Möglichkeit geben, dies an ein Modul weiterzugeben. In RequireJS Dies erfolgt mit der Konfigurationsoption für requirjs.config (). Module kann diese Informationen dann lesen, indem Sie nach der speziellen Abhängigkeit "module" .__ fragen. und Aufruf von module.config ().

Für Bootstrapping-Modelle haben wir also auf der obersten HTML-Seite:

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

Dann haben wir im App-Modul (app.js):

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

Hier ist "Modul" eine spezielle Abhängigkeit, die für diese Art von Fällen bereitgestellt wird.

Dies ist nicht getestet, scheint aber aus der Dokumentation ziemlich sicher.

31
Brave Dave

In RequireJS erfolgt dies mit der Konfigurationsoption für requirejs.config(). Module können diese Informationen dann lesen, indem sie nach der speziellen Abhängigkeit "module" fragen und module.config() aufrufen. Beispiel:

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;
});

Beachten Sie, dass der Name des Konfigurationsobjekts muss übereinstimmen den Namen des Moduls. In meinem Beispiel war der Name des Moduls app, daher musste der Name des Konfigurationsobjekts auch app heißen. Im Modul müssen Sie ['module'] als Abhängigkeit angeben und module.config()[property name] aufrufen, um die Konfigurationsdaten abzurufen.

Lesen Sie dazu die Dokumentation: http://requirejs.org/docs/api.html#config-moduleconfig

11
Tom Doe

Einige der Antworten hier haben mich an ein ähnliches Problem herangeführt, aber es hat nichts gebracht. Insbesondere die am besten bewertete und akzeptierte Antwort schien mir eine unangenehme Rennbedingung zu geben, bei der das Dummy-Objekt manchmal zuerst geladen wurde. Dies geschah auch zu 100%, wenn es mit dem Optimierer verwendet wurde. Es verwendet auch explizite Zeichenfolgennamen für das Modul, die in der erforderlichen Dokumentation ausdrücklich dazu aufgefordert werden, dies nicht zu tun. 

So habe ich es gemacht. Ähnlich wie bei Brave Dave verwende ich das config-Objekt, um Parameter (in meinem Fall von einer JSP-Seite) so zu erfassen

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

Beachten Sie insbesondere, dass sich meine Parameter in einem Objekt befinden, das als Optionen bezeichnet wird. Dieser Name ist nicht optional! Obwohl dies in der Dokumentation nicht erwähnt wird, wird Ihr config folgendermaßen geladen (Zeile 564 in requirjs 2.1.1):

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

Der entscheidende Punkt ist, dass im config-Objekt eine Eigenschaft mit dem Schlüssel mod.map.id vorhanden sein muss, der in "Optionen" aufgelöst wird.

Von hier aus können Sie jetzt so auf die Modelle zugreifen

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

Sie können am Ende Ihres AMD-Moduls eine loopy-Funktion hinzufügen, um zu überprüfen, wann die init-Methode definiert ist (damit sie nach body gefüllt oder aus einem Include geladen werden kann). Auf diese Weise ist das Modul garantiert verfügbar und die Initialisierung kann erfolgen, wenn es fertig ist.

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

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

Ich bin nicht (zu) mit dem AMD-Ansatz vertraut, aber anstatt eine globale Variable zu verwenden, fügen Sie dem Dom nicht JSON hinzu.

zum Beispiel:

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

Dann statt eines eingebetteten Skript-Tags, wie in der Backbone-Dokumentation vorgeschlagen, im Dokument ready:

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

Wie wäre es mit so etwas zu tun:

<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>

Dann können Sie Models in anderen Modulen wie folgt verwenden:

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

oder dieses:

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

Wie oben beschrieben, könnte das 'data module' (oder die config oder was auch immer Sie es nennen wollen) in einer Datei enthalten sein, die sowieso schon erzeugt wurde (z. B. index.html), aber ich finde das ziemlich hässlich. 

Eine andere Möglichkeit wäre, sie in einer eigenen Moduldatei zu deklarieren, dies würde jedoch einen zusätzlichen Roundtrip zum Server in Produktionsumgebungen erfordern. Sobald Sie Ihre Anforderungsabhängigkeiten erstellen und optimieren möchten, kann das Datenmodul nicht eingeschlossen werden, da es beim Laden der Seite dynamisch generiert wird.

Eine dritte Möglichkeit wäre, es an eine der Dateien anzuhängen, die Sie bereitstellen (z. B. die optimierte Requiredjs-Datei), aber ich habe keine Ahnung, wie und ob dies möglich wäre.

2
godspeedelbow

Ansewr by @dlrust funktioniert, aber es ist nicht in der Lage, param zu erweitern und mehr als von einer Stelle im Code zu übergeben. Wenn Sie versuchen, etwas in Ihrer Rendervorlage zu tun:

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

und in einer anderen Datei einige Daten hinzufügen

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

es wurde überschrieben (NOT EXTEND !!!). In config werden nur die zuletzt angegebenen Daten angegeben.

Meine Lösung: verwendetes Backbone-Modell mit Set/Get-Daten.

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>

eine andere Vorlage

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();
});

Dateistruktur:

 file structure

1
Vitaliy