jComponent - #Часть 3, Учимся создавать свои компоненты

Javascript, jComponent 10 октября 2019 3 мин. 1307


Довольно много компонент уже создано (более 170) и размещено на сайте сomponentator.com. Но иногда этого недостаточно. Возможно, ни один из них не подходит вашему проекту или то что Вам нужно еще не существует, а может Вы хотите сделать обертку для кого-то популярного jQuery плагина.

Например, я делал обертки для таких популярных библиотек: DataTables, Summernote. Это очень облегчало работу с ними. Об этом, позже сделаю отдельный пост. Итак как создавать компоненты?


Начало

Для создания компоненты, необходимо воспользоваться методом COMPONENT()

COMPONENT(name, [config], declaration, [dependencies]);
// @name {String} - название
// @config {String} - параметры конфигурации по умолчанию
// @declaration {Function(instance, config)}
// @dependencies {Array String} - url зависимостей (script, styles, etc.)

Создадим первый компонент, он пока пустой и не содержит никакого функционала.

<script>
COMPONENT('mycomponent', function(self, config) {
    //self - ссылка на сам компонент
    //config - конфигурация
});
</script>

Свойства, методы и делегаты

Компонент имеет свойства, методы и делегаты.
Некоторые свойства, методы и делегаты, которые понадобятся нам при создании компонент.

Свойства

  • self._id - свойство содержит внутренний идентификатор для этого компонента
  • self.config - свойство содержит всю конфигурацию
  • self.element- возвращает jQuery элемент компонента
  • self.name - свойство содержит имя компонента
  • self.path - свойство содержит абсолютный путь для привязки данных

Более подробная информация по свойствам при создании компонент.

Методы

self.html() - метод является псевдонимом для self.element.html(), может устанавливать / получать содержимое элемента.

self.html(value);
// @value {String} or {String Array}
// returns {String} or {jQuery Element}

self.find() - метод находит элементы в соответствии с selector. Есть псевдоним для self.element.find() метода.

self.find(selector);
// @selector {String}
// returns {jQuery}

self.find('input').prop('disabled', true);

Делегаты

Делегаты - это функции, которые выполняет компонент.

self.configure() - делегат обрабатывает конфигурацию компонента. Он выполняется после инициализации компонента и после self.make(), или если self.reconfigure() выполняется вручную. Каждый key обрабатывается независимо.

self.configure = function(key, value, init, prev) {
    // @key {String}
    // @value {String/Number/Boolean/Object}
    // @init {Boolean} Is initialization?
    // @prev {String/Number/Boolean/Object} A previous value

    switch (key) {
        case 'required':
            self.tclass('required', value);
            break;
        case 'icon':
            self.find('.fa').rclass2('fa-').aclass('fa fa-' + value);
            break;
    }
};

self.make() - делегат выполняется при создании компонента. Если вы заполните self.template свойство, то template аргумент будет содержать полученные данные.

self.make = function(template) {
    // @template {String} Optional, it contains processed template data
};

self.setter() - делегат наблюдает за изменениями в модели в соответствии с определенным path. Выполняется каждый раз, когда данные изменяются. Реализация по умолчанию содержит автоматический механизм для ввода HTML, таких как <input data-jc-bind />, <textarea data-jc-bind>и <select data-jc-bind>.

self.setter = function(value, path, type) {
    // @value {Object}
    // @path {String}
    // @type {Number} 0: init, 1: manually, 2: by input, 3: default 

    // Apply formatters
    value = self.formatter(value);

    // Render the value:
    self.html(value == null ? 'NULL' : value.toString());
};

Пример 1

Создадим компонент, который выведет "Hello world". Для начала размести компонент в HTML:

<div data---="hello"></div>

Добавим Js код:

COMPONENT('hello', function(self, config) {
    //после инициализации запустится делегат make
    self.make = function() {
        //Среди свойств у нас есть self.element, который возвращает   
        //элемент jQuery. Он содержит элемент нашей декларации.
        self.element.text('Hello world');
    };
});

See the Pen Example 3.1 by Saper639 (@saper639) on CodePen.


Пример 2

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

<!--Время в формате по умолчанию-->
<div data---="timer"></div>
<!--Время в формате по mm:ss-->
<div data---="timer__null__format:mm\:ss"></div>
<!--Время в формате по dd.MM.yyyy hh:mm:ss-->
<div data---="timer__null__format:dd\.MM\.yyyy hh\:mm\:ss"></div>

JS код компонента:

//format:HH\\\:mm\\\:ss форматировани по умолчанию
COMPONENT('timer', 'format:HH\\\:mm\\\:ss', function(self, config) {
    self.make = function() {
        //каждую секунду вызывается функция self.timer()
        setInterval(self.timer, 1000);
    };

    self.timer = function() {
        //определяем текущее время
        var time = new Date().format(config.format); 
        //выведем время
        self.element.text(time);
    };
});

