Рендеринг представления коллекции Backbone.js только для чтения

Кратко о моей проблеме: первая визуализация представления содержит элементы коллекции, а вторая — нет. Подробнее...

Также примечание: я понимаю, что следующий код представляет собой счастливый путь и что в нем отсутствует много кода потенциальной обработки ошибок и т. д., и т. д... это сделано намеренно для краткости.

Я новичок в backbone.js, и у меня возникли проблемы с поиском правильного способа реализации решения для следующего сценария:

У меня есть страница с двумя основными областями для содержания/отображения отображаемого контента, который будет основным макетом для приложения. В целом это выглядит примерно так:

-------------------------------------------
|                          |              |
|                          |              |
|         Content          |   Actions    |
|          Panel           |    Panel     |
|                          |              |
|                          |              |
|                          |              |
|                          |              |
|                          |              |
|                          |              |
-------------------------------------------

У меня есть HTTP API, из которого я получаю данные, который фактически предоставляет информацию о ресурсах для различных модулей в виде результатов JSON из OPTIONS вызова базового URL-адреса для каждого модуля. Например, запрос OPTIONS к http://my/api/donor-corps возвращает:

[
    {
        "Id":0,
        "Name":"GetDonorCorps",
        "Label":"Get Donor Corps",
        "HelpText":"Returns a list of Donor Corps",
        "HttpMethod":"GET",
        "Url":"https://my/api/donor-corps",
        "MimeType":"application/json",
        "Resources":null
    }
]

В приложении есть магистральная коллекция, которую я называю DonorCorpsCollection, которая будет доступна только для чтения из DonorCorpModel объектов, которые потенциально могут быть отображены несколькими различными магистральными представлениями и отображены по-разному на разных страницах приложения (в конечном итоге... позже ... на данный момент это не так). Свойство url объекта DonorCorpsCollection должно быть свойством Url объекта со свойством Name "GetDonorCorps" результатов начального вызова OPTIONS для получения ресурсов модуля API (показано выше).

В верхней части приложения есть строка меню со ссылками, при нажатии на которые будут отображаться различные страницы приложения. На данный момент в приложении есть только две страницы: «Главная» и «Предварительная сортировка». В представлении «Главная» обе панели просто содержат информацию-заполнитель, указывающую, что пользователь должен щелкнуть ссылку в строке меню, чтобы выбрать действие, которое необходимо выполнить. Когда пользователь нажимает ссылку на страницу «Предварительная сортировка», я просто хочу отобразить базовое представление, которое отображает DonorCorpsCollection в виде неупорядоченного списка на панели действий.

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

var myModule = (function () {

// Models
    var DonorCorpModel = Backbone.Model.extend({ });

// Collections
    var DonorCorpsCollection = Backbone.Collection.extend({ model : DonorCorpModel });

// Views
    var DonorCorpsListView = Backbone.View.extend({
        initialize : function () {
            _.bindAll(this, 'render');
            this.template = _.template($('#pre-sort-actions-template').html());
            this.collection.bind('reset', this.render); // the collection is read-only and should never change, is this even necessary??
        },

        render : function () {
            $(this.el).html(this.template({})); // not even exactly sure why, but this doesn't feel quite right

            this.collection.each(function (donorCorp) {
                var donorCorpBinView = new DonorCorpBinView({
                    model : donorCorp,
                    list : this.collection
                });

                this.$('.donor-corp-bins').append(donorCorpBinView.render().el);
            });

            return this;            
        }
    });

    var DonorCorpListItemView = Backbone.View.extend({
        tagName : 'li',
        className : 'donor-corp-bin',

        initialize : function () {
            _.bindAll(this, 'render');
            this.template = _.template($('#pre-sort-actions-donor-corp-bin-view-template').html());
            this.collection.bind('reset', this.render);
        },

        render : function () {
            var content = this.template(this.model.toJSON());
            $(this.el).html(content);
            return this;
        }
    });

// Routing
    var App = Backbone.Router.extend({
        routes : {
            '' : 'home',
            'pre-sort', 'preSort'
        },

        initialize : function () { },

        home : function () {
            // ...
        },

        preSort : function () {
            // what should this look like??
            // I currently have something like...

            if (donorCorps.length < 1) {
                donorCorps.url = _.find(donorCorpResources, function (dc) { return dc.Name === "GetDonorCorps"; }).Url;
                donorCorps.fetch();
            }

            $('#action-panel').empty().append(donorCorpsList.render().el);
        }
    })

// Private members
    var donorCorpResources;
    var donorCorps = new DonorCorpsCollection();
    var donorCorpsList = new DonorCorpsListView({ collection : donorCorps });

// Public operations
    return {
        Init: function () { return init(); }
    };

// Private operations
    function init () {
        getAppResources();
    }

    function getAppResources () {
        $.ajax({
            url: apiUrl + '/donor-corps',
            type: 'OPTIONS',
            contentType: 'application/json; charset=utf-8',
            success: function (results) {
                donorCorpResources = results;
            }
        });
    }

}(myModule || {}));

