Javascript, jComponent 10 октября 2019 3 мин. 1506
Довольно много компонент уже создано (более 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());
};
Создадим компонент, который выведет "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.
В этом примере сделаем часы, которые будут отображать время в нужном нам формате. Разместим 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.
Сделаем компонент счётчик, который будет производить обратный отсчёт от нужного нам числа. в переменной 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.
Создадим оригинальный компонент, это выбор изображения из списка, например, может понадобится при выборе аватарки. Массив аватарок свяжем со списком выбора. При выборе изображения, будем его отображать в теге 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.