How to load bootstrapped models in Backbone.js whi

2019-01-04 16:49发布

Backbone.js documentation suggest loading bootstrapped models this way:

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

But this is a pattern that can't be used in AMD approach (using require.js)

The only possible solution is to declare global variable storing JSON data and use this variable later in relevant initialize methods.

Is there a better way to do this (without globals)?

9条回答
迷人小祖宗
2楼-- · 2019-01-04 16:51

Some of the answers here got me close to my similar problem but nothing nailed it. In particular the top ranked and accepted answer gave seemed to give me a nasty race condition where sometimes the dummy object would load first. This also happened 100% of the time when used with the optimiser. It also uses explicit string names for the module which the require documentation specifically advises you not to do.

Here's how I worked it. Similar to Brave Dave, I use the config object to capture parameters (in my case from a jsp page) like so

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

In particular note that my parameters are in an object called options. This name is not optional! Though the documentation makes no mention of this, the following is how require will load your config (line 564 in requirejs 2.1.1) :

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

The key point is that there has to be property on the config object with the key mod.map.id which resolves to 'options'.

From here you can now access the models like so

define(['module'], function(module){
    console.log(module.config().bootstrappedModels);
    //...
});
查看更多
相关推荐>>
3楼-- · 2019-01-04 16:54

This is how we bootstrap data in a way that it doesn't pollute the global namespace. Instead it uses require.js exclusively. It also helps you provide the initial app configuration based on variables within the template.

Within your rendered page

<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

This file checks for config and extends itself using any of the data returned

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

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

});

config.js

This file is needed if you want be able to load the app regardless of if you have defined config in the 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);
  }

});
查看更多
啃猪蹄的小仙女
4楼-- · 2019-01-04 16:59

Ansewr by @dlrust work but it not able extend param and pass more than from one place in code. If you try do something like this in your render template:

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

and in another file add some data

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

it was overwrite (NOT EXTEND!!!). In config will be only last declared data.

My solution: used Backbone model with 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>

another template

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:

file structure

查看更多
【Aperson】
5楼-- · 2019-01-04 17:04

Looks like you can use the require.config() function or the "require" global with the "config" option in order to pass data to a module through the special dependency "module". See http://requirejs.org/docs/api.html#config-moduleconfig:

There is a common need to pass configuration info to a module. That configuration info is usually known as part of the application, and there needs to be a way to pass that down to a module. In RequireJS, that is done with the config option for requirejs.config(). Modules can then read that info by asking for the special dependency "module" and calling module.config().

So, for bootstrapping models we have, in the top level HTML page:

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

Then in the app module (app.js), we have:

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

Here "module" is a special dependency supplied for these types of cases.

This is untested but looks pretty sure from the documentation.

查看更多
何必那么认真
6楼-- · 2019-01-04 17:06

How about doing something like this:

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

Then you'll be able to use Models in other modules like this:

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

or this:

define(['any', 'dependencies', 'and', 'Models'], function(a, b, c, Models) {
   // Models will be available here
});
查看更多
SAY GOODBYE
7楼-- · 2019-01-04 17:07

I'm not (too) familiar with the AMD approach, but instead of using a global variable, why don't you add the JSON to the dom.

for example:

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

Then, instead of an embedded script tag as suggested by the backbone documentation, inside the document ready:

$(function(){
    MyCollection.reset($("#json-container").html());
    ...
});
查看更多
登录 后发表回答