WidLib – декларативный js-фреймворк для построения виджетов

от автора

Сегодня я хотел бы анонсировать js-фреймворк, позволяющий создавать диалоговые микро-приложения в несколько строк js-кода, разделяемого между клиентом и сервером.

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

В отличие от большинства фреймворков, WidLib не претендует на универсальность: он предназначен для быстрого создания многостраничных диалоговых приложений.

Спектр задач
  1. Виджеты заказа доставки
  2. Wi-Fi приложения (путеводитель по торговому центру или интерактивное меню при подключении к местному Wi-Fi)
  3. Калькуляторы (кредиты, пластиковые окна и двери)
  4. Формы подписки на сайте
  5. Викторины (игровые, а так же для маркетинговых акций и розыгрышей)
  6. Тесты (проверка компетенций, обучение, работа с кадрами)
  7. Онлайн-помощники (например, подбор туристического маршрута или ассистент колл-центра)
  8. Встраивание виджетов в мобильные приложения (здесь он похож на PhoneGap)
  9. Диалоговые приложения для соцсетей (опять-таки викторины, нелинейные опросы)
  10. Бронирование (билеты на самолет, номер в гостинице или время у стоматолога)
  11. и т.д.

Чтобы создать DSL, мы спросили себя – каким мы хотим видеть предельно простой, но при этом гибкий язык описания (и в меньшей степени – программирования) этих диалоговых приложений. Мы использовали подход convention over configuration, знакомый по Ruby on Rails. Простые приложения пишутся в три строчки, сложные – чуть больше.
Давайте рассмотрим на примере виджета по заказу пиццы на сайте:
(DSL еще находится в доработке, поэтому предложения приветствуются.)

Coffeescript
widlib=require("widlib-server") server=widlib.init   # Можно использовать любой шаблонизатор (index_template=Handlebars.compile("...")),   # в функцию передается объект страницы, шаблон сохранится в @app.template   template: index_template   pages:     type:       # шаблоны можно также задавать индивидуально для каждой страницы       template: type_template # @app.pages["type"].template       body: "Выберите пиццу"       # при клике на ссылку происходит событие submit, автоматический переход на следующую страницу (если не указано явно)       # также у каждой страницы есть события onLeave, onEnter       inputs: [         { value: "Маргарита", type: "link", name: "type", price: 350 },         { value: "Пепперони", type: "link", name: "type", price: 360 },         { value: "Филадельфия", type: "link", name: "type", price: 370 },         { value: "Четыре сыра", type: "link", name: "type", price: 380 },       ]      size:       body: "Выберите размер"       # если в параметре используем функцию - значение вычисляется лениво       inputs: ->         price = @session.input("type").price # session - хранит данные текущей сессии. @session.input("type") присвоился автоматически после страницы type.         [           { value: "30", type: "link", name: "size", price: price },           { value: "40", type: "link", name: "size", price: price*1.2 },           { value: "50", type: "link", name: "size", price: price*1.5 },         ]       # можно в явном виде указать следующую страницу или использовать функцию       onSubmit: "address"      address:       body: "Введите адрес"       inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]      phone:       body: "Введите телефон"       inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }]       onSubmit: -> # также возможно использовать события onLoad, on         @data("orders").push @session.values() # данные записываем в постоянное хранилище         @data("email").push email_template(@session.values())         "success" # возвращаем имя следующей страницы      success:       body: "Ваша пицца уже едет к вам"       image: -> "/images/#{@session.value("type")}.jpg" # можно использовать дополнительные параметры, в данном случае image для view    data:     orders:       type: "spreadsheet" # используя модули для различных API можно хранить или синхронизировать данные с внешними сервисами       url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXIZFSUE"     email:       type: "email"       to: "1@interactiff.net"  server.listen "3000" 

Код без комментариев

