Превращение атрибутов в директиву AngularJS
Я создавал директиву выбора замены, чтобы упростить стиль, выбирает в соответствии с дизайном без необходимости всегда править кучу разметки (т.е. директива делает это для вас!).
Я не понял, что атрибуты не переходят туда, где вы помещаете ng-transclude
, и просто переходите к корневому элементу.
У меня есть пример здесь: http://plnkr.co/edit/OLLntqMzbGCJS7g7h1j4?p=preview
Вы можете видеть, что он выглядит великолепно... но есть один главный недостаток. Атрибуты id
и name
не передаются. Что, я знаю, без name
, он не отправляет на сервер (эта форма связана с существующей системой, поэтому AJAXing модель не является вариантом).
Например, это то, с чего я начинаю:
<select class="my-select irrelevant-class" name="reason" id="reason" data-anything="banana">
<option value="">Reason for Contact...</option>
<option>Banana</option>
<option>Pizza</option>
<option>The good stuff</option>
<option>This is an example of a really, really, really, really, really, really long option item</option>
</select>
... вот что я хочу, чтобы он выглядел так:
<div class="faux-select" ng-class="{ placeholder: default == viewVal, focus: obj.focus }">
<span class="faux-value">{{viewVal}}</span>
<span class="icon-arrow-down"></span>
<select ng-model="val" ng-focus="obj.focus = true" ng-blur="obj.focus = false" ng-transclude class="my-select irrelevant-class" name="reason" id="reason" data-anything="banana">
<option value="">Reason for Contact...</option>
<option>Banana</option>
<option>Pizza</option>
<option>The good stuff</option>
<option>This is an example of a really, really, really, really, really, really long option item</option>
</select>
</div>
... но это то, что на самом деле происходит:
<div class="faux-select my-select irrelevant-class" ng-class="{ placeholder: default == viewVal, focus: obj.focus }" name="reason" id="reason" data-anything="banana">
<span class="faux-value">{{viewVal}}</span>
<span class="icon-arrow-down"></span>
<select ng-model="val" ng-focus="obj.focus = true" ng-blur="obj.focus = false" ng-transclude>
<option value="">Reason for Contact...</option>
<option>Banana</option>
<option>Pizza</option>
<option>The good stuff</option>
<option>This is an example of a really, really, really, really, really, really long option item</option>
</select>
</div>
В частности, проблема в том, что в элементе нет атрибута name
, поэтому он фактически не отправляет данные на сервер.
Очевидно, что я могу использовать фазу предварительной компиляции для переноса атрибутов name
и id
(что я делаю сейчас), но было бы неплохо, если бы она просто автоматически передавала все атрибуты, поэтому они могут добавлять любые классы, произвольные данные, (ng-) требуемые, (ng-) отключенные атрибуты и т.д. и т.д.
Я пытался получить transclude: 'element'
для работы, но тогда я не мог использовать другие атрибуты из шаблона.
Заметьте, я видел сообщение здесь: Как я могу перейти в атрибут?, но похоже, что они просто переносят данные вручную, и я стараюсь получить его для автоматического переноса всех атрибутов.
Ответы
Ответ 1
Вы можете использовать функцию компиляции для доступа к атрибутам элемента и создания шаблона.
app.directive('mySelect', [function () {
return {
transclude: true,
scope: true,
restrict: 'C',
compile: function (element, attrs) {
var template = '<div class="faux-select" ng-class="{ placeholder: default == viewVal, focus: obj.focus }">' +
'<span class="faux-value">{{viewVal}}</span>' +
'<span class="icon-arrow-down entypo-down-open-mini"></span>' +
'<select id="' + attrs.id + '" name="' + attrs.name + '" ng-model="val" ng-focus="obj.focus = true" ng-blur="obj.focus = false" ng-transclude>';
'</select>' +
'</div>';
element.replaceWith(template);
//return the postLink function
return function postLink(scope, elem, attrs) {
var $select = elem.find('select');
scope.default = scope.viewVal = elem.find('option')[0].innerHTML;
scope.$watch('val', function(val) {
if(val === '') scope.viewVal = scope.default;
else scope.viewVal = val;
});
if(!scope.val) scope.val = $select.find('option[selected]').val() || '';
}
}
};
}]);
Функция компиляции возвращает функцию postLink, есть другие способы сделать это, вы найдете более подробную информацию здесь.
Вот плункер
Ответ 2
ng-transclude
преобразует содержимое элемента, на который была поставлена директива. Я бы назначил атрибут его родительскому div и переместил все поле выбора в шаблон:
Первый подход:
http://plnkr.co/edit/fEaJXh?p=preview
<div class="form-control my-select">
<select class="irrelevant-class" name="reason" id="reason" data-anything="banana">
<option value="">Reason for Contact...</option>
<option>Banana</option>
<option>Pizza</option>
<option>The good stuff</option>
<option>This is an example of a really, really, really, really, really, really long option item</option>
</select>
</div>
И удалите опцию замены из определения:
app.directive('mySelect', [function () {
return {
template:
'<div class="faux-select" ng-class="{ placeholder: default == viewVal, focus: obj.focus }">' +
'<span class="faux-value">{{viewVal}}</span>' +
'<span class="icon-arrow-down entypo-down-open-mini"></span>' +
'<div ng-transclude></div>' +
'</div>',
transclude: true,
//replace: true,
scope: true,
restrict: 'C',
link: function (scope, elem, attrs) {
var $select = elem.find('select');
scope.default = scope.viewVal = elem.find('option')[0].innerHTML;
scope.$watch('val', function(val) {
if(val === '') scope.viewVal = scope.default;
else scope.viewVal = val;
});
if(!scope.val) scope.val = $select.find('option[selected]').val() || '';
}
};
}]);
Второй подход:
В своей демонстрации просто включите следующую строку в конце метода ссылки:
$select.attr({'id': elem.attr('id'), 'name': elem.attr('name')});