IML TODO

angular vs iml 2

disclaimer: статья является ответом на критику ( которая обрушилась на хабре ), раскрывая потенциал IML на примере популярного приложения ToDo MVC.

Получил тумаков

Критика – это слово крайне мягкое по отношению к дискуссии, которая возникла к моей предыдущей статье, потому что это было больше похоже на избиение в котором были крайне не приятные комментарии ( на фото топ бредовых ), но также и объективные:

  • Код  на AngularJs не фонтан  – сложно опровергнуть, хотя все они были с официального сайта и популярного руководства
  • Слабые примеры – упор был на задачи, а не на сценарии, но соглашусь что более комплексное решение более полно раскрывает потенциал ( я предложил некоторые наши проекты, которые открыты на open source, но они остались без внимания )
  • Не знаешь AngularJs ? –  по понятным причинам это очень сильно задело разработчиков AngularJs
  • Топик JS – это серьезная ошибка, потому что не используя  asp.net mvc, сложно понять прелести типизированных TextBoxFor и других расширений

Почему ToDo ?

В комментариях предложили попробовать реализовать “Todo MVC”  в качестве доказательства возможностей IML и сейчас мы посмотрим что из этого получилось. Во первых demo версия, которая имеет одно  отличите от тех, что представлены для js framework, в том, что в качестве хранилища используется не local storage, а база данных, а также  исходный код,  который мы будем разбирать далее в посте. В процессе реализации я строил всю логику ( расчеты подвала, скрытие элементов и т.д. )  на клиенте, хотя на реальных задачах, проще ( иногда необходимо ) обновлять “точечно” элементы, которые имея IML код, знают, как себя вычислить и отобразить.

Code review

Стиль повествования в этот раз будет не сравнение одного решения с другим (иначе объем материала будет очень большим ), а обзор кода, который получится при реализации приложения todo.  Я упоминал выше о том, что в реализации IML присутствует и серверная часть, но в целях уравнивания решаемых задач для более объективного сравнения, сфокусируемся только на клиентской части.

Из чего состоит

Код был разделен на 3 View

  • Index – основная страница ( и по факту единственная для браузера )
  • Todo_List_Tmpl – шаблон для построения центрального списка
  • Todo_Footer_Tmpl – шаблон для построения подвала с показателями

расположение в графическом ввиде

ОПАСНО: далее будет много C# кода и отсутствие JavaScript, так что слабонервных просьба успокоиться и не пугаться всяким using(Html) 

Index ( состоит из трех элементов )

Форма для добавления TODO

примечание: сразу ожидаю фразы в стиле “Да, ну это не серьезно, кода в разы больше, надо везде копировать, посмотри как люди делают !!!”  – на что у меня есть аргумент в лице C# extensions, который позволяет оборачивать конструкции IML. Далее в статье будет приведены альтернативные варианты решения задач ( также repository на GibHub с переработанным кодом ) с применением C# extensions 

Что к чему ?
  • When(JqueryBind.Submit) – указываем целевое событие
  • DoWithPreventDefault – поведение события ( отменяем обработчик браузера )
  • Submit – отправляем форму через ajax

примечание:  пару замечаний по представленной реализации:

    • Url куда отправляется форма задается в опциях ( а не через атрибут action у form )
    • ClientId можно вынести форму, как Hidden, который по InitIncoding проставит значение из Cookies, что бы вызывать Submit без параметров
  • OnSuccess – выполняем после удачного завершения submit
    • Trigger Incoding to containerId  – запускаем весь IML код для элемента Container ( описание ниже )

примечание:  можно применять более одного When, что позволяет подвязаться на разные события ( с разным IML кодом ), поэтому trigger Incoding запускает все цепочки.

    • Form reset  – сбрасываем значение элементов формы
  • AsHtmlAttributes – собираем IML код в удобный для asp.net mvc формат ( RouteValueDictionary )
  • ToBeginTag – упаковываем полученные атрибуты в тэг form ( принцип работы как Html.BeginForm )

примечание: можно использовать Html.BeginForm(“action”,”controller”,Post,iml.AsHtmlAttributes())

Форма для добавления TODO ( альтернативный вариант )