И, наконец, все это использует следующий HTML:

...
<div class="row-fluid">
    <div id="viewer" class="span8">
    </div>
    <div id="action-panel" class="span4">
    </div>
</div>
...
<script id="pre-sort-actions-template" type="text/template">
    <h2>Donor Corps</h2>
    <ul class="donor-corp-bins"></ul>
</script>

<script id="pre-sort-actions-donor-corp-bin-view-template" type="text/template">
    <div class="name"><%= Name %></div>
</script>

<script>
    $(function () {
        myModule.Init();
    });
</script>
...

До сих пор мне удавалось заставить это работать в первый раз, когда я нажимал на ссылку меню "Предварительная сортировка". Когда я щелкаю по нему, он отображает список Корпуса доноров, как и ожидалось. Но если я затем нажму ссылку «Главная», а затем снова ссылку «Предварительная сортировка», на этот раз я увижу заголовок, но <ul class="donor-corp-bins"></ul> будет пустым и в нем нет элементов списка. ... и я понятия не имею, почему или что мне нужно делать по-другому. Я чувствую, что понимаю представления и маршрутизаторы backbone.js в целом, но на практике я, видимо, что-то упускаю.

В общем, это кажется довольно простым сценарием, и я не чувствую, что пытаюсь сделать здесь что-то исключительное, но я не могу заставить его работать правильно и не могу понять, почему. Помощь или, по крайней мере, толчок в правильном направлении были бы очень признательны.


person Bob Yexley    schedule 28.06.2012    source источник


Ответы (2)


Итак, я нашел решение здесь. Не уверен, что это лучшее или правильное решение, но оно работает, по крайней мере, на данный момент.

Кажется, было несколько проблем. Основываясь на отзывах Роба Конери, я начал рассматривать различные варианты рендеринга коллекции с помощью моего шаблона представления, и вот что я придумал:

Как я упоминал в комментарии, мы используем Handlebars для создания шаблонов представления, поэтому этот код использует это.

В итоге я изменил свой шаблон представления, чтобы он выглядел так:

<script id="pre-sort-actions-template" type="text/x-handlebars-template">
    <h2>Donor Corps</h2>
    <ul class="donor-corp-bins">
    {{#each list-items}}
        <li class="donor-corp-bin">{{this.Name}}</li>
    {{/each}}
    </ul>
</script>

Затем я полностью удалил DonorCorpListItemView и изменил DonorCorpsListView, чтобы теперь он выглядел так:

var DonorCorpsListView = Backbone.View.extend({
    initialize : function () {
        _.bindAll(this, 'render');
        this.collection.bind('reset', this.render);
    },
    render : function () {
        var data = { 'list-items' : this.collection.toJSON() };
        var template = Handlebars.compile($('#pre-sort-actions-template').html());
        var content = template(data);
        $(this.el).html(content);

        return this;
    }
});

Итак, это, кажется, работает и делает то, что мне нужно сейчас.

Мне явно еще многое предстоит узнать о том, как работают JS и Backbone.js, потому что следующее НЕ РАБОТАЕТ (выдает ошибку)... и у меня нет идея почему:

var DonorCorpsListView = Backbone.View.extend({
    initialize : function () {
        _.bindAll(this, 'render');
        this.template = Handlebars.compile($('#pre-sort-actions-template').html());
        this.collection.bind('reset', this.render);
    },
    render : function () {
        var data = { 'list-items' : this.collection.toJSON() };
        var content = this.template(data);
        $(this.el).html(content);

        return this;
    }
});

Надеюсь, это будет кому-то полезно.

person Bob Yexley    schedule 29.06.2012

Ваш код рендеринга кажется мне странным для DonorCorpListView - похоже, вы создаете шаблоны до того, как у вас есть какие-либо данные. Вам нужно скомпилировать этот шаблон с данными, чтобы получить вывод HTML.

Итак, логика рендеринга должна быть примерно такой:

var template = $([get the template).html();
template.compile(this.collection.toJSON());
this.el.append(template.render());

Я использовал здесь некоторый псевдокод, так как я не уверен, каков ваш механизм шаблонов (мне самому нравятся Handlebars).

person Community    schedule 28.06.2012
comment
Я просто использую шаблон underscore.js для этого примера... но я использую Handlebars для реализации. Чтобы заставить это работать, я хотел удалить как можно больше переменных. - person Bob Yexley; 29.06.2012
comment
Хотя, я думаю, я понимаю, о чем вы сейчас говорите... с Handlebars я мог бы использовать помощник #each для коллекции JSON, вместо того, чтобы перебирать коллекцию и добавлять дочерние представления для каждого элемента. Я могу попробовать, но не уверен, что это решит проблему. Хотя спасибо за идеи... - person Bob Yexley; 29.06.2012