See the Pen Example 3.2 by Saper639 (@saper639) on CodePen.


Пример 3

Сделаем компонент счётчик, который будет производить обратный отсчёт от нужного нам числа. в переменной mynumber содержится текущее значение счётчика. Когда отсчёт будет закончен будет вызвана функция test(). Разместим HTML код, который будет выводить счётчик:

<div data---="countdown__mynumber__end:test"></div>

JS код компонента:

COMPONENT('countdown', function(self, config) {
    var count;
    var timer;

    self.timer = function() {
        //уменьшаем значение счётчика    
        count--;
        //сохраним текущее значение, вызовется self.setter()
        self.set(count, 3);
        //если значение > 0, выведем значение
        if (count)
            self.element.text('countdown: ' + count);
        //иначе, счётчик завершил отсчёт    
        else {
            clearInterval(timer);
            self.element.text('countdown: END');
            //вызовем функцию, которая указана в параметре end
            config.end && EXEC(config.end); 
        }
    };
    //вызывается при изменении значения
    self.setter = function(value, path, type) {
        //если значение не существует или оно задано по умолчанию,   
        //то прервем выполенение
        if (!value || type === 3)
            return;
        //каждую секунду запускаем обновление счётчика
        count = value;
        timer && clearInterval(timer);
        timer = setInterval(self.timer, 1000);
        self.timer();
    };
});
//==========================
//запуск счётчика
SET('mynumber', 20);
//запустится функция когда отсчёт будет завершен
function test() {
  alert('Таймер завершил отсчёт');
}

See the Pen Example 3.3 by Saper639 (@saper639) on CodePen.


Пример 4

Создадим оригинальный компонент, это выбор изображения из списка, например, может понадобится при выборе аватарки. Массив аватарок свяжем со списком выбора. При выборе изображения, будем его отображать в теге img. В переменной form.img будет храниться url текущей аватарки. datasource:arr формируем список изображений из массива arr.
Разместим HTML код:

<div data---="imgview__form.img__datasource:arr;text:name;value:url;" class='mb10'></div>
<!-- выведем текущее изображение -->
<p>Изображение: <span data-bind="form.img__html:value"></span></p>

JS код компонента:

COMPONENT('imgview', function(self, config) {
    var select, preview, content = null;
    var render = '';
    //конфигурирование компоненента при его инициализации
    self.configure = function(key, value, init) {
        if (init) return;
        if (key=='datasource') self.datasource(value, self.bind);       
    };  
    self.redraw = function() {      
        var html = '<select data-jc-bind="" size="5" style="width:150px;">{0}</select><span class="preview"></span>'.format(render);        
        self.html(html);
        select = self.find('select');
        preview = self.find('.preview');
        render && self.refresh();           
    };  
    //наблюдает за изменениями в модели в соответствии с опред. path, при выборе в списке меняем аватарку
    self.setter = function(value) {
        if (!value) return;
        var img = self.preview.format(value);
        preview.html(img);
        select.find('option').each(function(el) {
            var el = $(this);
            var is = el.attr('value') === value;
            el.attr('selected', is);  
        })
    };  
    //привязываем datasource к нашему списку
    self.bind = function(path, arr) {       
        if (!arr) arr = EMPTYARRAY;     
        var builder = [];
        var value = self.get();
        var propText = config.text || 'name';
        var propValue = config.value || 'url';
        //формируем список изображений
        for (var i = 0, length = arr.length; i < length; i++) {
            var item = arr[i];          
            builder.push(self.template({ value: item[propValue], selected: value == item[propValue], text: item[propText] }));
        }       
        render = builder.join('');
        select.html(render);
    };  
    //выполняется при создании компонента
    self.make = function() {        
        self.template = Tangular.compile('<option value="{{value}}"{{if selected}} selected="selected"{{ fi }}>{{text}}</option>');
        self.preview = '<img src="{0}" style="width:100px">';
        content = self.html();
        self.redraw();
        config.datasource && self.reconfigure('datasource:' + config.datasource);       
    };  
})  

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

var form = {'img': 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTK9DkWY2BgUcJMNZXyAz6Cpmiq-AhC098wtOnBL-3foioVeyaI'};
var arr = [
    { name: 'ava1', url: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQKMU4kxf7EmZPYd8SJgXuK1rd9YUw6ZZs5sGPuADjKiCxIixo4'},
    { name: 'ava2', url: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTK9DkWY2BgUcJMNZXyAz6Cpmiq-AhC098wtOnBL-3foioVeyaI'},
    { name: 'ava3', url: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6yM6JujrHFOvFH9NvuV2lWnyXECBr1SWeF-I0tMdYmK942MXr'},
]               

Что можно делать с этим компонентом, можно изменить весь список изображений или установить нужное изображение. Экспериментируйте, это не так сложно.

See the Pen Example 3.4 by Saper639 (@saper639) on CodePen.


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


Ссылки:

Категории