Как условно применить шаблон с помощью пользовательских директив Angular?
DEMO
Рассмотрим следующую директиву:
angular.module('MyApp').directive('maybeLink', function() {
return {
replace: true,
scope: {
maybeLink: '=',
maybeLinkText: '='
},
template: '<span>' +
' <span ng-hide="maybeLink" ng-bind-html="text"></span>' +
' <a ng-show="maybeLink" href="#" ng-bind-html="text"></a>' +
'</span>',
controller: function($scope) {
$scope.text = $scope.maybeLinkText.replace(/\n/g, '<br>');
}
};
});
В директиве добавляются как <span>
, так и <a>
в DOM (только один из них отображается за раз).
Как я могу переписать директиву так, чтобы она добавила либо <span>
, либо <a>
в DOM, но не оба?
UPDATE
ОК, я думаю, я мог бы использовать ng-if
следующим образом:
template: '<span>' +
' <span ng-if="!maybeLink" ng-bind-html="text"></span>' +
' <a ng-if="maybeLink" href="#" ng-bind-html="text"></a>' +
'</span>'
Но как можно избавиться от окружающего <span>
в этом случае?
ОБНОВЛЕНИЕ 2
Вот версия директивы, которая использует $compile
. Он не имеет окружения <span>
, но двухсторонняя привязка данных тоже не работает. Мне очень интересно узнать, как исправить проблему двусторонней передачи данных. Любые идеи?
DEMO
angular.module('MyApp').directive('maybeLink', function($compile) {
return {
scope: {
maybeLink: '=',
maybeLinkText: '='
},
link: function(scope, element, attrs) {
scope.text = scope.maybeLinkText.replace(/\n/g, '<br>');
if (scope.maybeLink) {
element.replaceWith($compile('<a href="#" ng-bind-html="text"></a>')(scope));
} else {
element.replaceWith($compile('<span ng-bind-html="text"></span>')(scope));
}
}
};
});
Ответы
Ответ 1
Возможно, вы сможете использовать template
функцию. Согласно docs:
Вы можете указать шаблон в виде строки, представляющей шаблон, или как функцию , которая принимает два аргумента tElement и tAttrs (описанные в функции компиляции api ниже) и возвращает строковое значение, представляющее шаблон.
function resolveTemplate(tElement, tAttrs) {
}
angular.module('MyApp').directive('maybeLink', function() {
return {
//...
template: resolveTemplate,
//...
};
});
Ответ 2
Я думаю, что это самый чистый способ вставить динамический шаблон, основанный на свойстве scope
angular.module('app')
.directive('dynamic-template', function () {
return {
template:'<ng-include src="template"/>',
restrict: 'E',
link: function postLink(scope) {
scope.template = 'views/dynamic-'+scope.type+'.html';
}
};
})
Ответ 3
В конце появилась следующая версия:
angular.module('MyApp').directive('maybeLink', function($compile) {
return {
scope: {
maybeLink: '=',
maybeLinkText: '='
},
link: function(scope, element, attrs) {
scope.$watch('maybeLinkText', function(newText) {
scope.text = newText.replace(/\n/g, '<br>');
});
scope.$watch('maybeLink', function() {
var newElement;
if (scope.maybeLink) {
newElement = $compile('<a href="#" ng-bind-html="text"></a>')(scope);
} else {
newElement = $compile('<span ng-bind-html="text"></span>')(scope);
}
element.replaceWith(newElement); // Replace the DOM
element = newElement; // Replace the 'element' reference
});
}
};
});
Ответ 4
Я бы использовал ng-switch
.
что-то вроде
template: '<span ng-switch on="maybeLink">' +
' <span ng-switch-when="http://www.yahoo.com" ng-bind-html="text"></span>' +
' <a ng-switch-when="http://google.com" href="#" ng-bind-html="text"></a>' +
'</span>',
или
template: '<span ng-switch on="maybeLink">' +
' <span ng-switch-when={{maybeLink.length == 0}} ng-bind-html="text"></span>' +
' <a ng-switch-when={{maybeLink.length > 0}} href="#" ng-bind-html="text"></a>' +
'</span>',
Итак, это направление
Plunker
Ответ 5
Вы можете использовать ng-if для того же
Ниже приведен рабочий пример
Рабочий демонстрационный пример
Код директивы:
angular.module('MyApp').directive('maybeLink', function() {
return {
replace: true,
scope: {
maybeLink: '=',
maybeLinkText: '='
},
template: '<span>' +
' <span ng-if="!maybeLink.link" ng-bind-html="text"></span>' +
' <a ng-if="maybeLink.link" href="#" ng-bind-html="text"></a>' +
'</span>',
controller: function($scope) {
$scope.text = $scope.maybeLinkText.replace(/\n/g, '<br>');
}
};
});
Ответ 6
Ниже приведено решение, обеспечивающее динамические обновления. Использование:
<a rewrite-as-span="true"></a>
<a rewrite-as-span="false"></a>
<a rewrite-as-span="yourFn()"></a>
и т.д..
app.directive('rewriteAsSpan', function($compile){
return {
restrict: 'A',
template: '<span />',
replace: true,
// we transclude the element because when it gets replaced with the span
// we want all the properties from the original element
transclude: 'element',
compile: function(tElement, tAttrs){
return {
post: function(scope, element, attrs, controller, transclude){
var rewrittenEl, originalEl;
transclude(scope, function(clone){
originalEl = clone;
});
scope.$watch(attrs.rewriteAsSpan, function(value){
if (value === undefined || value === true){
if (!rewrittenEl){
// lazy compile and cache the rewritten element
transclude(scope, function(clone){
rewrittenEl = tElement;
rewrittenEl.html(clone.html());
// remove this directive because the $compile would get infinite
rewrittenEl.removeAttr('rewrite-as-span');
$compile(rewrittenEl)(scope);
});
}
element.replaceWith(rewrittenEl);
element = rewrittenEl;
} else {
element.replaceWith(originalEl);
element = originalEl;
}
});
}
};
}
};
});
Код и спецификации как gist