AngularJS - How do I change an element within a template that contains a data-binding?

爱⌒轻易说出口 提交于 2020-01-13 06:28:25

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!