Здравствуйте!
Недавно мне довелось адаптировать ui-select под x-editable в Ангуляре и поскольку для этого пришлось потратить определенное количество времени, собирая по крупицам наиболее приемлемый вариант, сегодня я решил поделиться своими наработками с вами, в надежде на то, что кому-нибудь это сэкономит время.
Если вкратце, то полученная в итоге директива замещает стандартный editable-select, плюс дополнительная возможность добавлять объекты на лету.
Теперь подробнее.
Для начала начну с конца и приведу код директивы, позволяющей добавить кнопку в ui-select. На эту кнопку вешается функция, в моем случае — функция, вызывающая модальное окно(ui-bootstrap modal) с формой добавления нового объекта:
app.directive("addNewItem", function($timeout) {
return {
restrict: "A",
link: function(scope, e, attrs) {
var method = attrs.addNewItem;
var template = "<div class='add-new-item-container'>"+
"<button class='btn btn-xs btn-default pull-right'>Добавить</button>"+
"<div class='clearfix'></div></div>";
e.find('li.ui-select-choices-group').append(template);
e.find('div.add-new-item-container button').bind('click', function() {
// workaround for closing ui-select
$timeout(function() {
e.trigger("click");
});
var searchResult = e.find('li.ui-select-choices-row').length;
if ( ! searchResult ) {
var value = e.find('input.ui-select-search').val();
scope[method].apply(null, [value]);
} else {
scope[method].apply();
}
});
}
}
})
В принципе, код предельно прост, поэтому остановлюсь на паре моментов. Во-первых, ui-select не закрывается после нажатия кнопки, но закрывается, если кликнуть куда-нибудь по модальному окну. Поэтому пришлось добавить костыль с $timeout.
Во-вторых, (по поводу последнего if else): если в поиске ui-select не найдено ни одного элемента, я передаю значение поиска в метод, чтобы в дальнейшем автозаполнить этим значением форму и таким образом немного сэкономить время.
Теперь основная директива:
app.directive('editableUiSelect',
['editableDirectiveFactory', 'editableNgOptionsParser',
function(editableDirectiveFactory, editableNgOptionsParser) {
var dir = editableDirectiveFactory({
directiveName: 'editableUiSelect',
inputTpl: '<ui-select></ui-select>',
render: function() {
this.parent.render.call(this);
var parsed = editableNgOptionsParser(this.attrs.eNgOptions);
this.inputEl.attr('ng-model', 'editable.entity');
// слева директива, описанная вначале,
// справа - метод, в моем случае вызывающий модальное окно с формой добавления
this.inputEl.attr('add-new-item', 'addNewItem');
// поскольку модель самостоятельно не меняется, пришлось добавить этот метод
this.inputEl.attr('on-select', 'setModel($item)');
this.inputEl.attr('theme', 'select2');
this.inputEl.css('width', '200px');
var html = "<ui-select-match><span ng-bind='$select.selected.name'></span></ui-select-match>"+
"<ui-select-choices repeat='" + parsed.ngRepeat + "'>" +
"<span ng-bind-html='" + parsed.locals.displayFn + "'></span></ui-select-choices>";
this.inputEl.removeAttr('ng-options');
this.inputEl.append(html);
}
});
return dir;
}]);
Директива была сделана по образу и подобию стандартных директив xeditable, а потом немного переделана.
Одной из основных проблем, с которыми я столкнулся при написании директивы, была невозможность изменить модель, поэтому был добавлен дополнительный метод для on-select.
Использовать все это можно так:
<span
data-pk="{{ entity.id }}"
editable-ui-select="entity.property"
ng-model="entity.property"
e-ng-options="obj.id as obj.name for obj in objects | filter: $select.search track by obj.id"
e-form="rowform">
{{ entity.property.name }}
</span>
И напоследок бонус. Если использовать это дело в table-responsive (bootstrap), то выпадающий список может быть перекрыт таблицей (особенно, если она состоит из пары строк). Исправить это можно, добавив css:
.table-responsive .ui-select-dropdown {
position: relative !important;
}
Заключение. Вариантов реализации данной директивы может быть множество и наверняка среди них есть варианты лучше моего. Я опубликовал свои наработки только потому, что в официальной документации xeditable пока нету упоминания о поддержке ui-select, а также потому, что я нашел мало информации по теме, а та, что нашел, разнится между собой.
Надеюсь на то, что гуру ангуляра помогут мне улучшить мою директиву, а также на то, что кому-нибудь пригодится эта статья.
Автор: ivanuzzo