Как стать автором
Обновить

Комментарии 7

>> header-view.js
>> container.appendChild(el);
Плохая практика. Тот кто будет вызывать не должен передавать контейнер а должен сделать
var view = new Header-view();
$('.main').html(view.$el);

Потому что передача контейнера при инициализации это нестандартная фича. А $el/el есть в backbone всегда.

Вот так выглядит инициализация модулей в Router:
newMessage : function(){
            require([
                'js/modules/NewMessage/NewMessageModel',
                'js/modules/NewMessage/NewMessageView'
            ], function(NewMessagesModel, NewMessagesView){
                var model = new NewMessagesModel(),
                    view = new NewMessagesView({model : model});
                $('.main').html(view.$el);
            }.bind(this));
        }

соотвественно все данные(типа ID модели) записываются из роутера, и он — звено, связующее компоненты между собой(модели-вьюхи). Всё что нужно сделать — после инициализации вьюхи вернуть this.
А с подходом container.append(this.el) мы рано или поздно перейдём к $('.body, .ololo').append(this.el) что черевато.
Заголовок спойлера
нда, минус 6 в репу и минус 2 в карму. Очень заслуженно и в тихую, без какой-либо критики.

Т.е по вашей логике необходимость передачи контейнера во view это «возможность работать со view из консоли»? В этом случае я могу view использовать по сути без связки с DOM. Просто инициализировать-пользоваться по необходимости.
А «this» в любом случае возвращать нужно — вьюху можно слушать, у вьюхи можно дёргать «внешние» методы…
Вообщем — необходимость во внешнем контейнере при инициализации view — лишняя зависимость. Есть варианты вьюх без контейнеров от слова _совсем_, например крипто-подпись(если всё ОК то пользователю не будет ничего предлагать, просто отдаст внешнему методу желанную подпись). В итоге — получается каша из решений и нужно помнить где передавать контейнер а где нет. Зачем зависимость внутри кода которая не-необходима для работы кода? Всё прекрасно будет работать и в виртуальном DOM.
Есть логика в ваших словах, доработаю статью, чтобы этот момент больше не вызывал вопросов. Спасибо.

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


Ну и «Толик» всё же не справился с задачей до конца и использует глобальную переменную app:


app.headerView  = new HeaderView(app, document.getElementById('head'));
Ну модульный подход типа CommonJS, как require и import, автоматом решают эти задачи. Если использовать сборщик webpack, то вообще красота будет. Однако, спасибо за коммент!
А кто сказал что они глобальные? $ объявлен в require уровнем выше и уровнем ниже(в самой view). Просто не видел смысла приводить полный пример кода ради аргументирования своего мнения по поводу одной строки кода.
Если нужен полный пример — пожалуйста:
router.js
define([
    'jquery',
    'underscore',
    'backbone',
    'require'
], function ($, _, Backbone, App, require) {
"use strict";
return Backbone.Router.extend({
	routes: {
		'': 'notFound',
		'newMessage' : 'newMessage',
		'*notFound': 'notFound'
	},
	notFound: function (route) {
		this.navigate('newMessage', {trigger: true});
	},
	newMessage : function(){
		require([
			'js/modules/NewMessage/NewMessageModel',
			'js/modules/NewMessage/NewMessageView'
		], function(NewMessagesModel, NewMessagesView){
			var model = new NewMessagesModel(),
				view = new NewMessagesView({model : model});
			$('.main').html(view.$el);
		}.bind(this));
	}
})


NewMessageModel.js
define([
    'underscore',
    'backbone'
], function (_, Backbone) {
    "use strict";
    return Backbone.Model.extend({
        url: '/api/v2/mail',
        defaults : {
            document_number : '',
            mail_subject : '',
            mail_body : '',
            attach : []
        },
        initialize: function () {
            return this;
        },
        validate : function(attr){
            var arr = _.compact(_.map(attr, function (val, name) {
                var error_name = '';
                if (!val) {
                    error_name = name;
                }
                return error_name;
            }));
            return arr.length > 0 ? arr : false;
        }
    });
});


NewMessageView.js
define([
    'jquery',
    'underscore',
    'backbone',
    'toastr',
    'js/modules/FileUploader/FileUploaderModel',
    'js/modules/FileUploader/FileUploaderCollection',
    'js/modules/FileUploader/FileUploaderCollectionView',
    'js/modules/FileUploaderTemp/FileUploaderTempCollection',
    'js/modules/FileUploaderTemp/FileUploaderTempCollectionView',
    'js/modules/FileUploaderTemp/FileUploaderTempModel',
    'js/app'
], function ($, _, Backbone, toastr,
             FileUploaderModel,
             FileUploaderCollection,
             FileUploaderCollectionView,
             FileUploaderTempCollection,
             FileUploaderTempCollectionView,
             FileUploaderTempModel,
             App) {
    "use strict";
    return Backbone.View.extend({
        tagName: 'div',
        className: 'NewMessage',
        events: {
            'click .TooltipLoaderCloseButton' : 'remove',
            'click .sendMessage' : 'sendMessage',
            'keyup .limited': 'limit_characters',
            'keydown .limited': 'limit_characters'
        },
        initialize: function () {
            this.model.on('validationError', this.showError, this);
            this.render();
            return this;
        },
        render: function () {
            this.template().done(function (html) {
                this.$el.html(html);
                var fileUploaderCollection = new FileUploaderCollection(this.model.get('attach') || [], {
                        model : FileUploaderModel,
                        modelReference : this.model
                    }),
                    fileUploaderCollectionView = new FileUploaderCollectionView({
                        collection : fileUploaderCollection
                    }),
                    fileUploaderTempCollection = new FileUploaderTempCollection([], {
                        model : FileUploaderTempModel
                    }),
                    fileUploaderTempCollectionView = new FileUploaderTempCollectionView({
                        collection : fileUploaderTempCollection,
                        collectionReference : fileUploaderCollection
                    });
                this.$el.find('.fileUploaderTemp').replaceWith(fileUploaderTempCollectionView.$el);
                this.$el.find('.fileUploader').replaceWith(fileUploaderCollectionView.$el);
            }.bind(this));
            return this;
        },
        sendMessage : function(){
            var data = this.serialize(),
                promise = this.model.save(data, {
                    data : JSON.stringify(data),
                    contentType : 'application/json'
                });
            if(promise){ //validation passed
                promise.done(function(){
                    toastr.success('Сообщение успешно отправлено');
                    App.router.navigate('/journal', {trigger : true});
                    this.remove();
                }.bind(this));
            }
            return promise;
        },
        serialize : function(){
            var res = {
                document_number : this.$el.find('input[name=document_number]').val(),
                mail_subject : this.$el.find('input[name=mail_subject]').val(),
                mail_body : this.$el.find('textarea[name=mail_body]').val()
            };
            if(this.model.get('attach').length > 0){
                res.attach = this.model.get('attach');
            }
            return res;
        },
        showError : function(){ //TODO error handling
            console.log(arguments);
        },
        limit_characters: function (event) {
            var res = true,
                target = $(event.currentTarget),
                limit = +target.attr('limit'),
                length = target.val().length;
            if (event.type === 'keydown' && length > limit + 5) {
                res = false;
            } else if (length > limit) {
                event.preventDefault();
                target.val(target.val().substr(0, limit));
            }
            return res;
        }

    });
});


Ну и ещё config.js, app.js, main.js, FileUploader*.js и тому подобное(ещё под ~10 файлов). Зачем оно всё, если к указанному недостатку — не относится, да ещё и описывает конкретную реализацию? Принцип такой что view ничего не должно быть известно об исполняемом окружении, тогда она хорошо будет работать в любом. Я, за ~50 модулей с вью/вью коллекции ещё не встречал АБСОЛЮТНО НЕОБХОДИМЫХ аргументов из-за которых вью не смогла бы работать и падала бы просто при инициализации некорректной.
PS хотя, если вы про селектор по всему DOM — привязываться-то изначально к чему-то всёравно нужно. Вот эту проблему и решает router. Хотя можно было бы и изначально взять роутеру ссылку на .main и на неё ссылаться через this, что бы каждый раз по дому не бегать. Спасибо за совет.
В каждой умной книге пишут, что нужно свое решение создать из n-ого числа отдельных приложений, которые замыкают всю логику в себе (по типу ввод/вывод) и не влияют на окружающий код. Тут это показано на примере. Годнота. Разве, что «беженцы» с мегамозга не оценят.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории