Javascript Interface Abstract Notation

в 10:37, , рубрики: javascript, jiant, jquery, web applications, Программирование, метки: , , ,

Здравствуйте всем, этот пост о библиотеке, а вернее философии разработки приложений на Javascript которую изобрел пару месяцев назад. Постоянно использую сам, раздал всем друзьям. Отзывы положительные, поэтому решил рискнуть открыть более широкому сообществу.
Сама библиотека работает как подстройка к jQuery. Практически зависимость от jQuery условна и ее можно разделить с самой библиотекой, пока не было необходимости.
Jiant позволяет создавать клиентские приложения любой сложности, значительно упрощая их поддержку и развитие.

Краткая предыстория

Я более 10 лет занимаюсь разработкой веб приложений. Последние пару лет постоянно использовал GWT. К сожалению, Google не смог решить самую серьезную проблему этой библиотеки — медленную компиляцию. После длительной паузы в выходе новых версий (между 2.4 и 2.5) и передачи GWT на сторону, ведущие разработчики компании где я работаю (не широко известной) сошлись во мнении что с GWT пора завязывать. Мы решили дальше вести разработку на jQuery. Но GWT привило несколько очень удобных «рефлексов» разработки, которых явно не хватало при чистом javascript программировании. В частности, MVC подход с разделением на презентацию и логику, EventBus. Неудобство и дискомфорт нового положения привели к некоторым идеям, которые постепенно развиваясь и трансформируясь привели к решению, описанному ниже.
Также до написания библиотеки я разбирался с существующими MVC решениями типа Backbone и был не очень счастлив количеством boilerplate кода(избыточностью требуемых объявлений).

Бенефиты

Собственно, чтобы был стимул читать, забегу вперед и перечислю основные плюсы:

  1. Автозавершение кода (autocomplete) — вместо строковых объявлений.
  2. Полное абстрагирование интерфейса и разделение логики и представления
  3. Валидация привязки javascript переменных к собственно элементам веб страницы во время запуска приложения, а не во время исполнения конкретного фрагмента кода. Что дает дополнительную защиту разработчику функционала от шаловливых рук дизайнера
  4. Самодокументирование на уровне кода максимально простым способом — меньше и в то же время понятней мне не встречалось
  5. Простота встраивания — в любой момент в любое место можно встроить и немедленно получить плюсы

И еще много всякого по мелочи.

Принципы

Идеология библиотеки строится на следующий тезисах. Если принять их за основу, то все дальнейшее понимается легко:

  • Любой интерфейс состоит из набора вьюшек (View, Вид?) — по русски слово подобрать сложно, так как это не виджеты, но и не экраны. Это уникальные значащие части интерфейса. Проще всего их выделить, если просто слушать рассказ заказчика: вот тут «экран редактирования контакта», отсюда переходим на «список пользователей» (на странице может быть несколько видимых View одновременно).
  • Каждая View содержит в себе набор элементов, типа «кнопка возврата на список», «контейнер списка пользователей», «картинка пользователя». Эти элементы идентифицируемы в рамках одной View

А также несколько технологических тезисов, которые будут вести через дебри, когда надо принять решение «а как это сделать»? Так, чтобы не противоречило следующему:

  • То что видит пользователь в итоге на экране это по сути дела html код. А что лучше всего отрисует html? Только сам html. То есть когда нам надо показать пользователю некий интерфейс — мы просто помещаем его html на страницу и используем когда нужно. Вместо генерации через DOM манипуляцию или конкатенирование строк на лету (кроме самых простых случаев). Это также облегчит жизнь дизайнера — он просто отдизайнит html код.
  • Повторяющиеся элементы генерируем шаблонами, которые опять же есть просто html код на странице

Самое важное — Jiant (javascript interface abstract notation) — это больше философия разработки чем набор трюков. Если разрабатывать придерживаясь этой философии, то все получается легко и естественно.

View

Проектируем

Итак, вначале проектируем интерфейс на json, не вдаваясь в детали реализации. Например, просто слушая рассказчика (или заказчика) о форме регистрации: «Пусть пользователь вводит имя и емейл, потом жмет кнопку зарегистрироваться и ему показывается окошко что он зарегистрирован, на которое он нажмет и попадет внутрь».
Описываем это на json, как слышим так и пишем.

var registration = { //объявим одну переменную, чтобы не засорять глобальное пространство имен 

  views: {  // секция views содержит те самые концептуальнофилософские View

    registrationFormView: {  // форма регистрации
      nameInput: jiant.input, // поле ввода имени
      emailInput: jiant.input, // поле ввода емейла
      registerCtl: jiant.ctl // элемент интерфейса, нажав на который пользователь зарегистрируется
    },

    successView: { // экран с сообщением об успехе
      continueCtl: jiant.ctl // кнопка или ссылка, неважно, для входа в приложение
    },

    errorView: {  // экран с сообщением об ошибке
      errorMessage: jiant.label, // сообщение об ошибке
      disposeCtl: jiant.ctl // кнопка чтобы спрятать сообщение об ошибке и поменять значения в полях ввода
    }

  }
};

