Создаем собственные виджеты в Dojo


    Dojo Toolkit это одновременно самый мощный и наименее используемый JavaScript фреймворк. Dojo состоит из AMD модулей, большая часть которых является виджетами. Виджеты обычно состоят из логики на JavaScript и HTML шаблона. В будущей версии 2.0 заявлена поддержка WebComponents. Dojo позволяет легко как создать полностью новый виджет, так и расширить или изменить уже существующий. В этом посте я расскажу как это делать.

    Создание простейшего виджета


    Для начала создадим простейший виджет.

    Step 1: Создание структуры

    Для объявления своего виджета нужно создать JavaScript файл со следующим содержанием:
    define([
          "dojo/_base/declare",
          "dijit/_Widget",
          "dijit/_TemplatedMixin"
    ], function(declare, _Widget, _TemplatedMixin){
      return declare([_Widget, _TemplatedMixin], {});
    });
    

    Пока мы просто объявили AMD модуль и указали несколько зависимостей. Первым аргументом функции declare является массив модулей от которых требуется унаследовать создаваемый модуль. Сейчас указано, что модуль наследует базовый для всех виджетов модуль dijit/_Widget и модуль _TemplatedMixin, который предоставляет шаблонизатор.

    Step 2: Создание HTML представления

    Создадим рядом с файлом модуля папку templates и в ней HTML файл. Назвать его лучше так же как и файл модуля. Пусть модуль называтся MyCustomWidget.js, тогда файл шаблона следует назвать MyCustomWidget.html. В файле шаблона должно находится HTML представление виджета. Причем корень обязательно должен быть один.

    Шаблонизатор позволяет:

    • указать специальные узлы, attachPoints, которые будут доступны как свойства виджета;

      Для этого у нужного узла необходимо задать специальный атрибут: data-dojo-attach-point=”customNode”, где “customNode” — название свойства виджета через которое будет доступен узел. Корневой узел виджета всегда доступен через свойство domNode.

    • определить обработчики событий;

      Для этого у нужного узла необходимо задать специальный атрибут: data-dojo-attach-event=”ondijitclick:_onClick”, где “ondijitclick” указывает на событие, которое необходимо обрабатывать, в данном случае клик, а “_onClick” — название метода, который должен выступить в роли обработчика события.

    • указать место подстановки значений свойств виджета при его создании.

      Для этого в шаблоне нужно написать ${nameProp}, где “nameProp” — название свойства.


    Step 3: Связываем шаблон и данные

    Подключим шаблон:
    define([
          "dojo/_base/declare",
          "dijit/_Widget",
          "dijit/_TemplatedMixin",
          "dojo/text!./templates/MyCustomWidget.html"
    ], function(declare, _Widget, _TemplatedMixin, template){
      return declare([_Widget, _TemplatedMixin], {
          templateString: template
      });
    });
    

    На самом деле мы могли и не выносить шаблон в отдельный файл, а сразу задать HTML как значение свойства templateString или вынести его в переменную, но, по-моему, это снижает красоту кода.

    В качестве примера виджета создадим виджет выводящий фамилию и имя.
    <div>
          <span data-dojo-attach-point="surnameNode">${surname}</span>
           
          <span data-dojo-attach-point="nameNode">${name}</span>
    </div>
    

    Чтобы каждый раз, когда у виджета изменяются свойства, менялись значения и в HTML, необходимо связать сеттеры свойств и attachPoints. Можно указать какое свойство какого attachPoint соответствует какому свойству виджета, а можно определить собственные сеттеры.

    Если у вас есть свойство, где задание/получение значения сложнее чем просто обращение к свойству объекта, то вам нужно определить собственные сеттеры/геттеры соблюдая простые правила именования: для свойства “foo” это будут _setFooAttr/_getFooAttr. Методы set и get автоматически найдут их и вызовут в случае необходимости.
    define([
          "dojo/_base/declare",
          "dijit/_Widget",
          "dijit/_TemplatedMixin",
          "dojo/text!./templates/MyCustomWidget.html"
    ], function(declare, _Widget, _TemplatedMixin, template){
      return declare([_Widget, _TemplatedMixin], {
          templateString: template,
          _setSurnameAttr: { node: "surnameNode", type: "innerHTML" },
          _setNameAttr: function(val){
                  this.nameNode.innerHTML = val;
                  this._set("name", val);
          }
      });
    });
    


    Как вы, наверно, уже могли догадаться, описанный виджет можно подключить используя его имя и расположение. Можно использовать прямой путь до JS-файла модуля, а можно объявить свой пакет, по аналогии с dojo, dijit и dojox, и подключать файлы из него. Альтернативой является сразу использовать результат вызова функции declare.

    Как работает созданный нами код можно посмотреть здесь.

    Виджет состоящий из других виджетов


    Виджет может состоять из других виджетов. Составляющие виджеты можно добавить динамически во время создания виджета, но удобнее объявить их сразу в шаблоне. Для этого к модулям от которых наследуется виджет необходимо добавить dijit/_WidgetsInTemplateMixin.

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

    <div>
    	<div>
    		<div data-dojo-type="dijit/form/HorizontalSlider"
    			data-dojo-attach-point="slider"
    			name="${name}"
    			value="${value}"
    			maximum="${maximum}"
    			minimum="${minimum}"
    			step="${step}"
    			showButtons="${showButtons}"
    			intermediateChanges="${intermediateChanges}"
    			style="width:150px">
    		</div>
    		<div data-dojo-type="dijit/form/TextBox"
    			value="${value}"
    			type="number"
    			data-dojo-attach-point="textbox">
    		</div>
    		<span data-dojo-attach-point="legendNode"></span>
    	</div>
    	<div data-dojo-attach-point="descriptionNode"></div>
    </div>
    

    В этом фрагменте мы объявили виджет dijit/form/HorizontalSlider, который представляет собой горизонтальный ползунок, и виджет dijit/form/TextBox — текстовое поле ввода.

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

    Также, мы снабдили вложенные виджеты атрибутами data-dojo-attach-point, чтобы можно было обращаться к ним, как к свойствам виджета. Т.е. если мы захотим получить значение поля ввода, то нам нужно написать:
    this.textbox.get("value");
    

    Аналогичный виджет из APS JS SDK можно увидеть в APS Fiddle.

    Жизненный цикл виджета


    Жизненний цикл виджета позволяет понять что именно и когда происходит.

    Вы можете расширить или переопределить следующие методы:

    • constructor

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

    • параметры создания виджета смешиваются со значением свойств по умолчанию

      Данное действие нельзя переопределить, но важно знать когда это происходит.

    • postMixInProperties

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

    • buildRendering

      dijit/_Templated предоставляет реализацию buildRendering, которой достаточно в большинстве случаев: загрузит и прочитает шаблон, создаст DOMElements, привяжет специальные узлы и события. Итоговый результат будет помещен в this.domNode. Если вы не используете dijit/_Templated, например, используете другой шаблонизатор, тогда это то место, где вы должны его использовать.

    • вызываются сеттеры

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

    • postCreate

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

    • startup

      Разбор и создание всех дочерних виджетов завершено. Виджет помещен в DOM.

    • destroy

      Реализуйте destroy, если вам нужно выполнить какие-то дополнительные действия при уничтожении виджета.


    Inherited

    При переопределии метода всегда полезно убедиться, что вы не потеряли нечто важное, что происходит в данном методе выше по цепочке наследования. Поэтому не забывайте вызывать this.inherited до или после вашего кода.
    postCreate: function(){
       // do my stuff, then...
       this.inherited(arguments);
    }
    


    Полезные ссылки


    Еще пример создания виджета.
    Больше информации о создании виджетов.
    Больше информации о жизненном цикле виджета.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 19

      +1
      Возможно я ошибаюсь, но вместо dojoType наверное лучше использовать data-dojo-type.
        +1
        В версии 1.5 был dojoType, начиная с 1.6 data-dojo-type. И да, лучше, data-dojo-type.
          0
          Спасибо за замечание. Поправил. Но не встречал информацию почему data-dojo-* лучше. Это не самая важная оптимизация, но в проекте с большим количество шаблонов простая замена data-dojo-type/dojoType и data-dojo-attach-point/dojoAttachPoint позволила заметно уменьшить размер билда, т.к. шаблоны включаются в слой и не минифицируются.
        +1
        Я несколько лет назад участвовал в проекте на dojo. В те времена у них из компонент нормально работали только какие-то базовые штуки — инпуты там и прочее. Гриды и прочие навороты — были глючные в доску. Впрочем, хоть система модулей работала — в те времена это уже был хайтек.

        Сейчас там как насчет стабильности?
          +1
          Компоненты, которые входят в dijit — стабильны, dojox — все также приглючивают)
            0
            dijit — стабилен, dojox — бабушкин сундук. Иногда кажется есть все, но многие модули давно брошены авторами. Нужно почистить от пыли, может местами переписать, и получаются хорошие стабильные вещи.
            +1
            Мне не нравится только, что шаблонизатор тупой в доску, умеет только переменные подставлять, а к примеру циклов нету. :(
            С циклами в Dojo траблы даже в dojox MVC. Хотя конечно dojo/dom-construct никто не отменял.
              0
              А не пробовали встраивать другие шаблонизаторы? В случае Dojo циклы в шаблонах не очень актуальны, т.к. обычно нужно размножать не DOM Element в одном виджете, а несколько виджетов. Для этого есть dojox/mvc/WidgetList, но он тормозной. Особенно если еще байндинги использовать.
                0
                Какие? Я так и не нашёл хорошего и лёгкого шаблонизатора с поддержкой Dojo AMD. В dojox есть клон джанговского шаблонизатора но как я понял, он еле жив. В списке рассылки видел, что они хотят решить этот вопрос капитально к Dojo 2.0 но дальше увы не следил за новостями.
                  0
                  Можно использовать underscore, он поддерживает AMD и в его шаблонах можно использовать циклы, у меня была идея использовать комбинированные шаблоны, сначала пробегаться underscore'овским шаблонизатором, а затем уже пускать в ход DOJO
                    0
                    Ну и зачем underscore когда почти всю его функциональность реализует Dojo?
                    Нужно что то независимое и желательно нативное.
                      0
                      Что простите, DOJO не реализует даже четверти функционала underscore, там есть более 100 полезных функций + chaining. Покажите ка мне, пожалуйста, где DOJO это реализует…
                        +1
                        Странно как. Вы вот это видели? dojotoolkit.org/reference-guide/1.10/dojo/index.html#dojo-index
                        Как минимум по разделам Collection, Arrays, Objects, Functions (с ходу не всё нашёл) и частично Utils в Dojo есть. Прям очень плотно не смотрел но кажется процентов 60 (а то и больше) в Dojo всё это есть.
                          0
                          Мммм, мне кажется я чего то не догоняю, или вы, но underscore это Util-библиотека, из которой есть частичная (ну прям малюсенькая) реализация в array и lang
                            0
                            Может быть и я… но я смотрю сюда:
                            underscorejs.org/
                            и там функции чётко пересекаются с Dojo.
                            Может вы про другой underscore?
                              0
                              Попробуйте например найти в dojo например аналог _.each(list, iteratee, [context]) применительно к объектам. Что-то подобное есть в dojox, но это не серьезно с точки зрения зависимостей.
                                +2
                                Я написал, что есть процентов 60%. То что в dojox вроде особо за собой не тянет. Ну и честно, бегать по объектам можно и без этого. :)
                                0
                                найдите мне пожалуйста такие дико полезные функции как, pairs, values, keys, omit, pick, partitial, sortBy, once, after, before, zip, flatten и перечислять еще можно долго, если вы не работаете с данными на клиенте и не замечаете, что чего то нет, то не надо говорить, что в DOJO есть все
                                  +1
                                  Вы видимо плохо читали. Я написал:
                                  но кажется процентов 60 (а то и больше) в Dojo всё это есть.


                                  И пока я всё ещё вижу что 60% точно есть. Зачем мне тащить в зависимости библиотеку которая на 60% перекрывается с Dojo?

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

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

              Самое читаемое