Комментарии 7
>> header-view.js
>> container.appendChild(el);
Плохая практика. Тот кто будет вызывать не должен передавать контейнер а должен сделать
Потому что передача контейнера при инициализации это нестандартная фича. А $el/el есть в backbone всегда.
Вот так выглядит инициализация модулей в Router:
соотвественно все данные(типа ID модели) записываются из роутера, и он — звено, связующее компоненты между собой(модели-вьюхи). Всё что нужно сделать — после инициализации вьюхи вернуть this.
А с подходом container.append(this.el) мы рано или поздно перейдём к $('.body, .ololo').append(this.el) что черевато.
>> 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). Просто не видел смысла приводить полный пример кода ради аргументирования своего мнения по поводу одной строки кода.
Если нужен полный пример — пожалуйста:
Ну и ещё config.js, app.js, main.js, FileUploader*.js и тому подобное(ещё под ~10 файлов). Зачем оно всё, если к указанному недостатку — не относится, да ещё и описывает конкретную реализацию? Принцип такой что view ничего не должно быть известно об исполняемом окружении, тогда она хорошо будет работать в любом. Я, за ~50 модулей с вью/вью коллекции ещё не встречал АБСОЛЮТНО НЕОБХОДИМЫХ аргументов из-за которых вью не смогла бы работать и падала бы просто при инициализации некорректной.
PS хотя, если вы про селектор по всему DOM — привязываться-то изначально к чему-то всёравно нужно. Вот эту проблему и решает router. Хотя можно было бы и изначально взять роутеру ссылку на .main и на неё ссылаться через this, что бы каждый раз по дому не бегать. Спасибо за совет.
Если нужен полный пример — пожалуйста:
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-ого числа отдельных приложений, которые замыкают всю логику в себе (по типу ввод/вывод) и не влияют на окружающий код. Тут это показано на примере. Годнота. Разве, что «беженцы» с мегамозга не оценят.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Фундамент масштабируемости javascript приложения