Как это все понять? Читаем код следующим образом: «наше приложение состоит из трех вьюшек — формы регистрации, сообщения об успехе и сообщения об ошибке. Форма регистрации содержит два поля ввода и один контрол ...» и так далее. jiant.input и jiant.ctl здесь служат только для целей документирования. Например, можно написать так и это тоже будет работать, но будет менее понятно при чтении:

var registration = {

  views: {  
    registrationFormView: {  
      nameInput: 1,
      emailInput: 1,
      registerCtl: 1,
    },

    successView: { 
      continueCtl: 1
    },

    errorView: {  
      errorMessage: 1,
      disposeCtl: 1
    }

  }
};
Реализуем

Теперь, когда интерфейс описан в высшей степени абстрактно и максимально кратко — мы реализуем логику работы приложения с интерфейсом и реализуем сам интерфейс в html коде. В итоге получая MVC в форме html-json-javascript. Вначале реализуем сами вьюшки на html, по следующим правилам:

  • Каждой View соответствует элемент html с соответствующим id-ом
  • Каждому элементу View (nameInput, registerCtl) соответствует элемент html с соответствующим классом, расположенный внутри элемента View

Реализация registrationFormView:

<div id="_registrationFormView">
  Your name: <input class="_nameInput"> <br>
  Your email: <input class="_emailInput"> <br>
  <button class="_registerCtl">Register</button>
</div>

Что за подчерк перед идентификатором и именем класса? Чуть ниже он объясняется.

Аналогично реализуем successView:

  <div id="_successView">
     Поздравляем с регистрацией, мы Вам очень рады!
     <button class="_continueCtl">Continue</button>
  </div>

и errorView.

Воплощаем абстракцию

Итак, у нас есть абстрактное определение интерфейса. Есть его реализация. Как их связать?
Следующим образом:

$(function() {
  jiant.bindUi("_", registration, true);
});

Готово.

Функция bindUi принимает первым параметром префикс, который добавляется к каждому абстрактному определению View или элемента View. В данном случае это подчерк. Если передать пустую строку, то имена в json и идентификаторы-классы в html будут совпадать. Я нахожу удобным использовать подчерк, для повышения читаемости html кода — сразу видно значащие элементы интерфейса.
Второй параметр — это переменная, содержащая абстрактное определение приложения. jiant достает из нее элемент views и связывает каждую вьюшку с ее реализацией.
Третий параметр — это включение отладочного режима, что упрощает поиск ошибок и увеличивает количество вывода в консоль.

Магия

Здесь уже начинается немного магии. Во время bindUi выполняется поиск каждой View из определения интерфейса по идентификатору. Затем результирующий jQuery wrapper объект присоединяется к переменной определения. То есть — после выполнения bindUi — переменная registration.views.registrationFormView содержит в себе результат $("#_registrationFormView"). Также каждый элемент вьюшки ищется по классу и заполняется, то есть registration.views.registrationFormView.registerCtl содержит в себе $("#_registrationFormView").find("._registerCtl").

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

На выходе мы имеем полностью привязанный к настоящим html элементам объект описания интерфейса. Который теперь можно использовать в коде логики следующим образом.

Используем

Самый простой способ — пишем свой отдельный javascript файл и регистрируемся на событие onUiBound, например:

jiant.onUiBound(function($, app) { // в этот момент все jQuery wrapper объекты присоединены к абстрактному объявлению интерфейса

  var view = app.views.registrationFormView,
        errorView = app.views.errorView; // чтобы меньше писать

  view.registerCtl.click(function() {
    errorView.hide();
    if (view.nameInput.val() == "") {
      errorView.errorMessage.html("Пустое имя!");
      errorView.show();
    } else if (view.emailInput.val() == "") {
      errorView.errorMessage.html("Пустой емейл!");
      errorView.show();
    } else {
      view.hide();
      app.views.successView.show();
    }
  });

});

Готова логика. При чтении это не ощущается, но в хорошем IDE — 75% этого кода авто-завершено, т.е. сэкономлено время на написании и уменьшена вероятность ошибки из-за опечатки.

Что еще

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

var app = {

   ajax: {
    getDataFromServer: function(id, callback) {}
   }
}

Больше от разработчика ничего не требуется — jiant сам реализует код аякс запроса.

Остается пользоваться:

  app.ajax.getDataFromServer(6345, function(data) {
    //do smth with data
  });

Кто знает GWT — сразу увидит полное сходство с Async сервисом.

Дальше

Если эта статья выйдет в печать и вызовет интерес, я готов дальше расписать остальные функции (шаблоны, аякс, состояния и шину событий). Код проекта на gihub: github.com/vecnas/jiant, сайт с описанием: sites.google.com/site/jiantscript/home

Автор: Vecnas

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js