How to replace an element in AngularJS directive linking function?

霸气de小男生 提交于 2019-12-03 01:52:46

问题


I'm creating a <row> AngularJS directive that needs to replace itself (the <row> tag must not be present in the DOM after execution) with a dynamic template that can contain any HTML code.

The problem in using replace: true is that it does not work with table's <tr> tags and that the template is dynamically chosen.

So I'm trying to find a way to replace the element in the linking function, with no success.

Using jQuery's .replaceWith() breaks ngRepeat for unknown reason.

Any hints?

Here is the fiddle


回答1:


Your fiddle seems pretty basic but you should be able to just use outerHTML

element[0].outerHTML ='<div>I should not be red</div>';

Updated fiddle

If you have to deal with ng-repeat you can bind your items to a scope property and reference them in your template that you compile. Once it is compiled you can use jQuery replaceWith()

html

<row items="items">***</row>

directive

.directive('row', function ($compile) {
    return {
        restrict: 'E',
        scope: {
            items: "="
        },
        link: function (scope, element, attrs) {
            var html ='<div ng-repeat="item in items">I should not be red</div>';
            var e =$compile(html)(scope);
            element.replaceWith(e);
        }
    };
});

ng-repeat example




回答2:


Mark's answer will work, however, that example is too limited to show the whole picture. Whereas Mark's directive might indeed suffice for common and simple UI components, for more complex operations, that pattern is one to be avoided. Below I explain in detail the reason behind this. In fact, Angular already provides a far simpler way to replace the directive element with a template. It can be found at the bottom of this answer.

Here is how a directive looks behind the scenes:

.directive('row', function ($compile) {
  return {
      restrict: 'E',
      scope: {
          items: "="
      },

      // Whether you define it this way or not, this is the order of
      // operation (execution) behind every AngularJS directive.
      // When you use the more simple syntax, Angular actually generates this
      // structure for you (this is done by the $compile service):

      compile: function CompilingFunction($templateElement, $templateAttributes, transcludeFn) {

        // The compile function hooks you up into the DOM before any scope is
        // applied onto the template. It allows you to read attributes from
        // the directive expression (i.e. tag name, attribute, class name or
        // comment) and manipulate the DOM (and only the DOM) as you wish.

        // When you let Angular generate this portion for you, it basically
        // appends your template into the DOM, and then some ("some" includes
        // the transclude operation, but that's out of the $scope of my answer ;) )

          return function LinkingFunction($scope, $element, $attrs) {

            // The link function is usually what we become familiar with when
            // starting to learn how to use directives. It gets fired after
            // the template has been compiled, providing you a space to
            // manipulate the directive's scope as well as DOM elements.

            var html ='<div ng-repeat="item in items">I should not be red</div>';
            var e = $compile(html)($scope);
            $element.replaceWith(e);
          };
      }
  };
});

What can we make out of that? It is obvious then, that manually calling $compile for the same DOM layout twice is redundant, bad for performance and bad for your teeth, too. What should you do instead? Simply compile your DOM where it should be compiled:

.directive('row', function ($compile) {
  return {
      restrict: 'E',
      template: '<div ng-repeat="item in items">I should not be red</div>',
      scope: {
          items: "="
      },

      compile: function CompilingFunction($templateElement, $templateAttributes) {
          $templateElement.replaceWith(this.template);

          return function LinkingFunction($scope, $element, $attrs) {
            // play with the $scope here, if you need too.
          };
      }
  };
});

If you want to dive in further under the hood of directives, here is what I like to call the Unofficial AngularJS Directive Reference

Once you're done with that head over here: https://github.com/angular/angular.js/wiki/Understanding-Directives


Now, as promised, here is the solution you came here for:

Using replace: true:

.directive('row', function ($compile) {
    return {
        restrict: 'E',
        template: '<div ng-repeat="item in items">I should not be red</div>',
        replace: true,
        scope: {
            items: "="
        }
    };
});


来源:https://stackoverflow.com/questions/17090964/how-to-replace-an-element-in-angularjs-directive-linking-function

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