Angular Directive DOM 操作行为

Posted

技术标签:

【中文标题】Angular Directive DOM 操作行为【英文标题】:Angular Directive DOM manipulation behavior 【发布时间】:2015-02-27 16:24:08 【问题描述】:

我在指令中使用 DOM 操作时出现了一些意外行为。我有两个图标,想要图标、电话和电子邮件,并希望在悬停时显示它们的内容。这应该很简单,但我在遍历 DOM 以到达子元素时遇到了麻烦。

这是我的指令:

app.directive('contact', function () 
return 
    restrict: 'E',
    scope: 
      email:'@',
      phone: '@',
      extension: '@'
    ,
    link: function (scope, element, attrs) 
        var el = element;
        var kids = $(el).children();
        var emailChild = $(kids[0]);
        var email = emailChild.context.children[0];

        element.children().bind('mouseenter', function() 
                console.log(this);
                console.log(emailChild.context.children[0]);
                console.log(email);
            );
    ,


    template: '<div class="contact_methods">' +
              '<div ng-if="email" class="email" href="#">' +
              '<div class="contact_info_email more_info" style="display:none;"><a ng-href="mailto:email">email</a></div>' +
              '</div>&nbsp;&nbsp;' +
              '<div ng-if="phone" class="phone" href="#"> ' +
              '<div class="contact_info_phone more_info">phone ext.extension</div>' +
              '</div>' +
              '</div>'




);

这是意外行为:

console.log(this);
console.log(emailChild.context.children[0]);
console.log(email);

这些评估为:

 <div class=​"contact_methods">​…​</div>​
 <div ng-if=​"email" class=​"email ng-scope" href=​"#">​…​</div>​
 undefined

电子邮件输出未定义;但是,它的内容是上面的那行吗?我也无法像 element.children().children() 那样深入研究它?悬停停止工作。

【问题讨论】:

出于某种原因 element.find('.email').bind('mouseenter', function() console.log('yes'); );不行吗?但是 element.bind('mouseenter', function() console.log('yes'); );会。 @byrdr 你没有 replace true,这意味着你的孩子实际上是模板的根。你为什么不只使用角度事件? 由于某种原因 replace:true 有另一个奇怪的效果。 element.children().bind('mouseenter', function() console.log(this); );不会返回任何东西。 【参考方案1】:

您不必在这里进行 DOM 访问,而只需使用 ng-events。在你的情况下ng-mouseenterng-mouseleave 结合ng-show

 <div ng-if="email" class="email" href="#" 
      ng-mouseenter="toggleEmail(true)" ng-mouseleave="toggleEmail(false)">Email 
      <div class="contact_info_email more_info" ng-show="displayEmail">
         <a ng-href="mailto:email">email</a></div>

在链接函数中:

link: function (scope, element, attrs) 
  scope.toggleEmail = function(shouldShow)
    scope.displayEmail = shouldShow;
  
,

示例演示

angular.module('app', []).controller('ctrl', function($scope) 
  $scope.email = "aaa@aa.com";
  $scope.phone = "111-222-3333";
).directive('contact', function() 
  return 
    restrict: 'E',
    scope: 
      email: '@',
      phone: '@',
      extension: '@'
    ,
    link: function(scope, element, attrs) 
      scope.toggleEmail = function(shouldShow) 
        scope.displayEmail = shouldShow;
      
    ,


    template: '<div class="contact_methods"> \
              <div ng-if="email" class="email" href="#" ng-mouseenter="toggleEmail(true)" ng-mouseleave="toggleEmail(false)">Email \
             <div class="contact_info_email more_info" ng-show="displayEmail"><a ng-href="mailto:email">email</a></div> \
             </div>&nbsp;&nbsp; \
             <div ng-if="phone" class="phone" href="#">Phone \
              <div class="contact_info_phone more_info">phone ext.extension</div> \
              </div> \
              </div>'


  

);;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <contact email="email" phone="phone"></contact>
</div>

我什至会为此指令使用控制器并在那里设置范围方法和变量。您也可以将模板放在外部而不是内联模板,因为它更大且难以在 javascript 文件中维护。

您的方法的问题是您在这里的选择器错误。因为实际的根将是元素 &lt;contact 所以孩子不是你所期望的。另请记住,您不需要再次用$(element) 包装element,因为它已经是一个jquery 包装器元素(前提是您在angular 之前包含jquery)。所以你也可以这样做(即使我会重新考虑这样做):

 //Let the directive render DOM first
 $timeout(function() 
    element.find('.email, .phone').bind('mouseenter mouseleave', function() 
      $(this).children().toggle()
    );
  );

angular.module('app', []).controller('ctrl', function($scope) 
  $scope.email = "aaa@aa.com";
  $scope.phone = "111-222-3333";
).directive('contact', function($timeout) 
  return 
    restrict: 'E',
    scope: 
      email: '@',
      phone: '@',
      extension: '@'
    ,
    link: function(scope, element, attrs) 
      $timeout(function() 
        element.find('.email, .phone').bind('mouseenter mouseleave', function() 
          $(this).children().toggle()
        );
      );
    ,
    template: '<div class="contact_methods">' +
      '<div ng-if="email" class="email" href="#"> Email' +
      '<div class="contact_info_email more_info" style="display:none;"><a ng-href="mailto:email">email</a></div>' +
      '</div>&nbsp;&nbsp;' +
      '<div ng-if="phone" class="phone" href="#"> Phone' +
      '<div class="contact_info_phone more_info" style="display:none;">phone ext.extension</div>' +
      '</div>' +
      '</div>'
  

);;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <contact email="email" phone="phone"></contact>
</div>

【讨论】:

谢谢你。我试图将所有内容都包含在内,这比深入研究子元素更有意义。 @byrdr 通常指令很难测试(单元测试),如果你自己在做 DOM 操作,那就忘了它。在指令中使用控制器的优点是,控制器易于测试,基本上您可以避免任何 DOM 操作以及角度方式。

以上是关于Angular Directive DOM 操作行为的主要内容,如果未能解决你的问题,请参考以下文章

angular源码分析 摘抄 王大鹏 博客 directive指令及系列

angular $compiler

Angular directive 实例详解

如何获取html元素id,通过angularjs

Angular2 指令:如何检测 DOM 更改

angular 自定义指令详解 Directive