Backbone.js

6yrop

Вопрос по базовым идеям Backbone.js
 
The general idea is to organize your interface into logical views, backed by models, each of which can be updated independently when the model changes, without having to redraw the page.
...
Backbone is an attempt to discover the minimal set of data-structuring (models and collections) and user interface (views and URLs) primitives...
...
Whenever a UI action causes an attribute of a model to change, the model triggers a "change" event; all the Views that display the model's state can be notified of the change, so that they are able to respond accordingly, re-rendering themselves with the new information.
  

В примере Todos используется underscore template-ы. Понятно, что Backbone как бы не обязывает пользоваться текстовыми темплейтами. Но, тем не менее, в примере используются темплейты для замены html как стринги. Т.е. re-rendering происходит через генерацию новой html строки. Вопрос: чем определяется размер куска страницы, который обновляется через подстановку новой html строки? Например, почему бы весь список Todos не рендерить одним underscore template-ом? Почему этого хочется? Модель программирования становится похожа на аля request->response. Без дополнительных событий и без дополнительного изменяемого состояния. Состояние только в request и response. Известный чувак об этом говорил.
За что мы боремся? Только за пефоманс? А эта проблема реальна? Современные браузеры вроде быстро меняют html.
P.S. На todomvc.com в реализации Backbone неприятно мигает оранжевый квадратик, когда переключаем галочку. В других реализациях такого нет. И понятно почему, заменяем html и фокус теряется.

Hastya

Например, почему бы весь список Todos не рендерить одним underscore template-ом?
потому что удобнее рендерить отдельные строки, а не все целиком

6yrop

потому что удобнее рендерить отдельные строки, а не все целиком
Ты ж не сам рендеришь, а компьютер рендерит. Ты пишешь код. А кода будет ровно столько же.
Возможно, ты имеешь ввиду, что в undescore template плохо с декомпозицией. Так это проблемы конретной реализации движка темплейтов. Можно Razor портировать под JS или TypeScript. Это всё не принципиальные, решаемые проблемы. А вот вопрос смены евентной модели программирования с состоянием на модели запрос/ответ это принципиальный вопрос. Вторая модель сильно проще.

s507040

Вопрос: чем определяется размер куска страницы, который обновляется через подстановку новой html строки?
определяется необходимым функционалом.
когда пользователь хочет от страницы поведения полноценного приложения, хочется писать код, консистентный функционалу: обновлять только то, что нужно обновлять, синхронизировать только меняющиеся данные и прозрачным образом описывать относящиеся к ним шаблоны.
пока publish/subscribe не набрал должной популярности, придется терпеть события, чтобы получить модную отзывчивость интерфейса.

luna89

За что мы боремся? Только за пефоманс? А эта проблема реальна? Современные браузеры вроде быстро меняют html.
P.S. На todomvc.com в реализации Backbone неприятно мигает оранжевый квадратик, когда переключаем галочку. В других реализациях такого нет. И понятно почему, заменяем html и фокус теряется.

Ты сам ответил на свой вопрос.

6yrop

определяется необходимым функционалом.
и только? Вопрос то в том, что один и тот же функционал можно реализовать по разному.

обновлять только то, что нужно обновлять...

почему не обновить бОльший кусок? Как пользователь видит разницу в размере куска, который обновляем?

синхронизировать только меняющиеся данные...
а что значит синхронизировать неменяющиеся данные? :grin:

... прозрачным образом описывать относящиеся к ним шаблоны.

а что кто-то выступает за мутное описание шаблонов? :grin:

пока publish/subscribe не набрал должной популярности, придется терпеть события, чтобы получить модную отзывчивость интерфейса.

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

6yrop

На todomvc.com в реализации Backbone неприятно мигает оранжевый квадратик, когда переключаем галочку. В других реализациях такого нет. И понятно почему, заменяем html и фокус теряется.
Ты сам ответил на свой вопрос.
Ты ж вроде за Backbone? А так придется от него отказываться?
Что касается фокуса, то его можно восстанавливать. Но неприятный нюанс, да.

luna89

Ты ж вроде за Backbone? А так придется от него отказываться?
В бэкбоне нет датабиндинга. Ботай ember.js,там в шаблоне меняется только тот кусочек, который изменился.

s507040

а что кто-то выступает за мутное описание шаблонов? :grin:
Backbone предлагает простой способ декомпозиции интерфейса: каждому элементу, представляющему данные --- отдельная модель, отдельный шаблон и отдельная коллекция.
зачем следовать этому принципу? чтобы минимизировать обновления DOM и повторные навешивания обработчиков на перерендеренные элементы.
в демо можно было бы весь лист Todos рендерить на каждый чих, однако в реальной жизни элементов в коллекции может быть много, обновлять целую коллекцию ради одного измененного Todo может стать накладно, каждый шаблон Todo может содержать элементы с обработчиками, которые перестанут работать после того, как элемент будет заново добавлен в DOM и пр.
вообще, Backbone мне не особо понравился, все та же steaming pile of good intentions (с AngularJS, например, более няшен.

6yrop

чтобы минимизировать обновления DOM и повторные навешивания обработчиков на перерендеренные элементы.
в демо можно было бы весь лист Todos рендерить на каждый чих, однако в реальной жизни элементов в коллекции может быть много, обновлять целую коллекцию ради одного измененного Todo может стать накладно, каждый шаблон
Там есть кнопка проставить галочку на все todo. При этом будет вставлен html для каждого todo, т.е. 100 раз будем напрягать браузер, если у нас 100 элементов в списке. Если мы отдадим браузеру сразу один большой кусок, то у него появится возможность всё сделать сразу, и это будет быстрее. Это я к чему, для разных сценариев разная стратегия оптимизации рендеринга. А надо ли заниматься такой оптимизацией?

6yrop

весь лист Todos рендерить на каждый чих
В версии non-framework implementation jQuery так и делается, перерендеривается почти всё. И сорци выглядят проще всех :grin:
http://todomvc.com/architecture-examples/jquery/
На списке из 100 элементов на машине core i7 в Chrome/IE9 задержек не заметно.

stm6692945

внезапно часто на работе сталкиваюсь с Backbone.
И скажу что обновление всей страницы на мой взгляд плохая затея.
Вот к примеру есть форма с кучай полей, и есть несколько полей на изменения которых необходимо обновить локальный темплайт (в твоей случае весь)
Так вот если весь то придется обойти все поля и собрать значения с них. А после зарендерить с ними. Кучу ненужных и лишних действий.
Но ты еще не забывай про события. Будем менять поля А, а после перерендерить всю страницу.
Ебать тут ненужные события на поля посыпятся. И если форма простая то это еще ничо. Но когда у тя на форму будет привязна хуява туча аяксов, то проблемы начнут появляться

6yrop

Так вот если весь то придется обойти все поля и собрать значения с них. А после зарендерить с ними. Кучу ненужных и лишних действий.
В смысле лишние действия, лишний код ты хотел сказать? А код такой уже есть, и вот почему. Мы же создаем как-то страницу по данным. Остается только вопрос, что подаем на вход алгоритму, который создает страницу (или кусок страницы). Чаще всего данные со страницы надо отправлять обратно на сервер, поэтому код, собирающий данные со страницы, будет присутствовать. В редких случаях, когда какие-то данные не собираются для отправки на сервер, тогда на вход алгоритму можно подать текущий DOM. В таком случае надо будет прописать делегирование от текущего DOM-а. Это, да, дополнительный код. Но его либо вообще не будет, либо будет мало. Поэтому это несравнимая мелочь по сравнению с тем, когда мы полагаемся на run time состояние при написании кода. Фактически мы прописываем явные зависимости, а так они неявные и плохо прослеживаются по коду.
Ебать тут ненужные события на поля посыпятся.

В том то и фишка, что при таком подходе мы избавляемся от ненужных событий. Их просто нет.
Кстати вот. Компания ThoughtWorks (в ней работает Мартин Фаулер) периодически выпускает Technology Radar:
http://thoughtworks.fileburst.com/assets/technology-radar-oc...
Backbone.js is a great example of an abstraction pushed
too far. While we initially liked the ease of wire-up, in
practice it suffers from the same issues as all such databound frameworks from WebForms to client/server tools. We
find that it blurs the framework and model too much, forcing
either bad architectural decisions or elaborate framework
hackery in order to preserve sanity
.

6yrop

Backbone.js is a great example of an abstraction pushed
too far.
Кстати, если бы язык позволял находить связи в исходном коде, то выбор оптимального уровня абстракции превращается в простую задачу

marat7256

Хорошо, когда программы пишутся для людей.
Плохо, когда программы пишутся ради красоты программирования.

6yrop

Хорошо, когда программы пишутся для людей.
Плохо, когда программы пишутся ради красоты программирования.
Ты про каких людей, про пользователей или программистов? Как пользователь видит разницу? Программистам проще работать с кодом.

luna89

 
И скажу что обновление всей страницы на мой взгляд плохая затея.
Вот к примеру есть форма с кучай полей, и есть несколько полей на изменения которых необходимо обновить локальный темплайт (в твоей случае весь)
Так вот если весь то придется обойти все поля и собрать значения с них. А после зарендерить с ними. Кучу ненужных и лишних действий.
Но ты еще не забывай про события. Будем менять поля А, а после перерендерить всю страницу.
Ебать тут ненужные события на поля посыпятся. И если форма простая то это еще ничо. Но когда у тя на форму будет привязна хуява туча аяксов, то проблемы начнут появляться

Смотри тут абзац Auto-Updating Handlebars Templates
Реально интересно, есть ли что-то подобное вообще в каком-нибудь неджаваскриптовом фреймворке, необязательно браузерном даже.

6yrop

Реально интересно, есть ли что-то подобное вообще в каком-нибудь неджаваскриптовом фреймворке, необязательно браузерном даже.
Как идея есть в WPF. Но реализация полное говно. Собственно, по Hello World и про Ember.js судить сложно.

6yrop

Auto-Updating Handlebars Templates
Это накладывает сильные требования на язык темплейтов. Без этого требования темплейты можно реализовывать как простую смесь языка общего назначения и строк. Между ними просто разделители <% %> или более красиво @ в Razor. А так у тебя язык темплейтов становится декларативным, последствия понятны.

6yrop

в каком-нибудь неджаваскриптовом фреймворке, необязательно браузерном даж
на самом деле в браузере есть нюанс, которого почти ни где нет! Это быстрый:
 
el.html(myString)

Остальные UI платформы изначально ООП на всю голову.

luna89

на самом деле в браузере есть нюанс, которого почти ни где нет! Это быстрый:
Ты не понял, в ember.js шаблон целиком не перерендеривается.

6yrop

Auto-Updating Handlebars Templates
О чем вообще разговор, если в этом Handlebars if-ы через жопу http://stackoverflow.com/q/8853396/2724979

luna89

О чем вообще разговор, если в этом Handlebars if-ы через жопу http://stackoverflow.com/q/8853396/2724979
Вставлять логические выражения в шаблон - само по себе говнокод. Учитывая, что такой шаблон еще и верстальщики будут бояться править, можно сделать вывод, что подход handlebars - абсолютно правильный. В коде пишешь что-то типа:

class FooView extends View
...
templateHelpers: ->
...
complexCondition: @model.simpleCondition1 and @model.simpleCondition2

а потом просто в шаблоне используешь complexCondition

6yrop

Ты не понял, в ember.js шаблон целиком не перерендеривается.
Я то всё понял. А вот ты, наверное, не совсем о чем я, ну да ладно.

6yrop

Смотри тут абзац Auto-Updating Handlebars Templates
я тут попробовал можно перерендоривать весь todo лист по каждому keypress, пользователь ничего не замечает.
на 100 todo видно задержки

6yrop

Между ними просто разделители <% %> или более красиво @ в Razor
Нормальные темплейты постепенно переползут в стандарт js http://wiki.ecmascript.org/doku.php?id=harmony:quasis

luna89

Есть в кофескрипте уже, но имхо ненужно.

6yrop

Есть в кофескрипте уже
могу ошибаться, но вроде там нет сценариев call и callback для шаблонов, т.е. вызов шаблона из шаблона и передача шаблона как аргумент в шаблон с произвольными параметрами. А это базовые элементарные средства декомпозиции любых задач, в том числе и задачи темплетирования.

6yrop

Backbone предлагает простой способ декомпозиции интерфейса: каждому элементу, представляющему данные --- отдельная модель, отдельный шаблон и отдельная коллекция.
зачем следовать этому принципу? чтобы минимизировать обновления DOM и повторные навешивания обработчиков на перерендеренные элементы.
в демо можно было бы весь лист Todos рендерить на каждый чих, однако в реальной жизни элементов в коллекции может быть много, обновлять целую коллекцию ради одного измененного Todo может стать накладно, каждый шаблон Todo может содержать элементы с обработчиками, которые перестанут работать после того, как элемент будет заново добавлен в DOM и пр.
вообще, Backbone мне не особо понравился, все та же steaming pile of good intentions (с AngularJS, например, более няшен.
Мой отчет по фрайворкам такой. На примере из todomvc.com
Backbone.js ну это полный пиздец. Даже не хочу обосновывать почему. Просто сплошная essential complexity. К тому же в примере из todomvc.com всё сильно тормозит в сравнении с другими решениями. Я тестил на 10000 todo. Рекомендую, всем кто на нем сидит, скорее спрыгивать с него, вы тупо проебываете время.
Далее декларативные фрейворки.
AngularJS современный хипстерский фрайворк. Объем получаемого кода, да, небольшой. Но вот бага на todomvc.com есть. В IE когда по ентеру заканчиваем редактирование todo, то текстовое поле исчезает, но мигающий курсор остается видимым. ;) Причем, можно вводить текст и он сохраняется. :grin: Починить не пытался, но в код посмотрел, там вроде бы прописано:
 
В html стоит <input todo-focus="todo == editedTodo">
в js $scope.doneEditing = function (todo) {
$scope.editedTodo = null;

Но что-то не работает. ;) Попробуйте починить.
Ember.js тоже есть бага в IE. Довольно хитрая. Сначала заводим несколько todo. Потом закрываем страницу. Открываем todomvc.com и кликаем по ссылке Ember.js. Двойной клик на todo, и, опа, курсор не становится на текстовое поле, а должен.
Думаю, фиксить вот такие мелочные баги в декларативных фрейворках довольно сложно, и, скорее всего, вся декларативная красота пойдет лесом.
Как писать UI правильно напишу, как приду домой.

Hastya

Backbone.js ну это полный пиздец. Даже не хочу обосновывать почему. Просто сплошная essential complexity. К тому же в примере из todomvc.com всё сильно тормозит в сравнении с другими решениями. Я тестил на 10000 todo. Рекомендую, всем кто на нем сидит, скорее спрыгивать с него, вы тупо проебываете время.
Так все же обоснуй. В backbone нет рендеринга, пример todo не имеет никакого отношения к его производительности. backbone - это голый скелет с управлением событиями.

6yrop

Пока просто оставлю ссылку. Комментарии будут позже.
В Хроме поставьте галочку Enable JS source maps, чтобы увидеть исходный код на TypeScript.

6yrop

вот полезно почитать http://javascript.ru/optimize/antimvc

6yrop

Так все же обоснуй. В backbone нет рендеринга, пример todo не имеет никакого отношения к его производительности. backbone - это голый скелет с управлением событиями.
Зачем нужны хрени которые создаются вот этой функцией Backbone.Model.extend? В них же куча mutable состояния, и еще свои события. Это же дорого, за что мы платим? Пока видно только отслеживание изменений, но это пустячная фича (которую можно реализовывать разными способами, а тут всего лишь один из вариантов, я хочу иметь возможность выбрать наиболее подходящий к моим условиям).

luna89

Пока просто оставлю ссылку. Комментарии будут позже.
Я в таком же стиле писал, когда начинал заниматься фронтендом. Потом отказался от такого подхода, когда не смог написать в таком стиле небольшую формочку строк на 700, в которой изменения в одних контролах должны были отображаться на других контролах. При переписывании с использованием MVC код стал тривиальным.
У тебя проблема в том, что модель неявно хранится в DOM, поэтому тебе приходится писать парные функции для записи в дом и для экстракции данных из дома. В случае MVC достаточно только первой функции из этой пары. Если например дизайнер решит что для чека тудушек будет использоваться не чекбокс а кастомный контрол, то тебе придется в 9 местах менять код. Или например тудушки показываются пейджингом по in-memory списку - тогда у тебя не будет дивов для хранения TODO. Или для переупорядочивания todo будет использоваться drag and drop, при драге див детачится от контейнера и функция $todos при этом работает некорректно.
Функция calcSharedAreas используется в 7 местах. Тебе приходится париться над обновлением dom при обрабаботке всех событий. В MVC ты просто оперируешь моделью данных, которая прибинжена к вьюхе.
Затем, у тебя функция setChecked должна знать обо всех местах, которые заинтересованы в выборе тудушки. Если использовать события, то ты просто триггеришь событие (точнее оно триггерится само при присваивании атрибута checked) и в нужных местах подписываешься на это событие.
Также, на мой взгляд, ты злоупотребляешь анонимными функциями.

6yrop

Я в таком же стиле писал, когда начинал заниматься фронтендом. Потом отказался от такого подхода, когда не смог написать в таком стиле небольшую формочку строк на 700, в которой изменения в одних контролах должны были отображаться на других контролах.
Это от того, что у тебя не было точной и надежной навигации по коду. В продемонстрированном мною подходе общее количество строк кода не является величиной, показывающей, сможет программист написать форму или не сможет. В моём подходе у программиста есть возможность вырезать ломтик (slice) из исходного кода, который соответствует той точке интереса, которой он занят в данный момент. Поэтому критичен не объем всего кода, а лишь объем ломтика.
 
У тебя проблема в том, что модель неявно хранится в DOM

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

В Backbone.js всё это тоже надо писать и плюс к этому надо писать прокидвание событий и подписывание на события. Т.е. в Backbone.js писать надо больше.
 
Если например дизайнер решит что для чека тудушек будет использоваться не чекбокс а кастомный контрол, то тебе придется в 9 местах менять код.

Обычно "красивые" контролы пишут так, что для js кода всё останется без изменений. Что там поменяется? У чекбокса не будет свойства .prop('checked') или события .change?
И опять-таки количество мест не является критичным показателем, если у тебя есть точная и надежная навигация. Ты все места находишь, встав на id-шник чекбокса и нажав одну кнопку.
 
Или например тудушки показываются пейджингом по in-memory списку - тогда у тебя не будет дивов для хранения TODO.

Не вижу проблемы показа одной страницы. Сейчас же показываются Active или Complited. Суть те же страницы.
 
Или для переупорядочивания todo будет использоваться drag and drop, при драге див детачится от контейнера и функция $todos при этом работает некорректно.

Надо смотреть подробнее детали, либо не удалять из контейнера, либо дополнить функцию $todos.
 
Тебе приходится париться над обновлением dom при обрабаботке всех событий. В MVC ты просто оперируешь моделью данных, которая прибинжена к вьюхе.

В Backbone.js тебе приходится делать всё это и еще больше.
 
Затем, у тебя функция setChecked должна знать обо всех местах, которые заинтересованы в выборе тудушки.

Ты как-то странно формулируешь, видимо, не понимаешь. Как это функция о чем-то должна знать? Звучит странно.
 
точнее оно триггерится само при присваивании атрибута checked

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

Что конкретно тебя смущает в анонимных функциях? Если функции дать имя, то всё будет ок? Или тебя смущают замыкания? Замыкания, да, намеренно используются, это еще один клевый момент.
В моем коде просматривается общий принцип. Есть функции с префиксом calc, которые вычисляют состояние UI элементов, и они просто дергаются из событий, от которых зависит вычисление. Плясать надо как раз не от событий, а от вот этих функций calc*. А потом их вызовы просто прописывать в событиях. Вызовы прописываются в событиях тех элементов, которые задействованы в вычислении. Например, если в функции calcSome мы обращаемся к значению в чекбоксе checkBox1, то мы должны просто дернуть calcSome в checkBox1.change, а так же, там где мы меняем checkBox1 программно. Это правило сугубо формально. Т.е. логику приложения ты прописываешь в функциях cal*, а потом уже не думаешь о бизнес логике и просто применяешь формальное правило (естественно, навигируясь по коду).
С гуманистической точки зрения применение формального правила надо бы поручить машине, но пока это заканчивается декларативными фрейворками с сильно ограниченными способами описания вычислений по сравнению с императивным стилем. Похожую проблему я решал в проекте Reactive Relation, там удалось совместить декларативный и императивный стиль. Можно попробовать что-то такое сделать и для UI-я, но ручное соблюдение описанного формального правила не является сильно большой проблемой.

6yrop

У тебя проблема в том, что модель неявно хранится в DOM
Ты по ссылке выше прочитал?
 
Controller пусть остается отдельно, а Model от View отделять не надо. Особенности Javascript/DOM/CSS позволяют успешно реализовывать аспекты Model средствами View.
...
Так зачем нам поддерживать иерархию, средства выборки и контейнеры для свойств в специальных javascript-объектах Модели, если с этим замечательно справляется DOM/View ?
...
Аналогично, в javascript-дереве это может быть единый объект Tree, который манипулирует узлами, а сами узлы - просто элементы DOM и хранят данные непосредственно в DOM.
...
Заключение
В этой статье содержатся те мысли и подходы, которые отличают профессионального JS-программиста от обычного десктоп-кодера, подсевшего на javascript.
Их применение ведет к уменьшению и упрощению кода. Успехов.
http://javascript.ru/optimize/antimvc
  

luna89

В Backbone.js всё это тоже надо писать и плюс к этому надо писать прокидвание событий и подписывание на события. Т.е. в Backbone.js писать надо больше.
В бэкбоне не надо экстрагировать данные из дома.
надо писать прокидывание событий и подписывание на события.
Бэкбон дерьмовый, не имеет смысла его с чем-то сравнивать. Но даже в бэкбоне подписка на события - это что-то типа

class MyView extends View
@listenToModel 'change:foo', (value) -> console.log 'foo changed '+val

Метод listenToModel реализуется в две-три строчки.
Надо смотреть подробнее детали, либо не удалять из контейнера, либо дополнить функцию $todos.

При усложнении приложения у тебя адские костыли появятся. Представь что у тебя есть два представления списка - краткое и полное. Они могут показываться одновременно. Как ты это закодируешь?

6yrop

В бэкбоне не надо экстрагировать данные из дома.
вот из примера с сайта backbonejs.org
 
 

// Re-render the titles of the todo item.
render: function {
this.$el.html(this.template(this.model.toJSON;
this.$el.toggleClass('done', this.model.get('done';
this.input = this.$('.edit');
return this;
},

// Close the `"editing"` mode, saving changes to the todo.
close: function {
var value = this.input.val;
if (!value) {
this.clear;
} else {
this.model.save({title: value});
this.$el.removeClass("editing");
}
},

// If you hit `enter`, we're through editing the item.
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close;
},

Строчки 22, 11, 5, 15 переливают данные из DOM в модель. О чем ты вообще говоришь? :confused:
Причем такой треш, input присваивается в функции render :confused: , а используется в close. И это в первом примере на официальном сайте проекта. На todomvc.com этот треш скопишащен также.
 
Бэкбон дерьмовый, не имеет смысла его с чем-то сравнивать.

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

Метод listenToModel реализуется в две-три строчки.
 
При усложнении приложения у тебя адские костыли появятся. Представь что у тебя есть два представления списка - краткое и полное. Они могут показываться одновременно. Как ты это закодируешь?
 

В событиях нет ни какой магии. События это просто callback-и (точнее массив callback-ов). Их надо и вызывать и определять. Там есть закон сохранения, введя события, ты не уменьшишь количество операций, которое надо прописывать. Это ж очевидные вещи, азы программирования так сказать, чему там вас в университете учили? :confused:
 

При усложнении приложения у тебя адские костыли появятся. Представь что у тебя есть два представления списка - краткое и полное. Они могут показываться одновременно. Как ты это закодируешь?
 

Напишу в таком же стиле как выше. Будет всё ясно и прозрачно, без костылей. Можем перейти к делу. Ты выкладываешь пример на своем любимом фрайворке. Я это же реализую в своем стиле.

6yrop

Представь что у тебя есть два представления списка - краткое и полное. Они могут показываться одновременно. Как ты это закодируешь?
Давай посмотрим как вам "помогает" ваша модель. Начнем с двух text box-ов.
http://todos.ucoz.ru/index2.html
Текст можно вводит в любой, и значение появляется в обоих text box-ах. Вот весь код:
  
<!DOCTYPE html>
<html>
<head>
<title>title</title>
</head>
<body>
<input id="id1" />
<input id="id2" />
</body>
<script src="jquery.js"></script>
<script>
$(function {
function addListener(selector1, selector2) {
$(selector1).bind('keyup keypress keydown', function {
$(selector2).val($(this).val;
});
}
addListener('#id1', '#id2');
addListener('#id2', '#id1');
});
</script>
</html>

Запости свой вариант через модель. ;)

Ivan826

для понимания идеи надо мыслить не двумя текстареями, а несколькими десятками шаблонов и событий

6yrop

для понимания идеи надо мыслить не двумя текстареями, а несколькими десятками шаблонов и событий
выкладывай source code

6yrop

и я покажу тебе как идеи, на которых базируется мой код, работают на десятка/сотнях шаблонов и событий.

6yrop

для понимания идеи надо мыслить не двумя текстареями, а несколькими десятками шаблонов и событий
а так ты, наверное, не понял, концепция с вашей "моделью" проваливается на примере из двух текст боксах. У вас будет так. Вы подпишитесь на изменение текстбокса, в этой функции будете выставлять атрибут/свойство модели, модель бросит событие changed, вы на него тоже подпишитесь, и в этом обработчике будете выставлять ? да что вы будете выставлять? выставлять значение в каком из двух полей? Здесь вам нужно знать контекст исходного события, нужно знать какой текст бокс бросил событие, а вы протащили это через события вашей идеологии и потеряли часть контекста. Фиксить вы это будете через введение филда в ваше вью, костыль в явном виде. На глазах нарастание accidental complexity.
P.S. это не относится к ангуляру и остальным декларативным фрейворкам, там есть возможность отслеживать циклические ссылки внутри фрейворка.

luna89

Ты выкладываешь пример на своем любимом фрайворке. Я это же реализую в своем стиле.
http://jsfiddle.net/zEHU7/
У меня кстати возникла гипотеза, почему ты так сильно настроен против MVC фреймворков. Подозреваю, что в MS сделали биндинги к jquery, а на биндинги к фреймворкам запала не хватило. Да и, скорее всего, в тайпскрипте недостаточно сильная система типов чтобы сделать нормальные типизированные биндинги, а без них тайпскрипт - это маргинальный кофескрипт. В результате в микрософте объявили что фреймвоки нинужны (повторяю, что это моя гипотеза).

6yrop

http://jsfiddle.net/zEHU7/
ну я же написал выше, декларативные фрайворки в состоянии отслеживать циклические зависимости. Ты сделай это на событиях с применением MVC. Имхо, ты смешиваешь совсем разные понятия декларативный бандинг и MVC.
 

У меня кстати возникла гипотеза, почему ты так сильно настроен против MVC фреймворков. Подозреваю, что в MS сделали биндинги к jquery
 

Мимо. Если ты посмотришь в мой ts код, то там в самом начале стоит "declare var $: any;", т.е. я использую JQuery без типов. Работа с JQuery это чистый js. А вот логику своего приложения я пишу сам с самого начала, поэтому могу сделать типизированно. В этом и есть прелесть TS, использование либы остается ровно таким же js-ом, а логика твоего приложения получает плюшки статической типизации: навигацию и .т.д.
В компиляторах с других языков: GWT, ScriptSharp, Kotlin и т.д. надо прописывать дефенишены к сторонним либам. В TS говоришь any и фигачишь обычный js. "TypeScript начинается в JavaScript и заканчивается JavaScript-ом" (с). :) Спасибо, Хельсбергу. :)

6yrop

против MVC фреймворков
Еще отмечу. Мой код ни как не противоречит MVC. Отсутствие специальных js-объектов для модели не противоречит MVC. Необходимые js-объекты могут быть введены, когда понадобятся. Просто они не нужны, когда нет на то оснований. Их отсутствие к стилю программирования тоже не имеет отношение. Я лишь показал простой способ написания UI-я. Я продемонстрировал простой код для задачи todos. Теперь пусть разработчики всяких фреворков показывают, зачем пользоваться их либами? На сайтах лишь маркетинговые бул шиты.

6yrop

http://jsfiddle.net/zEHU7/
Кстати, а почему тебе ангуляр не нравиться? Это же сейчас мейнстрим. Пиар и финансирование от google. А разработчики Ember не в столь комфортных условиях, проект может загнуться, если разработчики потеряют интерес, кстати, их предыдущий фрайворк был странным.

6yrop

Вот читаю я про ваши эти фрайворки AngularJS vs Ember. Ну смешно права, как дети малые:

Over time, in a large software project, you will inevitably want to replace something that was previously a primitive with a method.

заменить свойство на метод у вас в списке сравнения UI фрайворком. :confused:

luna89

При усложнении приложения у тебя адские костыли появятся. Представь что у тебя есть два представления списка - краткое и полное. Они могут показываться одновременно. Как ты это закодируешь?
..
Напишу в таком же стиле как выше. Будет всё ясно и прозрачно, без костылей. Можем перейти к делу. Ты выкладываешь пример на своем любимом фрайворке. Я это же реализую в своем стиле.
Мое решение - взять todo mvc на эмбере и попросить верстальщика, чтобы он отредактировал шаблон, так чтобы рядом два списка показывалось, а не один. Ждем твое решение. Еще можешь модифицировать пример с двумя текстинпутами, чтобы их там было не два а три.

6yrop

Мое решение - взять todo mvc на эмбере и попросить верстальщика, чтобы он отредактировал шаблон, так чтобы рядом два списка показывалось, а не один. Ждем твое решение.
Моё решение, попросить js кодера, чтобы он отредактировал код.

6yrop

Еще можешь модифицировать пример с двумя текстинпутами, чтобы их там было не два а три.
 
 
<html>
<head>
<title>title</title>
</head>
<body>
<input id="id1" />
<input id="id2" />
<input id="id3" />
</body>
<script src="~/bower_components/jquery/jquery.js"></script>
<script>
$(function {
function addListener(selector1, selector2, selector3) {
$(selector1).bind('keyup keypress keydown', function {
$(selector2).val($(this).val;
$(selector3).val($(this).val;
});
}
addListener('#id1', '#id2', '#id3');
addListener('#id2', '#id1', '#id3');
addListener('#id3', '#id1', '#id2');
});
</script>
</html>

6yrop

http://jsfiddle.net/zEHU7/
http://jsfiddle.net/zEHU7/13/
Двоечки не появляются в вычисляемом поле. Вот весь ваш ембер сдулся.

6yrop

Мое решение
попробовал, твое решение не рабочее

luna89

01	<html>
02 <head>
03 <title>title</title>
04 </head>
05 <body>
06 <input id="id1" />
07 <input id="id2" />
08 <input id="id3" />
09 </body>
10 <script src="~/bower_components/jquery/jquery.js"></script>
11 <script>
12 $(function {
13 function addListener(selector1, selector2, selector3) {
14 $(selector1).bind('keyup keypress keydown', function {
15 $(selector2).val($(this).val;
16 $(selector3).val($(this).val;
17 });
18 }
19 addListener('#id1', '#id2', '#id3');
20 addListener('#id2', '#id1', '#id3');
21 addListener('#id3', '#id1', '#id2');
22 });
23 </script>
24 </html>

Два чаю тому, кто сумеет сгенерить такой говнокод плюсовыми темплейтами.для произвольного n.

6yrop

эмбере
Ситуация такая. В эмбере есть Computed Propert, ты либо сам прописываешь зависимости явно, либо используешь специальные функции для задания вычисления. Второй вариант с глюками, см. пост выше. Так вот мой стиль это прописывать всегда явно зависимостей. Делаешь функцию calc*, смотришь на нее и прописываешь ее вызов в событиях тех элементов, которые используются в вычислении. Не вижу большой проблемы всегда прописывать зависимости. В том числе зависимости от непосредственно UI элементов. Эмбер автоматически определяет зависимости от UI элементов, ну хорошо, молодец, но тащить его в проект из-за этого я бы не стал. Я спрашивал у 1С-сников они в финансовых расчетах явно зависимости прописывают, и ничего как-то живут. В базах данных тоже не всё через материализованные вью удается реализовать, и ничего прописывают зависимости для пересчета явно. Не big deal.

6yrop

произвольного n.
откуда произвольный n? Если в задаче будет произвольный n, код перепишется через цикл. Не надо писать общее решение, когда требуется частное.

6yrop

Давай реальные сценарии рассматривать. Подходит к тебе аналитик и говорит: "Слушай, есть желание из галочки complited сделать элемент с тремя состояниями. Посмотри в код и скажи, какая логика завязана на эту галочку? И мы с тобой проверим насколько хорошо три состояния впишутся в наше приложение.". Ты открываешь код, находишь html галочки, встаешь на ее id/class Const.toggle. Запускаешь поиск. Поиск навигирует тебя на строку 84. Запускаешь поиск. Поиск выдает строки
37
50
92
97
103
105
139
156
Далее идем по результату поиска и отвечаем аналитику.
37 Логика под кнопкой clear_completed (внизу списка) завязана на этот флажок.
50 Сам флажок.
92 setChecked. Запускаем поиск 24 и 49.
24 Логика под галочкой toggle_all.
49 Восстановление галочки при загрузке галочки. Это для аналитика не существенно.
97 calcCompleted Стиль отображения элемента зависит от этой галочки.
103, 105 calchVisible Видимость элемента зависит от этой галочки. Фильтры.
139 Сохранение. Это для аналитика не существенно.
156 getActiveTodoCount Запускаем поиск. Переменная activeTodoCount сразу подсвечивается в строках
124 Состояние переключателя toggle_all зависит от галочки.
126 Подсвечивается переменная completedTodoCount
130 Логика значения CompletedCount внизу списка.
131 Видимость кнопки clear_completed внизу списка.
128 Логика значения LeftTodoCount внизу списка.
129 Логика значения PluralizeItem внизу списка.
Таким образом, мы в режиме реального времени рассказали аналитику всю бизнес-логику, в которой участвует галочка. Проделай тоже самое на Emder коде.
 
 
declare var $: any;

module App {
var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
var storageKey = 'todos-min-mut-state-typescript-20131028';
interface ITodo {
title: string;
completed: boolean
}
export function main {
fromStorage.forEach(addTodo);
$('#' + Const.new_todo).keyup(function (e) {
if (e.which !== ENTER_KEY) return;
var val = processValue($(this).val;
if (!val) return;
addTodo({ title: val, completed: false });
$(this).val('');
calcSharedAreasAndSave;
});
$('#' + Const.toggle_all).change(function {
var checked = $(this).prop('checked');
$todos.each(function {
setChecked($(this checked);
});
calcSharedAreasAndSave;
});
$(window).bind('hashchange', function {
$todos.each(function {
calchVisible($(this;
});
calcFilterState;
});
$('#' + Const.clear_completed).click(function {
$todos.each(function {
var item = $(this);
if (todo(item).toggle.prop('checked'
item.remove;
});
calcSharedAreasAndSave;
});
calcFilterState;
calcSharedAreas;
}
function addTodo(data: ITodo) {
var item = $('#' + Const.TodoTemplate).clone;
todo(item).title.text(data.title);
todo(item).edit.val(data.title);
setChecked(item, data.completed);
todo(item).toggle.change(function {
calcCompleted(item);
calchVisible(item);
calcSharedAreasAndSave;
});
todo(item).title.dblclick(function {
var edit = todo(item.addClass(Const.editing.edit;
edit.val(edit.val.focus;
});
todo(item).edit.keyup(function(event) {
if (event.which === ENTER_KEY)
event.target.blur;
else if (event.which === ESCAPE_KEY) {
todo(item).edit.val(todo(item).title.text;
event.target.blur;
}
}).blur(function {
item.removeClass(Const.editing);
var newVal = processValue($(this).val;
if (newVal)
todo(item).title.text(newVal);
else
item.remove;
calcSharedAreasAndSave;
});
todo(item).destroy.click(function {
item.remove;
calcSharedAreasAndSave;
});
item.appendTo('#' + Const.todo_list);
}
function todo(item) {
return Util.tag(item, => {
return {
toggle: item.find('.' + Const.toggle
title: item.find('.' + Const.title
destroy: item.find('.' + Const.destroy
edit: item.find('.' + Const.edit)
};
});
}
function setChecked(item, val: boolean) {
todo(item).toggle.prop('checked', val);
calcCompleted(item);
calchVisible(item);
}
function calcCompleted(item) {
item.toggleClass(Const.completed, todo(item).toggle.prop('checked';
}
function calchVisible(item) {
var hash = window.location.hash;
var show;
if (hash === '#' + Const.active)
show = !todo(item).toggle.prop('checked');
else if (hash === '#' + Const.completed)
show = todo(item).toggle.prop('checked');
else
show = true;
item.toggle(show);
}
function calcFilterState {
$('#' + Const.filters).children.each(function {
var hash = window.location.hash;
var filteItem = $(this).find('[href]');
var selected;
if (hash === '#' + Const.active || hash === '#' + Const.completed)
selected = filteItem.attr('href') === hash;
else
selected = filteItem.attr('href') === '#';
filteItem.toggleClass(Const.selected, selected);
});
}
function calcSharedAreas {
var activeTodoCount = getActiveTodoCount;
$('#' + Const.toggle_all).prop('checked', activeTodoCount === 0);
$('#' + Const.main).toggle($todos.length > 0);
var completedTodoCount = $todos.length - activeTodoCount;
var footer = $('#' + Const.footer);
footer.find('#' + Const.LeftTodoCount).text(activeTodoCount);
footer.find('#' + Const.PluralizeItem).text(pluralize(activeTodoCount, 'item';
footer.find('#' + Const.CompletedCount).text(completedTodoCount);
footer.find('#' + Const.clear_completed).toggle(completedTodoCount > 0);
footer.toggle($todos.length > 0);
}
function calcSharedAreasAndSave {
calcSharedAreas;
toStorage(Util.map($todos item => {
return {
title: todo($(item.title.text
completed: todo($(item.toggle.prop('checked')
};
};
}
function toStorage(data: ITodo[]) {
localStorage.setItem(storageKey, JSON.stringify(data;
}
function fromStorage: ITodo[] {
var storeItem = localStorage.getItem(storageKey);
return storeItem !== null ? JSON.parse(storeItem) : [];
}
function $todos {
return $('#' + Const.todo_list).children;
}
function getActiveTodoCount {
var count = 0;
$todos.each(function {
if (!todo($(this.toggle.prop('checked'
count++;
});
return count;
}
function pluralize(count: number, word: string) {
return count === 1 ? word : word + 's';
}
function processValue(val: string) {
return $.trim(val);
}
}

$(App.main);

6yrop

взять todo mvc
Еще раз, декларативность вычислений не путай с MVC. Декларативность вычислений могла быть и на самом DOM-е. Так, например, в QML:
 
QML extends a standards-compliant JavaScript engine, so any valid JavaScript expression can be used as a property binding. Bindings can access object properties, make function calls, and even use built-in JavaScript objects like Date and Math.
Example:
 Rectangle {
     function calculateMyHeight {
     return Math.max(otherItem.height, thirdItem.height);
     }
     anchors.centerIn: parent
     width: Math.min(otherItem.width, 10)
     height: calculateMyHeight
     color: { if (width > 10) "blue"; else "red" }
 }
  

Декларативность вычислений можно строить для разных форм данных.

luna89

Двоечки не появляются в вычисляемом поле. Вот весь ваш ембер сдулся.
Ты действительно не понимаешь почему этот код не работает или это такой полемический прием? Обычный массив в джаваскрипте не уведомляет о своих изменениях, надо пользоваться Ember.A. На практике неудобств это не доставляет, так как ты сам этот массив не инициализируешь.

luna89

Кстати, понял почему ты так любишь find usages. У тебя на 140 строк кода 7 вызовов одной функции. Название функции невнятное - calcSharedAreasAndSave. Если посмотреть на название без контекста, можно предположить, что это расчет налога на земли находящиеся в долевой собственности. Если в проекте много функций с загадочными названиями, которые повторяются в десятках мест, то без решарпера никуда.

6yrop

Если в проекте много функций с загадочными названиями, которые повторяются в десятках мест, то без решарпера никуда.
еще раз, без декларативного описания количество вызовов функции ты не уменьшишь. Декларативные фрейворки еще очень молоды. Посмотрим, где декларативные фрейворки будут года через 3. :smirk: Думаю, их судьба аналогична GWT.

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

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

luna89

еще раз, без декларативного описания количество вызовов функции ты не уменьшишь. Декларативные фрейворки еще очень молоды. Посмотрим, где декларативные фрейворки будут года через 3. :smirk: Думаю, их судьба аналогична GWT.
Ты осознааешь, что 90% нужного на практике датабиндинга реализуется поверх бэкбона в 500 строк кода? На гитхабе десятки таких библиотек. Я в свое время тоже приложил руку к созданию велосипедной надстройки над бэкбоном. Эти декларативные фреймворки - не какая-то новая технология, а хрень, которую каждый более-менее продвинутый js-кодер имплементировал несколько лет назад, потому что перед этим написал кучу говнокода в твоем стиле и пришел к выводу, что такой код не скейлится.

luna89

Да что говорить, даже в джаве для свинга был какой-то датабиндинг, несмотря на абсолютно суконный язык.

6yrop

был
именно :smirk:

6yrop

На гитхабе десятки таких библиотек
Большое количество говорит о том, что есть желание и большое количество попыток. Если было бы решение, а не попытки, то все бы давно успокоились и просто применяли.

6yrop

пришел к выводу, что такой код не скейлится
Отлично, что ты сам упомянул про масштабирование по количеству бизнес-логики. Декларативный байндинг уменьшает константный множитель перед сложность, но порядок сложности остается такой же. Поэтому, да, они приятны, если проект не выходит за их рамки применимости, но принципиально они погоды не делают. Инструменты для sclising-а изменяют именно порядок сложности.
Оставить комментарий
Имя или ник:
Комментарий: