在angularJS中处理DOM:最佳做法? [英] Manipulating DOM in angularJS : best practice?

查看:52
本文介绍了在angularJS中处理DOM:最佳做法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用AngularJS构建大型Web应用程序. 对于不同的情况,我们经常使用自定义指令.在进行DOM操作,绑定事件等操作时...我们在自定义指令的link函数中定义了用于操作DOM的函数,但我们从控制器中调用了该函数(我们在,因此可以通过给定的控制器进行访问).我认为做到这一点的有角度的方法是为每个函数定义一个单独的自定义指令,并直接从模板中使用它,但是在我们的情况下,我不知道这样做会有什么好处,我们已经很多自定义指令,所以BAD要做我们正在做的事情(定义在指令中操作DOM并从控制器调用它的函数),甚至具有一定意义,或者就像我们在控制器中操作DOM一样?对我们来说,这是一种关注点分离,我们从来没有定义仅在指令中操作控制器中DOM的函数,但从控制器中调用它似乎并不正确,是吧?

We are building a big web application using AngularJS. We use custom directive a lot for different cases. When it comes to do DOM manipulation, binding event, etc...It happens, that we define functions that manipulates the DOM in a custom directive's link function, but we call it from the controller (we define functions in the $scope so it can be accessible by the given controller). I think the angular way to do it, would be to define a separate custom directive for each function and use it directly from the template but in our case i don't know to what point it will be confortable to do so, we have already a lot of custom directive, so is it BAD to do what we are doing (defining function that manipulate the DOM in a directive and call it from controller), does that even has a meaning or it's just like we are manipulating the DOM in controller ? For us, it's kinda of concern separation, we never define function that manipulate the DOM in controller, only in directive, but call it from the controller doesn't seems to be so right, is it?

显示我们的自定义指令外观的示例:

An example showing how our custom directive look like:

angular.module('exp', []).directive('customdirectiveExp', ['', function(){
// Runs during compile
return {
    name: 'customDirectiveExp',
    controller: "ControllerExp",
    controllerAs: "ctrl",
    templateUrl: 'templateExp',
    link: function($scope, iElm, iAttrs, controller) {

        /* These function will be called from the ControllerExp when it needs so.
         Function can do any things like manipulating the DOM, addin
         event listner ...
        */
        scope.manipulateDom1 = function(){
            // DOM manipualtion
        };

        scope.manipulateDom2 = function(){
            // DOM manipualtion
        };

        scope.manipulateDom3 = function(){
            // DOM manipualtion
        };

    }
};
}]);

推荐答案

我认为不要从控制器操作DOM"的口号是从过去开始的,那时指令主要/仅用于链接功能(或指令控制器在哪里,只是与其他指令互通的一种方式.

I think the "don't manipulate the DOM from controllers" mantra is back from the days, when directives mainly/only used linking functions (or directive controllers where just a way to intercommunicate with other directives).

当前建议的最佳实践是使用组件"(可以通过指令实现),基本上所有指令逻辑都留在控制器中. (例如,请注意,在Angular 2中没有链接功能,每个组件/指令基本上都是一个类/控制器(加上一些元数据).)

The currently suggested best practice is to use "components" (which can be realized via directives), where basically all the directive logic leaves in the controller. (Note for example that in Angular 2 there is no linking functions and each component/directive is basically a class/controller (plus some metadata).)

在这种情况下,我认为从指令控制器内的指令模板中操作DOM是完全可以的.

In that context, I believe it is perfectly fine to manipulate the DOM in a directive's template from within the directive's controller.

该想法是使模板/HTML保持声明性.比较以下片段:

The idea is to keep your templates/HTML declarative. Compare the following snippets:

<!--
  `SomeController` reaches out in the DOM and
  makes changes to `myComponent`'s template --- BAD
-->
<div ng-controller="SomeController">
  ...
  <my-component></my-component>
  ...
</div>

vs

<div ng-controller="SomeController">
  ...
  <!--
    `myComponent`'s controller makes changes to
    `myComponent`'s template --- OK
  -->
  <my-component></my-component>
  ...
</div>

在第一个(错误的)示例中,myComponent将具有不同的行为/外观,具体取决于它在DOM中出现的位置(例如,它在SomeController下吗?).更重要的是,很难找出其他(无关)部分可能会改变myComponent的行为/外观.

In the first (bad) example, myComponent will have different behavior/appearance depending on where in the DOM it appears (e.g. is it under SomeController ?). What's more important, it is very hard to find out what other (unrelated) part might be changing myComponent's behavior/appearance.

在第二个(好的)示例中,myComponent的行为和外观在整个应用程序中将是一致的,并且很容易找出其含义:我只需要查看指令的定义(一个地方).

In the second (good) example, myComponent's behavior and appearance will be consistent across the app and it is very easy to find out what it will be: I just have to look in the directive's definition (one place).

尽管有一些警告:

  1. 您不想将DOM操作代码与其他逻辑混合使用. (这会使您的代码难以维护且难以测试).

  1. You don't want to mix your DOM manipulation code with other logic. (It would make your code less maintainable and harder to test).

