Да, Маска — model agnostic. И это к лучшему — модель может выглядеть как угодно — будь это данные из mongoose в node.js, или данные из localStorage, или обычный класс User на клиенте. Плюс для тестов это на порядок лучше.
Хотя маска и поддерживает такую запись — div > '~[:get("username")]', что бы достать данные из модели — но биндинги на данный момент работать не будут таким образом.
Спасибо! Но с событиями вы, наверное, делали что-то не так. В маске вы нигде на события(сигналы) не подписываетесь — всё довольно строго распределено — из любого контроллера или из любого дом элемента можно узнать, кто может / обрабатывает сигнал. В бэкбоне вы же наверняка подписываетесь на события? events: { «click .my-button»: «x»} — это называется «слушать разметку», а в этом есть большой недостаток — если есть иерархия контроллеров и каждый имеет свою разметку — тогда из предков, ни в коем случае нельзя «слушать разметку» детей. До этого, было как бы, два решения — первое, ребенок посылает «custom event», и второе, ребенок вызывает функции отца. Второе отпадает — из-за непозволительно большой связности. А «custom event» это уже лучше но лишние телодвижение — ребенок слушает свою разметку и посылает событие. Я же предлагаю выводить «событие»(сигнал) непосредственно в представление.
Модель:
В Backbone это именно модель
— вы, думаю, понимаете, что любые данные — это модель. Эти данные / состояния можно обернуть или скрыть в классах / структурах. И эти «обертки» собственно и называются domain model. Маска может работать и с тем, и с другим.
Да я видел, как люди использовали маску с бэкбоном, используя её как шаблонизатор. И постепенно создавали свои компоненты (обработчики тэгов). Биндинг в Маске реализoван привязками через defineProperty — согласитесь, иметь в конечном результате "model.someAttr" лучше, чем model.get("someAttr");. Если вы получаете данные по ajax запросу, вам их не надо расширять до бэкбоновской модели — а напрямую передавать во View.
Хорошие комментарии длинными не бывают, даже если занимают 10 скроллов)
С :datePicker-ом вы делаете, что то не то) Если вам надо до или после календаря вывести текст / кнопkи, так не обязательно помещать их в блок :datePicker —
h4 > 'Title'
:datePicker;
button > 'X'
Шаблон — p ::name — у вас также шаблон зависит от модели, а если имеется ввиду, что это данные из контроллера — тогда контроллер, это чистой воды Presenter — который в свою очередь получает данные из модели. (см. статью)
Наследование тоже самое что и MasterPages — layout
Контроллер — не понятно, как у вас определяются события. На узлы (DOM HTMLElements) всех компонент вешаются события клик (action)? А если надо не на весь элемент вешать обработчик, а только на кнопку 'OK' например, а если много кнопок?
Наследование контроллеров в MaskJS устроено пока что, только как обычное наследование Javascript прототипов — а там уже мы можем, например, переопределить метод onRenderEnd подсунув там другой шаблон для компоненты.
Модель — точно также можно и в маске это сделать. Но мне нравится, что не обязательно!! создавать врапперы — маске подойдет любой объект.
На счёт array-like — исходить из того, что фреймворк и так догадается, не особо стоит, потому что порой мы передаём массив, но хотим, что бы не элементы выводились из него, а к примеру, только статистика по нему. И если у вас есть модель, где не только массив, а помимо и другие пропертя, поэтому нам всё равно нужно передавать его (массив) в шаблон / контроллеры — почему бы не через each?
Ну а так расширить MaskJS до вашего видения не составит труда — я попытался по максимуму упрастить всё — и по максимуму дать возможность расширять. И если у вас есть время, можете форкнуть и попробовать поиграться — а если будут вопросы, я вам помогу.
Сигналы — это не вызовы методов, а как бы именованные события, которые по дереву контроллеров или трубкам гуляют. А привязываться к модели из шаблонов вы как предлагаете по другому? Эту задачи в любом случае нужно кому-то делегировать. Если мы из конроллеров будем вставлять в дом данные — тогда контроллеры слишком толстые получатся. Здесь же шаблоны отвечают за свои задачи — вывести данные пользователю, а в случае сигналов — предают динамичности посылая «события». Мне кажется такое распределение вполне уместно. Или есть у вас другие идеи?
:datePicker, это всего лишь обёртка над jQuery виджетом. И её реализация не подразумевает, что вы дополнительно в календарь свою разметку вставлять будете. Поэтому он просто это игнорирует. datePicker
В отличии от других реализаций, разметка превращается в AST дерево. И реализовав свой билдер можно будет и на canvas рисовать
rect dimension='0:0:100:100' color='red' {
line from='0:0' to='5:5' size=5;
}
Ну смысл вы уловили.
Вы убрали div — а если нам надо, что бы данные выводились в span? Сейчас data биндинги это лишь надстройка над MaskJS (не путать ~[name] и ~[bind: name]]), поэтому и синтаксиса специально для него нету. И из вашего примера, так будет через чур свободно, или?
td > p > 'My name is $name'
// vs
td > p > 'My name is ~[name]'
Хотя да, смотрится не плохо, но не зря все шаблонные движки выбирает немножко посложнее синтаксис для интерполяции.
Да я раньше туда отправлял реализацию, но тогда это был лишь шаблонизатор без разных «наворотов» и они приняли только как альтернативу. Сейчас я обновил приложение и может попробую ещё раз послать «request». Приложение здесь. Или более длинный путь:
1) Ну вы же дотнэтер, я так понимаю, и не мне вас убеждать, что для бизнес лейэр: один класс — один файл. Хорошо, javascript — не c#, но как минимум один модуль — один файл.
2) Можно для дебага и модули отдельно грузить — и вы не только номер строки видите, но и названия модуля/класса/файла, — и это уже плюс.
3) Сугубо субьективная оценка.
4,5) Файл-Модульная изоляция нужна для тестов, что бы тестировать внутренний апи который скрыт от внешнего доступа. Ведь вся библиотека в основном находится за замыканием, то как из тестов достать эти самые классы/модули?
6) Модульность вам же улучшает разработку. Решили создать другую реализацию для модуля — создали новый файл, создали модуль, собрали проект с новым модулем — вот и у вас уже новая бета, а человек решит — или хочет stable пользоваться, где ещё старый модуль, или взять бетку. Или другой пример — модуль с разными реализациями. Один только для современных браузеров, другой кроссбраузерный — с кучей polyfills и другими вещами. Разрабатывая мобильное приложение, я бы взял более легкую сборку.
Да, там много строк :) Просто, я более чем уверен, что в WebStorm не менее удобно и по файлам «прыгать». А преимущества налицо — видно сразу структуру проекта, но самое главное — изоляция модулей, а без этого покрывать тестами проект очень сложно. Ну и сборки можно разные делать.
А так, я вижу, проект уже не маленький, и в след. раз можно статью тоже побольше написать — с примерами, особенностями… Удачи!
Хотел посмотреть исходники, и скажите — вы и вправду весь код держите в одном файле js/jriapp.js? 1.5К строк можно было бы и по классам разложить, а потом собирать…
Просто блоки с with() {} дорогие в плане производительности, так что если вам она хоть немножко важна — а я так понял, что — да, тогда нужно от «with» отказываться. Благо его заменить довольно просто. А так, скажу вам — занимаетесь хорошим делом — удачи!
«Есть одно тёмное время — когда на свет рождаются самые уродливые строки когда, когда самые витиеватые хаки пробуждаются, когда сумерки опускаются над проектом. И это время кличут — мёртвой чертой. Программисту же все вокруг сочувствуют и сопереживают. В коридорах можно услышать шепот коллег -'Нет, нет — не трогайте его. У него сегодня дэдлайн ...' — и окончание фразы от страха и воспоминаний ещё долго висит в воздухе, ведь каждый переживал это время.»
emit/on — да, это почти тоже самое, что и сигналы: передаются по дереву, можем подписаться/отписаться. Различия только в а) семантике: оперировать понятиями event/signal, проще чем — event/custom event ( хотя это вполне субъективно и возможно проблема высосана мной из пальца только потому, что мне просто так больше нравится:)) и б) возможный декларативный биндинг — то есть как в случае с ng-click, в контроллере нам не нужно явного вызова .on(..), а все слоты мы складываем в объекте «slots» нашего контроллера. И еще один плюс в декларативном биндинге, что в контроллере мы явно разделаем «методы контроллера»/«слоты».
недостаток ng-click в том, что мы как бы исполняем выражение по событию непосредственно из view. То есть мы из view лезем в current scope и выполняем от туда метод контроллера. А это как-то грязновато, поэтому и называется «Model-View-Whatever»
:) this.slots('sendMail').disable() — a) слот мы не сможем вызвать (пока не .enable(...) ), и б) если слотов больше нет у сигнала в данном или выше по дереву контроллере, то соответствующей кнопке/пункту меню присвоится :disabled. И не надо создавать лишних биндингов к кнопке.
Но оговорюсь, что это все очень похоже друг с другом и возможно такие различия в нюансах важны только мне:) !?
Ваша правда, событие есть всегда и обработчик есть всегда. Но вот я пытаюсь абстрагироваться от «нативных» событий (clicked, changed ...) применяя qt философию signal/slot. В своем шаблонном движке переопределяю событие на сигнал.
button x-signal="click: sendMail";
И контроллер реализует этот слот:
//...
slots: {
sendMail: function() { },
// ...
}
Надеюсь уже сразу понятно в чем здесь преимущества.
избавились от «безликих» событий
контроллер может ничего не знать о разметке/классах
сигнал, как и событие может передаваться по дереву контроллеров
из view может ещё N-колличество элементов посылать этот сигнал
по необходимости, мы можем сделать слот или сигнал не активным
В результате получили гибрид, смесь дом событий и ng-click=«handler». View не должен ничего знать о контроллере/(-ах), просто определяем какие сигналы он посылает — и это дает более целостное понимание представления. Да и в контроллере мы тоже чётко группируем функции.
Но повторюсь, что это все тот же «событие — обработчик» — только немного другая «философия» ;)
Хотя маска и поддерживает такую запись —
div > '~[:get("username")]', что бы достать данные из модели — но биндинги на данный момент работать не будут таким образом.Модель:
— вы, думаю, понимаете, что любые данные — это модель. Эти данные / состояния можно обернуть или скрыть в классах / структурах. И эти «обертки» собственно и называются domain model. Маска может работать и с тем, и с другим.
"model.someAttr"лучше, чемmodel.get("someAttr");. Если вы получаете данные по ajax запросу, вам их не надо расширять до бэкбоновской модели — а напрямую передавать во View.p ::name— у вас также шаблон зависит от модели, а если имеется ввиду, что это данные из контроллера — тогда контроллер, это чистой воды Presenter — который в свою очередь получает данные из модели. (см. статью)Наследование тоже самое что и MasterPages — layout
Наследование контроллеров в MaskJS устроено пока что, только как обычное наследование Javascript прототипов — а там уже мы можем, например, переопределить метод onRenderEnd подсунув там другой шаблон для компоненты.
На счёт array-like — исходить из того, что фреймворк и так догадается, не особо стоит, потому что порой мы передаём массив, но хотим, что бы не элементы выводились из него, а к примеру, только статистика по нему. И если у вас есть модель, где не только массив, а помимо и другие пропертя, поэтому нам всё равно нужно передавать его (массив) в шаблон / контроллеры — почему бы не через each?
Ну а так расширить MaskJS до вашего видения не составит труда — я попытался по максимуму упрастить всё — и по максимуму дать возможность расширять. И если у вас есть время, можете форкнуть и попробовать поиграться — а если будут вопросы, я вам помогу.
Ну смысл вы уловили.
~[name] и ~[bind: name]]), поэтому и синтаксиса специально для него нету. И из вашего примера, так будет через чур свободно, или?Хотя да, смотрится не плохо, но не зря все шаблонные движки выбирает немножко посложнее синтаксис для интерполяции.
> cd tempFolder && ijs template todoappНО, такие вещи хорошо выносить в константы, и вовсе не в целях производительности.
пс. и результат будет совсем другим если использовать new RegExp(reg); jsperf/2
2) Можно для дебага и модули отдельно грузить — и вы не только номер строки видите, но и названия модуля/класса/файла, — и это уже плюс.
3) Сугубо субьективная оценка.
4,5) Файл-Модульная изоляция нужна для тестов, что бы тестировать внутренний апи который скрыт от внешнего доступа. Ведь вся библиотека в основном находится за замыканием, то как из тестов достать эти самые классы/модули?
6) Модульность вам же улучшает разработку. Решили создать другую реализацию для модуля — создали новый файл, создали модуль, собрали проект с новым модулем — вот и у вас уже новая бета, а человек решит — или хочет stable пользоваться, где ещё старый модуль, или взять бетку. Или другой пример — модуль с разными реализациями. Один только для современных браузеров, другой кроссбраузерный — с кучей polyfills и другими вещами. Разрабатывая мобильное приложение, я бы взял более легкую сборку.
А так, я вижу, проект уже не маленький, и в след. раз можно статью тоже побольше написать — с примерами, особенностями… Удачи!
js/jriapp.js? 1.5К строк можно было бы и по классам разложить, а потом собирать….on(..), а все слоты мы складываем в объекте «slots» нашего контроллера. И еще один плюс в декларативном биндинге, что в контроллере мы явно разделаем «методы контроллера»/«слоты».this.slots('sendMail').disable()— a) слот мы не сможем вызвать (пока не.enable(...)), и б) если слотов больше нет у сигнала в данном или выше по дереву контроллере, то соответствующей кнопке/пункту меню присвоится :disabled. И не надо создавать лишних биндингов к кнопке.Но оговорюсь, что это все очень похоже друг с другом и возможно такие различия в нюансах важны только мне:) !?
И контроллер реализует этот слот:
Надеюсь уже сразу понятно в чем здесь преимущества.
В результате получили гибрид, смесь дом событий и ng-click=«handler». View не должен ничего знать о контроллере/(-ах), просто определяем какие сигналы он посылает — и это дает более целостное понимание представления. Да и в контроллере мы тоже чётко группируем функции.
Но повторюсь, что это все тот же «событие — обработчик» — только немного другая «философия» ;)