Как загрузить загруженные модели в Backbone.js при использовании AMD (require.js) - PullRequest
53 голосов
/ 29 марта 2012

Документация Backbone.js предлагает загружать загруженные модели следующим образом:

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

Но это шаблон, который нельзя использовать в подходе AMD (с использованием require.js)

Единственное возможное решение - объявить глобальную переменную, хранящую данные JSON , и использовать эту переменную позже в соответствующих методах инициализации.

Есть ли лучший способ сделать это (без глобалов)?

Ответы [ 9 ]

71 голосов
/ 24 апреля 2012

Так мы загружаем данные таким образом, чтобы они не загрязняли глобальное пространство имен.Вместо этого он использует require.js исключительно.Это также помогает вам предоставить начальную конфигурацию приложения на основе переменных в шаблоне.

В пределах вашей отображаемой страницы

<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

Этот файл проверяет конфигурацию ирасширяет себя, используя любые возвращаемые данные

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

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

});

config.js

Этот файл необходим, если вы хотите иметь возможность загружать приложение, независимо от того, определено ли в строке configстр.

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

});
31 голосов
/ 21 октября 2012

Похоже, что вы можете использовать функцию require.config () или global "require" с опцией "config", чтобы передавать данные в модуль через специальную зависимость "module". Смотри http://requirejs.org/docs/api.html#config-moduleconfig:

Существует общая необходимость передавать информацию о конфигурации в модуль. Тот информация о конфигурации обычно известна как часть приложения, и должен быть способ передать это в модуль. В RequireJS, это делается с помощью опции config для requirejs.config (). Модули затем можно прочитать эту информацию, запросив специальную зависимость "модуль" и вызывая module.config ().

Итак, для моделей начальной загрузки мы имеем на HTML-странице верхнего уровня:

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

Тогда в модуле приложения (app.js) имеем:

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

Здесь «модуль» - это специальная зависимость, предоставляемая для этих типов дел.

Это не проверено, но выглядит довольно точно из документации.

11 голосов
/ 20 февраля 2013

В RequireJS это делается с помощью опции конфигурации для requirejs.config(). Модули могут затем прочитать эту информацию, запрашивая специальную зависимость "module" и вызывая module.config(). Пример:

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

Просто обратите внимание, что имя объекта конфигурации должно совпадать с именем модуля. В моем примере имя модуля было app, поэтому имя объекта конфигурации также должно было называться app. В модуле вам нужно будет включить ['module'] в качестве зависимости и вызвать module.config()[property name] для получения данных конфигурации.

Прочтите документацию по этому поводу: http://requirejs.org/docs/api.html#config-moduleconfig

6 голосов
/ 31 октября 2012

Некоторые ответы здесь приблизили меня к моей аналогичной проблеме, но ничто не пригвоздило ее. В частности, самый высокий и принятый ответ, казалось, дал мне неприятное состояние гонки, когда иногда фиктивный объект загружался первым. Это также происходит в 100% случаев при использовании с оптимизатором. Он также использует явные строковые имена для модуля, которые в обязательной документации специально не советуют делать.

Вот как я сработал. Как и в случае с Brave Dave, я использую объект конфигурации для захвата параметров (в моем случае со страницы jsp), например,

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

В частности, обратите внимание, что мои параметры находятся в объекте под названием options. Это имя не является обязательным! Хотя в документации не упоминается об этом, ниже показано, как require загрузит ваш конфиг (строка 564 в requirejs 2.1.1):

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

Ключевым моментом является то, что в объекте config должно быть свойство с ключом mod.map.id, который разрешается в 'options'.

Отсюда вы теперь можете получить доступ к таким моделям, как это

define(['module'], function(module){
    console.log(module.config().bootstrappedModels);
    //...
});
3 голосов
/ 04 апреля 2012

Я (слишком) не знаком с подходом AMD, но вместо того, чтобы использовать глобальную переменную, почему бы вам не добавить JSON в dom.

например:

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

Затем вместо встроенного тега сценария, как предлагается в базовой документации, внутри документа готово:

$(function(){
    MyCollection.reset($("#json-container").html());
    ...
});
3 голосов
/ 04 апреля 2012

Вы можете добавить зацикленную функцию в конце вашего модуля AMD, чтобы проверять, когда определен метод init (чтобы его можно было заполнять после body или загружать из include). таким образом, модуль гарантированно доступен, и инициализация может произойти, когда он будет готов.

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

   var initme = function () {
     if(document.initThisModule) 
       document.initThisModule();
     else
       setTimeout(initme, 10);
   }();
});
2 голосов
/ 26 ноября 2012

Как описано выше, «модуль данных» (или config, или как вы хотите его называть) может быть включен в файл, который уже сгенерирован (например, index.html), но я думаю, что это довольно уродливо

Другим способом было бы объявить его в своем собственном файле модуля, но для этого потребовалось бы дополнительное обратное обращение к серверу в производственных средах. Как только вы захотите построить и оптимизировать ваши зависимости requirejs, модуль данных не может быть включен, потому что он динамически генерируется при загрузке страницы.

Третьим вариантом может быть добавление его к одному из файлов, которые вы обслуживаете (например, оптимизированный файл requirejs), но я понятия не имею, как / если это можно сделать.

2 голосов
/ 29 марта 2012

Как насчет того, чтобы сделать что-то вроде этого:

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

Тогда вы сможете использовать Модели в других модулях, например так:

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

или это:

define(['any', 'dependencies', 'and', 'Models'], function(a, b, c, Models) {
   // Models will be available here
});
1 голос
/ 10 ноября 2016

Ansewr от @dlrust работает, но не может расширять param и передавать больше, чем из одного места в коде. Если вы попытаетесь сделать что-то подобное в шаблоне рендеринга:

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

и в другом файле добавить данные

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

это было перезаписано ( НЕ РАСШИРЯТЬ !!! ). В конфиге будут только последние объявленные данные.

Мое решение: используется модель Backbone с установкой / получением данных.

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>

другой шаблон

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

Структура файла:

file structure

...