问题
What is the most Angular recommended way to use a dynamic tag name in a template?
I have a drop-down containing h1-h6 tags. A user can choose any of these and the content will change to being wrapped by the chosen header tag (which is stored on the $scope). The content is bound to the model i.e. within {{ }}.
To persist the binding I can change the markup and use $compile. However, this does not work because it gets appended (obviously) before Angular replaces the {{ }} with model values. It's h3 on page load.
Example:
<div id="root">
<h3 id="elementToReplace">{{ modelData }}</h3>
</div>
When re-compiling I have tried using a string as follows:
<{{ tag }} id="elementToReplace">{{ modelData }}</{{ tag }}>
Any ideas?
回答1:
Demo Plunker Here
Define a scope variable named 'tag' and bind it to both your select list and custom directive.
HTML:
<select ng-model="tag" ng-init="tag='H1'">
<option ng-value="H1">H1</option>
<option ng-value="H2">H2</option>
<option ng-value="H3">H3</option>
<option ng-value="H4">H4</option>
<option ng-value="H5">H5</option>
</select>
<tag tag-name="tag">Hey There</tag>
Next, pass the tag scope model into your directive using two-way model binding:
var app = angular.module('app',[]);
app.directive('tag', function($interpolate) {
return {
restrict: 'E',
scope: {
tagName: '='
},
link: function($scope, $element, $attr) {
var content = $element.html();
$scope.$watch('tagName', function(newVal) {
$element.contents().remove();
var tag = $interpolate('<{{tagName}}>{{content}}</{{tagName}}>')
({tagName: $scope.tagName, content: content});
var e = angular.element(tag);
$element.append(e);
});
}
}
});
Notice that in the custom directive, we are using the $interpolate service to generate the HTML element based on the Tag that was selected in the select list. A $watch function is used to watch for changes to the tag model, and when it changes, the new element is appended to the DOM.
回答2:
Here is one I knocked up, even though you said you didn't want it ;)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="../lib/jquery.js"></script>
<script src="../lib/angular.js"></script>
<script>
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function ($scope) {
$scope.modelData = "<h1>Model Data</h1>" +
"<p id='replace'>This is the text inside the changing tags!</p>";
$scope.tags = ["h1", "h2", "h3", "h4", "p"];
$scope.selectedTag = "p";
}]);
app.directive("tagSelector", function(){
return {
resrict: 'A',
scope: {
modelData: '@',
selectedTag: '@'
},
link: function(scope, el, attrs){
scope.$watch("selectedTag", updateText);
el.prepend(scope.modelData);
function updateText(){
var tagStart = "<" + scope.selectedTag + " id='replace'>";
var tagEnd = "</" + scope.selectedTag + ">";
$("#replace").replaceWith(tagStart + $("#replace").html() + tagEnd);
}
}
}
});
</script>
</head>
<body ng-app="app">
<div ng-controller="ctrl">
<select ng-model="selectedTag" ng-options="tag for tag in tags"></select>
<div tag-selector selected-tag="{{selectedTag}}" model-data="{{modelData}}"></div>
</div>
</body>
</html>
回答3:
More kosher version. Work good with ng-repeat and nested directives. Working example here.
angular.module('myApp', [])
.directive('tag', function($interpolate, $compile) {
return {
priority: 500,
restrict: 'AE',
terminal: true,
scope: {
tagName: '='
},
link: function($scope, $element) {
$scope.$on('$destroy', function(){
$scope.$destroy();
$element.empty();
});
$scope.$parent.$watch($scope.tagName, function(value) {
$compile($element.contents())($scope.$parent, function(compiled) {
$element.contents().detach();
var tagName = value || 'div';
var root = angular.element(
$element[0].outerHTML
.replace(/^<\w+/, '<' + tagName)
.replace(/\w+>$/, tagName + '>'));
root.append(compiled);
$element.replaceWith(root);
$element = root;
});
});
}
}
})
.controller('MyCtrl', function($scope) {
$scope.items = [{
name: 'One',
tagName: 'a'
}, {
name: 'Two',
tagName: 'span'
}, {
name: 'Three',
}, {
name: 'Four',
}];
});
Usages:
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<tag class="item" tag-name="'item.tagName'" ng-repeat="item in items">
{{item.name}}
</tag>
</div>
</div>
来源:https://stackoverflow.com/questions/24299175/angularjs-how-do-i-change-an-element-within-a-template-that-contains-a-data-bi