通常,当所有子项都位于适当位置(编译+链接)时,通常要在链接后阶段中操作DOM.在控制器实例化期间运行DOM操作代码将意味着尚未处理模板内容.

Often, you want to manipulate the DOM in the post-linking phase, when all children are in place (compiled + linked). Running the DOM manipulation code during controller instantiation would mean that the template content has not been processed yet.

通常,当您未在指令的上下文中实例化控制器时,您不想运行DOM操作,因为这意味着您始终需要已编译的模板才能测试控制器.这是不希望的,因为即使您只想测试与DOM/HTML不相关的控制器逻辑的其他部分,它也会使单元测试变慢.

Usually, you don't want to run the DOM manipulation when your controller is not instantiated in the context of a directive, because that would mean you always need a compiled template in order to test your controller. This is undesirable, because it makes unit tests slower, even if you only want to test other parts of the controller logic that are not DOM/HTML related.

那么,我们该怎么办?

  • 通过专用功能隔离DOM操作代码.适当时将调用此函数(请参见下文),但是所有DOM交互都将集中在一个位置,这使得查看起来更加容易.

  • Isolate your DOM manipulation code in a dedicated function. This function will be called when appropriate (see below), but all DOM interaction will be in one place, which makes it easier to review.

将该函数公开为控制器方法,并从指令的链接函数中调用它(而不是在控制器初始化期间).这样可以确保DOM处于所需状态(如果需要),并且还可以将独立"控制器实例与DOM操作分离.

Expose that function as a controller method and call it from your directive's linking function (instead of during controller initialization). This ensures that the DOM will be in the desired state (if that is necessary) and also decouples "stand-alone" controller instantiation from DOM manipulation.

我们获得的收获:

  • 如果将控制器实例化为指令的编译/链接的一部分,则将按预期方式调用该方法并操纵DOM.

  • If your controller is instantiated as part of directive's compiling/linking, the method will be called and the DOM will be manipulated, as expected.

在单元测试中,如果不需要DOM操作逻辑,则可以直接实例化控制器并测试其业务逻辑(独立于任何DOM或编译).

In unit-tests, if you don't need the DOM manipulation logic, you can instantiate the controller directly and test it's business logic (independently of any DOM or compiling).

您可以更好地控制DOM操作的发生时间(在单元测试中).例如.您可以直接实例化控制器,但仍要传递$element,进行您可能要进行的任何声明,然后手动调用DOM操纵方法并声明元素已正确转换.传递模拟的$element和诸如添加事件侦听器之类的内容也更加容易,而无需设置真实的DOM.

You have more control over when the DOM manipulation happens (in unit tests). E.g. you can instantiate the controller directly, but still pass in an $element, make any assertions you might want to make, then manually call the DOM-manipulating method and assert that the element is transformed properly. It is also easier to pass in a mocked $element and stuff like adding event listeners, without having to set up a real DOM.

这种方法的缺点(公开方法并从链接函数调用它)是多余的样板.如果您使用的是Angular 1.5.x,则可以使用指令控制器生命周期挂钩(例如$onInit$postLink)来节省样板,而无需具有链接功能,只是为了握住控制器并调用一种方法就可以了. (附加功能:将1.5.x组件语法与生命周期挂钩一起使用,将使其更容易迁移到Angular 2.)

The downside of this approach (exposing method and calling it from the linking function), is the extra boilerplate. If you are using Angular 1.5.x, you can spare the boilerplate by using the directive controller lifecycle hooks (e.g. $onInit or $postLink), without a need to have a linking function, just to get hold of the controller and call a method on it. (Bonus feature: Using the 1.5.x component syntax with lifecycle hooks, would make it easier to migrate to Angular 2.)

示例:

在v1.5.x之前

.directive('myButton', function myButtonDirective() {
  // DDO
  return {
    template: '<button ng-click="$ctrl.onClick()></button>',
    scope: {}
    bindToController: {
      label: '@'
    }
    controllerAs: '$ctrl',
    controller: function MyButtonController($element) {
      // Variables - Private
      var self = this;

      // Functions - Public
      self._setupElement = _setupElement;
      self.onClick = onClick;

      // Functions - Definitions
      function _setupElement() {
        $element.text(self.label);
      }

      function onClick() {
        alert('*click*');
      }
    },
    link: function myButtonPostLink(scope, elem, attrs, ctrl) {
      ctrl._setupElement();
    }
  };
})

v1.5.x之后

.component('myButton', {
  template: '<button ng-click="$ctrl.onClick()></button>',
  bindings: {
    label: '@'
  }
  controller: function MyButtonController($element) {
    // Variables - Private
    var self = this;

    // Functions - Public
    self.$postLink = $postLink;
    self.onClick = onClick;

    // Functions - Definitions
    function $postLink() {
      $element.text(self.label);
    }

    function onClick() {
      alert('*click*');
    }
  }
})

这篇关于在angularJS中处理DOM:最佳做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