15

Angular 为我们提供了一种编写指令的机制——它的功能非常强大。但我一直想知道的是——在什么情况下你应该真正编写你自己的自定义指令。

我们一直在 Stack Overflow 内部和周围看到各种问题,各种人试图编写(在我看来)不需要首先编写的指令。在大多数情况下,它们可以通过重复、切换和显示的组合来解决。请参阅包含指令的问题示例,我认为这些指令首先不应该是指令!

https://stackoverflow.com/questions/16101073/angularjs-directive-is-not-working-in-ie-10

AngularJS中的Fire按钮单击

angularjs:在 ui-bootstrap 模式中使用指令

一些示例场景。无论如何我都不会挑剔它们……因为我确信任何人都不清楚我们何时应该使用/编写指令。

我们看到人们使用指令作为模板机制的场景。这是正确的做事方式吗?或者,还有更好的方法?(也许是ng-include?)使用指令作为模板机制有什么好处/坏处吗?这个问题的原因是,有时我想知道人们是否编写指令,因为他们来自 jquery 世界,他们首先想到的是编写 DOM 操作代码,并且由于 Angular 的方式是不在控制器中操作 DOM,所以这一切都倾向于编写指令中的所有代码。

编辑 :

我相信这种混淆(在指令中推东西)的出现是因为 Angular 没有单独的“视图”概念 - 不像 Backbone(它只有一个“视图”但没有组件!)。指令在定义组件方面非常出色——但我认为如果你使用它们来创建“视图”,你将失去一些“角度”的方式。不过,这是我的观点——这就是为什么我要征求其他 Angular 社区的想法。

更简单的指令(只做一件事的指令!)的好处是它们绝对容易测试。如果您查看所有 ng 指令,它们都会做一件事并且做得很好。

在 Angular 中定义可重用“视图”(不是组件!)的最佳方法是什么?应该写在指令中吗?或者,还有更好的方法?

如果其中一位 Angular 开发人员对此事有意见,那就太棒了!

4

2 回答 2

11

嗯……很好的问题。

我相信指令主要是为了“扩展 HTML,以便您可以构建 DSL ”,提高生产力和代码质量。

问题是这是通过将事物组件化来实现的。但最重要的是,我们理解指令不仅仅与视觉组件有关,也不仅与模板有关,还与行为有关

总而言之,使用指令您可以:

  1. 创建一个 DSL 来增强元素的行为
  2. 创建DSL 小部件,这样您就可以停止重复自己
  3. 包装已经存在的组件,为您购买生产力。
  4. 优化

增强行为只不过是对行为进行组件化。ng-click,例如,将可点击行为添加到任何元素。想象一下,您正在创建一个包含数十个可拖动元素的应用程序。比您创建一个指令来增强元素行为,使其可拖动甚至无需触摸元素视觉 ( <span draggable>Test</span>)。再举一个例子,假设您将在鼠标悬停时获得特殊提示。title属性不适合这个,那么你可以创建你自己的my-title属性,在鼠标悬停时自动创建你的“特殊提示”(<span my-title="Some caption">Test</span>)。

在开发应用程序时,您有大量特定领域的概念和行为。例如,Stackoverflow 就有很强的投票概念。您可以投票赞成/反对问题、答案、评论......因此您可以创建一个可重用的votable指令,该指令将添加“投票行为”和“投票小部件”(向上/向下箭头)到实际任何元素。

最后一个给了我们另一张脸:模板。不仅适用于懒惰的人,而且遵循DRY 原则提高代码质量和可维护性。如果您正在重复控制器代码、HTML 结构或其他任何内容,为什么不对其进行模板化和组件化,对吧?Directive 是你这个工作的人选。

当然,你也有一些通用的指令应用。许多应用程序(不是全部)依赖于可点击元素,这就是为什么我们有一个ng-click,例如。许多应用程序都有上传区域。你会以 jQuery 的思维方式做什么?您将创建一个 jQuery 插件。对?在 Angular 中,您将创建一个 Angular Widget(使用指令)。您甚至可以使用指令包装一个已经存在的插件,并且再次增强其行为,以便它可以顺利地与您的应用程序对话。

关于包装插件,对于每个 jQuery 插件(但可能是 MooTools、Ext...),你必须创建一个控制器,调用$('element').plugin()它,并关心 jQuery 事件的变化并为你消化你的范围。这是指令的另一种完美用法。您可以创建一个适用.plugin()于您的元素的指令,监听事件并为您更改/消化您的范围。这就是Angular UI 项目的全部内容,看看吧。

最后一点是优化。我最近创建了一个创建具有动态列和行(网格)的表的应用程序。问题是数据是实时更新的!ng-repeat 中的 ng-repeat 用于创建标头,正在扼杀应用程序性能(每个周期中的嵌套循环$apply,每半秒发生一次)。因此,您可以创建一个指令来创建列模板并仍然ng-repeat在其中使用,但是您将仅在列版本时通过列循环。

所以,总结一下,我相信指令是关于组件化行为和形式(模板)的,它们可以提高生产力和代码质量

于 2013-04-20T14:20:51.437 回答
2

我个人写了很多指令,因为它们往往使我的程序更具声明性。

一个例子:在我最近制作的 JSON -> HTML 表单解析器中,我创建了一个“form-element”指令,它解析 JSON 元素并创建必要的指令,因为它是子元素。这样,我对每种字段类型都有一个指令,具有特定的行为和方法。此外,所有元素之间共享的任何共同行为都在 form-element 指令中。

这样,组元素就是模板中带有 ng-repeat 的指令,而标题元素就像 h1 一样简单。但是所有人都可以具有相同的条件行为(例如,只有在前一个字段具有特定值集时,组才能出现)。而且一切都非常干净 - 任何时候我需要添加/更改,一切都保持完美,并且 html 非常具有声明性。

编辑- 包括一段代码,根据评论的要求。

  /**
  * Form Element
  * ============
  *
  * Handles different elements:
  *   Assigns diferent directives according to the element type
  *   Instanstiates and maintains the active property on the formElem
  */
  .directive("formElement", ['$compile', function($compile){
    return{
        restrict: "E",
        scope:{formElemModel: '='},
        link: function(scope, element, attrs){
            var template = '';
            var type = scope.formElem.type;
            switch (type){
                case "field":
                    template = 
                        "<form-field-"+scope.formElemModel.fieldType+" ng-switch-when='true'>\
                        </form-field-"+scope.formElemModel.fieldType+">";
                    break;
                default:
                    template = "<form-"+type+" ng-switch-when='true' ></form-"+type+">";
                    break;
            }
            element.html(template);
            $compile(element.contents())(scope);

        // Active state of form Element
        scope.formElem.active = true;
        scope.testActive = function(){
          if(scope.$parent.formElem && scope.$parent.formElem.active == false){
            scope.formElem.active = false;
          }
          else{
            scope.formElem.active = 
              scope.meetsRequirements(scope.formElem.requirements);
          }
        }
        scope.$watch("meetsRequirements(formElem.requirements)", scope.testActive);
        scope.$watch("$parent.formElem.active", scope.testActive);
        }
    }
  }])
于 2013-04-09T10:32:25.857 回答