Angular为我们提供了一种编写指令的机制 - 它的功能非常强大。但我一直想知道的是 - 在什么情况下你应该实际编写自己的自定义指令。
我们不断看到Stack Overflow及其周围的问题,各种人试图编写指令(我认为)不需要首先编写。在大多数情况下,它们可以通过重复,切换和显示的组合来解决。请参阅包含我认为不应该是指令的指令的问题示例!
https://stackoverflow.com/questions/16101073/angularjs-directive-is-not-working-in-ie-10
Fire button click in AngularJS
angularjs: using a directive inside the ui-bootstrap modal
一些示例场景。无论如何我都不会选择它们。因为我确信当我们应该使用/编写指令时,任何人都不清楚。
我们看到人们使用指令作为模板机制的场景。这是正确的做事方式吗?或者,还有更好的方法? (ng-include可能?)使用指令作为模板机制是否有任何好处/缺点?这个问题的原因是有时候我想知道人们是否编写指令因为来自jquery世界他们能想到的第一件事就是编写DOM操作代码,因为Angular方法是不操纵控制器中的DOM所有都倾向于写作指令中的所有代码。
编辑:
我相信这种混淆(在指令中推送内容)是因为Angular没有单独的“视图”概念 - 不像Backbone(它只有一个“视图”但没有组件!)。指令在定义组件方面是惊人的 - 但我认为如果你使用它们创建“视图”,你将失去一些“有角度”的方式。这是我的意见 - 这就是为什么我要征求其他角度社区的想法。
更简单的指令(只做一件事的指令!)的好处是它们绝对容易测试。如果你看看所有的ng指令,他们都会做一件事并做得很好。
在Angular中定义可重用“视图”(不是组件!)的最佳方法是什么?这应该写在指令中吗?或者,还有更好的方法?
如果其中一位Angular Dev在这件事上有意见,那就太棒了!
答案 0 :(得分:11)
嗯......这是一个很好的问题。
我认为指令主要是“extending HTML so you can build a DSL”,提高了工作效率和代码质量。
问题是这是通过组件化来实现的。但是最重要,我们理解该指令不仅仅是关于视觉组件,不仅仅是模板化,而且关于行为。
总结一下,使用指令你可以:
增强行为只不过是组件行为。例如,ng-click
将可点击行为添加到任何元素。想象一下,你正在创建一个包含数十个可拖动元素的应用程序。比你创建一个指令来增强元素行为,使它可以拖动,甚至没有触及元素visual(<span draggable>Test</span>
)。还有一个例子,想象一下你会对鼠标悬停有特别的暗示。 title
属性不适用于此,您可以创建自己的my-title
属性,在鼠标悬停时自动创建“特殊提示”(<span my-title="Some caption">Test</span>
)。
在开发应用程序时,您拥有大量特定于域的概念和行为。例如,Stackoverflow具有强烈的投票概念。你可以投票上/下问题,答案,评论...所以你可以创建一个可重复使用的votable
指令,它会将“投票行为”和“投票控件”(向上/向下箭头)添加到praticaly任何元素。
这最后一个给了我们另一面:模板。不仅适用于懒惰,而且可以提高DRY principle之后的代码质量和可维护性。如果您正在重复控制器代码,HTML结构或其他任何内容,为什么不进行模板化和组件化,对吧?指令是你的工作。
当然,您也有一些通用的指令应用程序。许多应用程序(并非所有应用程序)都依赖于可点击元素,这就是为什么我们有ng-click
的原因。许多应用都有上传区域。你会用jQuery的思维方式做什么?你会创建一个jQuery插件。对?在Angular中,您将创建一个Angular Widget(使用指令)。您甚至可以使用指令包装已存在的插件,再一次扩充其行为,以便它可以顺利地与您的应用程序通信。
关于包装插件,对于每个jQuery插件(但可能是MooTools,Ext ......),你将不得不创建一个控制器,在其上调用$('element').plugin()
,并关心它jQuery事件会更改并为您消化范围。这是指令的另一个完美用法。您可以创建一个指令,在您的元素上应用.plugin()
,监听事件并为您更改/消化范围。这就是Angular UI Project的全部内容,请查看。
最后一点是优化。我最近创建并创建了一个用动态列和行(网格)创建表的应用程序。问题是数据是实时更新的!并且ng-repeat内部的ng-repeat用于创建头文件,这会破坏应用程序性能(每$apply
个循环中的嵌套循环,每半秒发生一次)。因此,您可以创建一个创建列模板的指令,并且仍然在其中使用ng-repeat
,但只有在列版本时才会有一个循环遍历列。
所以,总结一下,我相信指令是关于行为和形式(模板)的组件化,这会给你带来生产力和代码质量。
答案 1 :(得分:2)
我亲自写了很多指令,因为它们往往使我的程序更具说明性。
JSON中的示例: - &gt;我最近制作的HTML表单解析器,我创建了一个“表单元素”指令,它解析JSON元素a创建必要的指令作为它的子节点。这样我就有了每个字段类型的指令,具有特定的行为和方法。此外,所有元素之间共享的任何常见行为都在form-element指令中。
这样,group元素是一个在其模板中带有ng-repeat的指令,title元素就像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);
}
}
}])