javascript - Cómo cargar modelos bootstrapped en Backbone.js mientras se usa AMD(require.js)
requirejs js-amd (9)
La documentación de Backbone.js sugiere cargar modelos bootstrapped de esta manera:
<script>
var Accounts = new Backbone.Collection;
Accounts.reset(<%= @accounts.to_json %>);
var Projects = new Backbone.Collection;
Projects.reset(<%= @projects.to_json(:collaborators => true) %>);
</script>
Pero este es un patrón que no se puede usar en el enfoque AMD (usando require.js)
La única solución posible es declarar la variable global que almacena datos JSON y usar esta variable más adelante en los métodos de inicialización relevantes.
¿Hay una mejor manera de hacer esto (sin globales)?
¿Qué tal si haces algo como esto?
<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>
Entonces podrás usar Modelos en otros módulos como este:
var models = require([''Models'']);
models.Accounts.doWhatYouNeed();
o esto:
define([''any'', ''dependencies'', ''and'', ''Models''], function(a, b, c, Models) {
// Models will be available here
});
Algunas de las respuestas aquí me acercaron a mi problema similar, pero nada lo encasilló. En particular, la respuesta mejor clasificada y aceptada dio parecía darme una desagradable condición de carrera en la que a veces el objeto ficticio se cargaba primero. Esto también ocurrió el 100% del tiempo cuando se usa con el optimizador. También utiliza nombres de cadena explícitos para el módulo que la documentación requerida específicamente le aconseja no hacer.
Así es como lo trabajé. Similar a Brave Dave, utilizo el objeto config para capturar los parámetros (en mi caso desde una página jsp) como tal
<script type="text/javascript">
var require = {
config: {
options : {
bootstrappedModels : ${models}
}
}
}
</script>
En particular, tenga en cuenta que mis parámetros están en un objeto llamado opciones. ¡Este nombre no es opcional! Aunque la documentación no hace mención de esto, el siguiente es cómo require cargará su configuración (línea 564 en requirejs 2.1.1):
config: function () {
return (config.config && config.config[mod.map.id]) || {};
},
El punto clave es que tiene que haber una propiedad en el objeto de configuración con la clave mod.map.id que se resuelve en ''opciones''.
Desde aquí ahora puedes acceder a los modelos como
define([''module''], function(module){
console.log(module.config().bootstrappedModels);
//...
});
Ansewr por @dlrust funciona pero no puede extender param y pasar más de un lugar en el código. Si intenta hacer algo como esto en su plantilla de renderizado:
<script>
define(''config'', function() {
return {
bootstrappedAccounts: <%= @accounts.to_json %>,
bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
};
});
</script>
y en otro archivo agregue algunos datos
<script>
define(''config'', function() {
return {
goods: <%= some data %>,
showcaseList: <%= some json or array %>
};
});
</script>
fue sobrescribir ( NO EXTENDER !!! ). En config solo serán los últimos datos declarados.
Mi solución: se utilizó el modelo Backbone con 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>
otra plantilla
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();
});
Estructura del archivo:
Así es como iniciamos los datos de una manera que no contamine el espacio de nombres global. En su lugar, utiliza require.js exclusivamente. También lo ayuda a proporcionar la configuración inicial de la aplicación basada en variables dentro de la plantilla.
Dentro de tu página renderizada
<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
Este archivo comprueba la configuración y se extiende utilizando cualquiera de los datos devueltos
define([
''config'',
''underscore''
], function(config) {
var globals = {
};
_.extend(globals, config);
return globals;
});
config.js
Este archivo es necesario si desea cargar la aplicación independientemente de si ha definido la config
en la página.
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);
}
});
Como se describió anteriormente, el ''módulo de datos'' (o config, o como quiera llamarlo) podría incluirse en un archivo que ya se haya generado de todos modos (por ejemplo, index.html) pero creo que esto es bastante feo.
Otra forma sería declararlo en su propio archivo de módulo, pero esto requeriría una ida y vuelta adicional al servidor en entornos de producción. Tan pronto como quiera construir y optimizar sus dependencias requirejs, el módulo de datos no puede incluirse porque se genera dinámicamente al cargar la página.
Una tercera opción podría ser agregarlo a uno de los archivos que se sirven (por ejemplo, el archivo requirejs optimizado), pero no tengo idea de cómo / si se podría hacer eso.
En RequireJS esto se hace con la opción de configuración para requirejs.config()
. Los módulos pueden leer esa información preguntando por el "módulo" de dependencia especial y llamando a module.config()
. Ejemplo:
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;
});
Solo tenga en cuenta que el nombre del objeto de configuración debe coincidir con el nombre del módulo. En mi ejemplo, el nombre del módulo era app
, por lo que el nombre del objeto de configuración también debía llamarse app
. En el módulo, deberá incluir [''module'']
como dependencia y llamar a module.config()[property name]
para recuperar los datos de configuración.
Lea la documentación sobre esto: http://requirejs.org/docs/api.html#config-moduleconfig
No estoy (demasiado) familiarizado con el enfoque de AMD, pero en lugar de usar una variable global, ¿por qué no agregas el JSON al dom?
por ejemplo:
var json = ...,
$jsonContainer = $(json).wrap("<script id=''json-container'' type=''text/javascript''>").appendTo($("body"));
Luego, en lugar de una etiqueta de script incrustada como lo sugiere la documentación de la red troncal, dentro del documento listo:
$(function(){
MyCollection.reset($("#json-container").html());
...
});
Parece que puede usar la función require.config () o la función "require" global con la opción "config" para pasar datos a un módulo a través del "módulo" de dependencia especial. Consulte http://requirejs.org/docs/api.html#config-moduleconfig :
Existe una necesidad común de pasar información de configuración a un módulo. Esa información de configuración se conoce generalmente como parte de la aplicación, y debe haber una forma de pasarla a un módulo. En RequireJS, eso se hace con la opción de configuración para requirejs.config (). Los módulos pueden leer esa información preguntando por el "módulo" de dependencia especial y llamando a module.config ().
Entonces, para los modelos de bootstrapping tenemos, en la página HTML de nivel superior:
<script>
var require = {
config: {
''app'': {
bootstrappedAccounts: <%= @accounts.to_json %>
bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
}
}
};
</script>
<script src="scripts/require.js"></script>
Luego, en el módulo de aplicación (app.js), tenemos:
define([''module''], function (module) {
var accounts = new Backbone.Collection( module.config().bootstrappedAccounts );
var bootstrappedProjects = new Backbone.Collection( module.config().bootstrappedProjects );
});
Aquí el "módulo" es una dependencia especial suministrada para este tipo de casos.
Esto no se ha probado, pero parece bastante seguro de la documentación.
Puede agregar una función de bucle al final de su módulo AMD para verificar cuando se define el método init (para que pueda ser llenado después del cuerpo, o cargado desde un include). De esta forma, el módulo está garantizado y la inicialización puede suceder cuando esté lista.
require(...,function (...) {
//define models collections, etc..
var initme = function () {
if(document.initThisModule)
document.initThisModule();
else
setTimeout(initme, 10);
}();
});