widlib=require("widlib-server") server=widlib.init   template: index_template   pages:     type:       template: type_template       body: "Выберите пиццу"       inputs: [         { value: "Маргарита", type: "link", name: "type", price: 350 },         { value: "Пепперони", type: "link", name: "type", price: 360 },         { value: "Филадельфия", type: "link", name: "type", price: 370 },         { value: "Четыре сыра", type: "link", name: "type", price: 380 },       ]      size:       body: "Выберите размер"       inputs: ->         price = @session.value("type").price         [           { value: "30", type: "link", name: "size", price: price },           { value: "40", type: "link", name: "size", price: price*1.2 },           { value: "50", type: "link", name: "size", price: price*1.5 },         ]       onSubmit: "address"      address:       body: "Введите адрес"       inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]      phone:       body: "Введите телефон"       inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }]       onSubmit: -> # также возможно использовать события onLoad, on         @data("orders").push @session.values()         @data("email").push email_template(@session.values())         "success"       success:       body: "Ваша пицца уже едет к вам"       image: -> "/images/#{@session.value("type")}.jpg"    data:     orders:       type: "spreadsheet"       url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXIZFSUE"     email:       type: "email"       to: "1@interactiff.net"  server.listen "3000" 
В переводе на JS

var client, server, widlib;  widlib = require("widlib-server");  server = widlib.init({   template: index_template,   pages: {     type: {       template: type_template,       body: "Выберите пиццу",       inputs: [         { value: "Маргарита", type: "link", name: "type", price: 350 },         { value: "Пепперони", type: "link", name: "type", price: 360 },         { value: "Филадельфия", type: "link", name: "type", price: 370 },         { value: "Четыре сыра", type: "link", name: "type", price: 380 },       ]     },     size: {       body: "Выберите размер",       inputs: function() {         var price;         price = this.session.input("type").price;         return [           { value: "30", type: "link", name: "size", price: price },           { value: "40", type: "link", name: "size", price: price*1.2 },           { value: "50", type: "link", name: "size", price: price*1.5 },         ];       },       onSubmit: "address"     },     address: {       body: "Введите адрес",       inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]     },     phone: {       body: "Введите телефон",       inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }],       onSubmit: function() {         this.data("orders").push(this.session.values());         this.data("email").push(email_template(this.session.values()));         return "success";       }     },     success: {       body: "Ваша пицца уже едет к вам",       image: function() {         return "/images/" + (this.session.value("type")) + ".jpg";       }     }   },   data: {     orders: {       type: "spreadsheet",       url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXI3cGRlbkJteGZFSUE#gid=0"     },     email: {       type: "email",       to: "1@interactiff.net"     }   } });  server.listen("3000");

Первой строкой мы создаем объект widget и загружаем в него сценарий.
Объект сценария состоит из страниц и данных. Каждая страница – отдельный экран, который увидит пользователь в этом виджете.

Каждый объект с данными соответствует адаптеру для их хранения или передачи. В простейшем случае – это просто массив, в сложных – REST интерфейс, MongoDB, Google Spreadsheet и прочие.

Мы можем использовать один и тот же сценарий, как на клиенте (в т.ч. standalone), так и на сервере node.js. Использование на сервере позволяет скрыть сценарий или его часть от пользователя, что может пригодиться для калькулятора кредита или викторины с розыгрышем призов, а также даёт доступ к динамическим данным и API, агрегированию и обработке пользовательской информации.
Клиентская часть виджета доставки пиццы, в данном случае пустая:

client=new Widlib.Client   # здесь можно использовать тот же самый код, что и в серверной части.   # pages: ...   # data: ...   server: "/"   container: "#container" 

Разработчику даже не нужно заботиться, каким образом передавать информацию, и в каком случае обмениваться данными с сервером. Библиотека автоматически проверяет наличие страниц или данных в локальном сценарии, после чего незаметно для автора запрашивает их у сервера (или отправляет), используя RPC. Серверная версия обладает более полным функционалом, а клиентская работает быстрее и независимо, что позволяет ее использовать в мобильных приложениях, а также не беспокоить сервер по пустякам.

Фичи
  • Декларативный стиль написания
  • Для простых случаев практически не требует навыков программирования
  • Возможно использовать готовые шаблоны (планируется также создать библиотеку шаблонов и визарды для создания виджетов обывателями)
  • Возможно использование декларативного html-шаблонизатора и биндинга rivets.js (подобен таковому в AngularJS). Скрипт связывания будет доступен в репозитории фреймворка
  • Независимость от библиотек для работы с DOM
  • Разделяемый код между клиентом и сервером
  • Незаметный fallback клиентского к серверному скрипту.

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

P.S. для проекта будет доступна исчерпывающая документация, планируется библиотека примеров и визардов, а также сверхшустрый хостинг виджетов с плюшками в виде простого доступа к различным API.

Нужен ли вам такой фреймворк?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовал 1 человек. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/202484/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *