Компоненты. Пример

Выпадающий список

Создадим простую компоненту, выпадающий dropdown список. (Обратите внимание, в коде всех заметок не будет jQuery). Пусть у нас есть якорь (форма ввода), и пусть, когда она получает фокус, под ней возникает выпадающий список заранее определенных значений. При клике на элементе списка получим его значение в произвольной функции.

90a978014dcae4220e753b40c31f9e0f

Код примера доступен по адресу https://github.com/mbykov/component-dropdown-example. Клонируйте его и откройте в браузере файл test/example.html, кликните на input.

Создадим скелет компоненты этого примера:

$ component create dropdown-example
repo (username/project): mbykov/component-dropdown-example
description: simple dropdown example
. . .

в components.json нужно указать необходимые нам компоненты:

"dependencies": {
      "component/domify": "*",
      "component/each": "*",
      "component/classes": "*"
  },

И выполняем $ make. В файле Makefile вы увидите, в частности (о формате makefile в заметке makefile vs. grunt):

build: components index.js dropdown.css
    @component build --dev
components: component.json
    @component install --dev

Это означает, что component установит все зависимости и создаст файлыbuild/build.js и build/build.css. (я переименовал исходный dd-example.css в dropdown.css, и соответственно исправил Makefile).

создание примера

Теперь создадим пример. Создаем директорию test и в ней файл example.html, суть которого

   <p><input type="text" id="anchor"><p>
    <script>
       var anchor = document.querySelector('#anchor');
       var Dropdown = require('dd-example');
       var dd = new Dropdown(anchor);
    </script>

А в файле index.js компоненты добавим основное содержание:

var domify = require('domify');
var classes = require('classes');
var each = require('each');

module.exports = Dropdown;

function Dropdown(anchor) {
    console.log(anchor);
    return this;
}

Обратите внимание на return this — это важно. Теперь мы можем создать цепочку методов для нашей компоненты. Каждый следующий метод получает все тот же this.

Теперь снова make, и релоад тестовой страницы (пока мы не используем watch, поэтому make вручную). В Фаейрбаге видим, что все идет как нужно, видим якорь.

Напишем собственно компоненту dropdown.

function Dropdown(anchor) {
    var _div = domify('<div></div>');
    var _list = domify('<ul></ul>');
    _div.appendChild(_list);
    this.list = _list;
    this.div = _div;
    this.anchor = anchor;
    anchor.parentNode.insertBefore(_div, anchor.nextSibling);
    anchor.focus();
    return this;
}

Что здесь происходит?

Мы создаем два объекта, _div и _list. Подчеркиванием мне удобно выделять DOM-объекты (underscore нам больше не нужен, а знак $ как-то не смотрится, но дело вкуса). В реальной жизни нужно, конечно, поместить html в template.html, и вызвать егоrequire('template'). Не делаю здесь для наглядности кода.

В строках this.list = _list сохраняем эти объекты как атрибуты объекта this, это пригодится в цепочке методов. И добавляем новый объект на страницу.

$ make

Но ничего не изменилось? Правильно, теперь нужно показать список с предопределенными значениями при клике на якорь. Добавим в _div и файл .css класс hidden, и в функцию Dropdow клик на якорь, и метод toggle():

    var self = this;
    anchor.onclick = function(e){
        self.toggle();
        classes(this.div).remove('hidden');
        return false;
    }

и создадим метод, который покажет значения

Dropdown.prototype.render = function(values) {
    var self = this;
    values.each(function(value){
        var added = domify('<li></li>');
        added.textContent = value;
        self.list.appendChild(added);
    });
    return this;
}

добавим в текст примера example.html вызов метода render c предопреденными значениями: var values = ['Асафий Аскольдович'...

    dd.render(values)

Здесь нет точки с запятой после строки. Это не ошибка, на следующей строчке будет вызван следующий метод компоненты — .select().

Теперь выведем значение при клике по строке списка в произвольном колбеке к нашей компоненте (Колбек в примере — будет просто console.log).

Dropdown.prototype.select = function(cb){
    var self = this;
    this.list.onclick = function(e) {
        var value = e.target.textContent;
        anchor.value = value;
        self.hide();
        cb(value);
    };
}

здесь нам не нужно возвращать return this в конце функции, потому что это конец цепочки. Вызовем метод .select в example.html, в котором выведем значение на консоль. Voila.

заключение

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

   _div.style.left = getOffset(anchor).left +'px';

функцию getOffset я позаимствовал у Mathew Muellera, спасибо ему.

При создании этого примера и своей «взрослой» компоненты dropdown я ориентировался на примеры старших товарищей: component/dropdownstagas/dropdown,bmcmahen/dropdownmatthewmueller/autocomplete. Они, правда, все используют jQuery, из-за чего я и написал свой mbykov/dropdown — он рассчитан на json-данные, получаемые из couchdb, поэтому имеет свои особенности.

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