примечание: кода стало меньше и  что самое главное, теперь  можно расширять ( валидация, redirect после submit и т.д. ) метод, под нужды конкретного проекта.

Под капотом



примечание: код знаком большинству asp.net mvc разработчиков, но стоит отметить то, что вместо “обычных” параметров, мы передаем анонимный метод, который принимает класс настроек.

Container

Что к чему ?
  • When(JqueryBind.InitIncoding | IncChangeUrl) – указываем целевые события
    • InitIncoding – срабатывает при первом появлении элемента на странице ( не важно ajax или обычно )
    • IncChangeUrl – срабатывает при изменение hash
  • Do – поведение события
  • AjaxGet – указываем url, на который будет выполнен ajax запрос
    • ClientId – получаем значение из cookies
    • Type – получаем значение из Hash Query String
  • OnSuccess – выполняем после удачного завершения AjaxGet
    • Insert data to self by template – вставляем  полученные данные из запроса ( json ) через template ( Todo_List_Tmpl ниже )  в текущий элемент.

примечание: template можно получить через любой доступный Selector, например раньше основным был Jquery.Id, но загрузка по ajax предпочтительней 

    • Trigger incoding to footerId –  запускаем весь IML код для элемента footer ( описание ниже )
  • AsHtmlAttributes – собираем IML код и задаем значение containerId ( guid ) атрибуту Id

примечание:  использование guid в качестве Id гарантирует уникальность элемента на странице, особенно актуально для single page application

  • ToDiv – упаковываем полученные атрибуты в тэг div

примечание: ToDiv это C# extensions над RouteValueDictionary, поэтому без труда можно написать свой нужный вариант 

Container ( альтернативный способ )

примечание: если в будущем надо будет добавить block ui или другие действия, то теперь это можно делать централизованно

Под капотом



Footer

Что к чему ?
  • When(JqueryBind.None) – указываем целевые события
    • None – When позволяет указать любое пользовательское событие, как строку “MySpecialEvent”, но практика показал, что для многих сценариев хватает одного.
  • Do – поведение события
  • Direct – можно рассматривать как action заглушка, который не выполняет действий, но может работать с данными
    • AllCount – получаем кол-во объектов с классом “toggle”

примечание: можно воспользоваться расширением Method ( вместо Length ), чтобы вызвать любой jquery метод, а также написать C# extensions над JquerySelectorExtend

    • IsCompleted  – проверяем на наличие отмеченных объектов с классом “toggle”

примечание: кому не хватит возможностей готовых jquery selector, то можно воспользоватся Selector.Jquery.Custom(“your jquery selector”)

    • CompletedCount – получаем количество отмеченных объектов с классом “toggle”

примечание: для получения значения JS функции подойдет :

  • OnSuccess – выполняем после удачного завершения AjaxGet
    • Insert prepare data to self by template  – вставляем подготовленные ( prepare ) данные из Direct через template ( Todo_Footer_Tmpl ниже ) в текущий элемент

примечание:  prepare перед тем как вставить данные выполняет селекторы, которые находятся в полях.

  • AsHtmlAttributes – собираем IML код
  • ToDiv – упаковываем полученные атрибуты в тэг div

Todo List Tmpl

Разметка шаблона для построения списка todo

примечание: исходный код больше ( удаленна  логика элементов ), чем представлен на пример, но это сделано для удобства объяснения template

Что к чему ?
  • Html.Incoding().Template() –  открываем контекст ( в рамках using ) построения template
  • template.ForEach() – начинаем перебор ( в рамках using ) элементов
  • using(each.Is(r=>r.Active)) – предыдущий вариант условий был в “одну линию”, но часто бывает что надо выполнить более сложные действия.
  • createCheckBox – анонимная  C# функция  для создания checkbox ( описание ниже )
  • each.IsInline(r=>r.Active,”completed”) – если поле Active true, тогда возвращаем “completed”

примечание: также имеются IsNotLine и IsLine. 

  • each.For(r => r.Title) – выводим значение поля Title

примечание: все обращения к полям происходят на основе указанной модели ( да, я опять о типизации )

Другие элементы

Button del

Что к чему ?
  • When(JqueryBind.Click) – указываем целевое событие
  • Do – поведение события
  • AjaxPost- указываем Url, на который делаем ajax запрос
    • Id– значение из Todo
    • AssemblyQualifiedName – получаем имя типа элемента ( или иной другой C# код )
  • OnBegin– выполняем до начала действия ( AjaxPost )

примечание: конечно правильней будет использовать OnSuccess, потому что может произойти ошибка на сервере ( timeout или что-то другое )  и транзакция будет не полная из-за того, что OnBegin сработает до вызова AjaxPost, но примеры TodoMVC на js framework используют local storage ( который быстрее чем ajax ) и поэтому я не много схитрил, да бы не проигрывать в быстродействие

    • Remove closest LI   – удаляем ближайший LI
    • Trigger incoding to footer id – запускаем весь IML код для элемента footer (  описание выше )
    • Trigger none to toggle all – запускаем IML  код ( только цепочку None )  для элемента Toggle All ( описание  ниже )

примечание: если бы для обоих ( footer и toggle all ) нужно было вызвать одинаковый trigger, то можно использовать следующий вариант

  • AsHtmlAttributes – собираем IML код
  • ToButton- упаковываем полученные атрибуты в тэг button

примечание: ToButton позволяет указать содержимое, но в данном случаи это не надо, потому что картинка устанавливает через CSS

Button Del ( альтернативный вариант )

примечание: OnBegin принимает Action<IIncodingMetaLanguageCallbackDsl>, что позволяет легко масштабировать ваш extensions внедряя в него IML. ( далее будут ещё примеры )

 

Под капотом



 примечание: по скольку Verb использует в нескольких сценариях, то можно легко делать опциональные параметры проверяю их на null, а так же задавать значения по умолчанию

 Checkbox Completed

примечание: в рамках razor страницы можно использовать анонимные  C# функции  или Razor helper, что позволяет агрегировать однотипные задачи.

Что к чему ?
  • When(JqueryBind.Change) – указываем целевое событие
  • Do – поведение события
  • AjaxPost – указываем Url, на который делаем ajax запрос

примечание: AjaxPost и AjaxGet это “именованная” версия Ajax, который имеет много дополнительных настроек

  • OnBegin – выполняем до начала действия ( AjaxPost )
    • Remove class on closest LI –  удаляем класс “completed” у ближайшего LI
    • Add class on closest LI  if self is true– добавляем класс “completed”

примечание: пока в IML не реализована  возможность else, но в версии 2.0 планируется

  • AsHtmlAttributes – собираем IML код, а также устанавливаем значение “toggle”  атрибуту class
  • ToCheckBox – упаковываем

Filter by type todo

примечание: ещё один пример реализации анонимных функций в рамках razor view

Что к чему ?
  • When(JqueryBind.InitIncoding) – указываем целевое событие
  • Do – поведение события
  • Direct – ничего не выполняем
  • OnSuccess – выполняем после удачного завершения

примечание: для Direct нет отличия между OnBegin или OnSuccess, но OnError и  OnBreak работают, так же как и для остальных

    • var type – объявляем переменную, которую потом будем использовать в выражениях
    • add class to self if IsFirst is true And type is Empty – добавляем класс, если текущий элемент является первым и в type пустой
    • add class to self if type is current type – добавляем класс к текущему элементу если type равен аргументу typeOfTodo
  • When(JqueryBind.Click) – указываем целевое событие
  • Do – поведение события

примечание: мы не отменяем поведение ссылки, потому что нам нужно, чтобы браузер обновил location

  • Direct – ничего не выполняем
    • remove class – удаляем класс selected у всех A, которые находятся в ближайшем UL
    • add class to self – добавляем класс selected текущему элементу
  • AsHtmlAttributes – собираем IML код, а также устанавливаем атрибут href

 

Filter by type todo ( альтернативный способ )

 

Под капотом



Безусловные плюсы !

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

  • Типизация – конечно каждый смотрит через свою призму на типизацию, кто-то считает что приходится писать больше кода  ( это верно ), другим не хватает гибкости, которая присуща не типизированным языкам, но IML это прежде всего C#, поэтому те разработчики, которые его выбрали, я думаю по достоинству оценят этот плюс.
  • Мощные extensions – в статье я привел несколько, но на практике их на много больше, чтобы подкрепить свои слова приведу ещё пару:
    • Drop down

примечание: OnInit принимает Action<IIncodingMetaLanguageCallbackDsl>, что позволяет легко масштабировать ваш extensions внедряя в него IML. 

    • Dialog

примечание: для большей гибкости можно использовать Action в качестве поля, например setting.Options это Action<JqueryUIDialogOptions>.  

Список можно продолжать бесконечно, но основная идея в том, что IML позволяет выполнять любые задачи, а html extensions решает проблему с повторным использованием.

  • Ещё мощнее extensions
    • Grid – полностью построенный на IML ( в ближайшие время будет документация )

    •  Tabs – 

 примечание: любой разработчик знакомый с html extensions может построить такой элемент под нужды своего проекта

  • Работа с hash – в этой статье была рассмотрена только на уровне IncChangeUrl, но у нас есть:
    •  Hash.Fetch – проставляет значения из hash в элементы  ( sandbox )
    •  Hash.Insert/Update – проставляет значения в hash из элементов
    •  Hash.Manipulate – позволяет тонко ( set/ remove by key ) настроить текущий hash
    • AjaxHash – это аналог Submit, но не для form, а для Hash.
  • Работа с Insert – для реализации TODO мне не пришлось применять, но в реальных проектах повсеместно
    • Insert Generic  – все примеры выше были построенные на одной модели, но часто бывают сценарии, когда полученные данные являются “контейнером”, для этих целей в Insert есть возможность указывать с какой частью модели мы работаем через For, а также template для каждой свой.

  • Работа с validation ( сервер, как клиент ) – в многих js framework есть инструменты для валидации, но IML, как упоминалось не однократно имеет интеграцию с сервером и поддержка любых validation engine  ( FluentValidation, стандартный MVC) без написания дополнительного кода.
    • Код command

    •  Код view

 примечание: обработчик OnError должен быть прикреплен к элементу, который вызывает action ( submit , ajaxpost or etc ) 

  • Меньше скриптов – с увлечением проекта js framework требует написания множества js файлов, но IML имеет фиксированный ( плагины не в счет ) набор библиотек
  • Типизированные template – я о типизации в целом, но для построения шаблонов это особенно важно
  • Замена template engine – выбирайте любой, а синтаксис тот же
  • Готовая инфраструктура –  IML это часть Incoding Framework и в отличии от js framework у нас полная ( сервер / клиент / unit testing ) инфраструктура  для разработки проектов, которая тесно интегрирована между собой.

Заключение

При реализации todo на IML я придерживался правила: меньше обновлений странице, то есть пересчитывал все на клиенте, но практика ( наших проектов ) показывает, что чаще узким местом бывает именно сервер, потому что многие действия не возможны  или не предпочтительны на клиенте, например

Невозможны ( по причине производительности ):

  • Paginated – если в базе сотни тысяч записей, то такой объем не правильно передавать на клиента
  • Order – та же причина
  • Where – та же причина
  • и другие действия связанные с базой данных

Не предпочтительны могут быть расчеты, такие как сложные вычисления ( общую сумму заказов с учетом налога ) на основе значений полей, удобней будет отправить на сервер запрос ( с данными полей ) и результат вставить.

 

Опять вариант IML

В рамках IML вычисления можно решить следующими способами:

  • Одиночное значение

  •  Набор данных 

Можно долго  рассказывать про возможности IML ( и Incoding Framework ), но статья и так получилось большой, поэтому , те кто захочет продолжить изучать наш инструмент смогут найти материалы в сети. Я понимаю, доказать то, что IML способен решать задачи не хуже популярных js framework крайне сложно, но в следующих статья будет обзор реализаций autocomplete , Tree View, grid  и других популярных сценариев, которые продемонстрируют больше возможностей нашего инструмента.

 

 

 

 

 

Vlad Kopachinsky

I am a Senior developer at Incoding Software in Russian Federation. I created the Incoding Framework for rapid development

More Posts - Website - Twitter - Facebook - LinkedIn

Leave a Reply