AngularJS адаптация ui-select под x-editable с дополнительной возможностью добавлять объекты на лету

от автора

Здравствуйте!

Недавно мне довелось адаптировать 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, а также потому, что я нашел мало информации по теме, а та, что нашел, разнится между собой.

Надеюсь на то, что гуру ангуляра помогут мне улучшить мою директиву, а также на то, что кому-нибудь пригодится эта статья.

ссылка на оригинал статьи https://habrahabr.ru/post/277001/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *