改进AngularJS指令代码

时间:2015-12-11 18:09:08

标签: javascript angularjs angularjs-directive

我写了一篇AngularJS指令,但我对此很新,而且我不知道我是否采用了#34; Angular方式" ...

以下是我的代码:http://plnkr.co/edit/X1tOk4z8f6dCK3mfB7HP?p=preview

HTML:

<!DOCTYPE html>
<html ng-app="app">

<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<meta charset=utf-8 />
<title>Directive Test</title>
<script src="script.js"></script>
</head>

<body ng-controller="MainCtrl">

  <button id="button1" ng-click="dummyClickFoo()" wait-button="foo"><i></i> Foo</button>
  <button id="button2" ng-click="dummyClickBar()" wait-button="bar"><i></i> Bar</button>

</body>

</html>

JS:

app = angular.module('app', []);

app.controller('MainCtrl', function($scope) {
    $scope.dummyClickFoo = function() {
        $scope.startSpinner('foo');

        setTimeout(function() {
                $scope.stopSpinner('foo');
          }, 3000);
    };

    $scope.dummyClickBar = function() {
        $scope.startSpinner('bar');

        setTimeout(function() {
                $scope.stopSpinner('bar');
          }, 3000);
    };
});


app.directive('waitButton', function() {
    return {
        restrict: 'A',
        controller: ['$scope', '$element', function($scope, $element) {
            $scope.startSpinner = function(id) {
                var el = angular.element(document.querySelector('[wait-button="'+id+'"]'));
                el.children('i').text('searching...');
            };

            $scope.stopSpinner = function(id) {
                var el = angular.element(document.querySelector('[wait-button="'+id+'"]'));
                el.children('i').empty();
            };
        }]
    };
});

我发现document.querySelector('[wait-button="'+id+'"]')部分,它有点&#34;讨厌&#34; ...(或不是?);否则我不知道在同一个控制器中不同时间重复使用同一指令的更好方法。 有人可以建议我更好的代码吗?

谢谢。

4 个答案:

答案 0 :(得分:2)

访问指令

中的元素

我主张将link函数用于此类事情:

link: function($scope, elem, attrs){ /* do something w. elem */ }

在控制器中访问您的元素并不是非常 angular-ish 。这是link&amp;的全部观点。指令对象的compile函数....

......但在极少数情况下这是合理的。控制器中注入的$element引用了angular.element(document.querySelector('[wait-button="'+id+'"]'))代码正在执行的操作。此时您只需要使用$element。但是,我可以推荐一种更有棱角的方法吗?

沟通。控制器和指令

另一个问题是你如何基本上将指令的意图传达给主控制器并返回指令。您的用例与大多数用例略有不同,因为您具有异步性质。

我做了一个利用隔离范围和回调参数的例子。在大多数现实场景中,您将处理异步回调的承诺。因此,我使用promises中的.finally逻辑来执行回调,该回调与指令进行通信,无论异步逻辑已经包含了什么。

我的例子中要记住的事情:

  • 我使用了coffeescript,因为我的方式是理智的
  • 我使用CSS / DOM驱动如何指令的加载状态,而不是尝试以编程方式执行。在我的书中,程序化DOM操作非常反NG。指令为您提供足够的声明性地执行此类操作。
  • 我没有使用指令控制器,因为除非你要为你的指令使用模板,否则你真的不需要自定义控制器。当你使用link函数而不是自定义指令控制器时,会有一个模糊的行。
  • 哦......我使用*控制器作为语法,因为如果你读到关于NG走向的任何内容,他们就会偏离整个$scope范例。

plunker - http://plnkr.co/edit/0AvlCQW5qqkpYKl2WpB3?p=preview

声明ui

主控制器

.controller 'MainCtrl', class MainCtrl
  @$inject = [
    '$scope'
    '$interval'
  ]

  constructor: ($scope, @$interval)->
    @viewData = 'Skynet 2.0'
    @isLoading = false

  callbackExample: ($callbackFunc)->
    @loadRqst()
    .finally -> $callbackFunc?()

  loadRqst: ->
    @isLoading = 1
    # this returns a promise which gets processed in the example functions
    @$interval => 
      console.log @isLoading++
    , 250, 10 
    .finally => 
      @isLoading = false

实施ui

<button callback-btn="vc.callbackExample($callbackFunc)">
  Callback Example<i> - I'm loading & I'm #1</i>
</button>

<button callback-btn="vc.callbackExample($callbackFunc)">
  Callback Example<i> - Look I can load too, I'm #2</i>
</button>

<强> CSS

[callback-btn] i{
  display: none;
}

[callback-btn].loading i{
  display: initial;
}

指令

.directive 'callbackBtn', ($parse)->
  dir =
    restrict: 'A' 
    scope: { callbackBtn: '&' }
    link: ($scope, elem, attrs)->

      onCallback = ->
        console.log 'on callback'
        elem.removeClass 'loading'

      elem.on 'click', ->
        elem.addClass 'loading'
        $scope.$apply ->
          $scope.callbackBtn({$callbackFunc: onCallback})

答案 1 :(得分:1)

我强烈建议您转换

<button id="button1" ng-click="dummyClickFoo()" wait-button="foo"><i></i> Foo</button>

进入指令,所以你的代码将是

<my-button  label="foo"></my-button>

答案 2 :(得分:1)

directive的整个想法是为了促进关注点的分离。

当你使用指令时,它的所有功能都应由指令本身来处理。

现在,你的MainCtrl中有一部分指令的逻辑,理想情况下应该在你的指令中。

此外,在您的指令中,还有一个控制器功能。理想情况下,控制器函数应该只处理视图模型之间传递的数据等。

对于DOM manilulations,你正在做的事情,它应该在link函数中实现。

因此,执行相同操作的angular way如下所示。

你的Html

<body ng-controller="MainCtrl">

  <button id="button1" wait-button="foo"><i></i> Foo</button>
  <button id="button2" wait-button="bar"><i></i> Bar</button>
  <!-- NOTICE: No ng-click handlers here -->
</body>

您的MainCtrl

app.controller('MainCtrl', function($scope) {
    // No code required here. This will be handled in Directive's link function.
});

您的waitButton指令。

    app.directive('waitButton', function() {
        return {
            restrict: 'A',
            controller: ['$scope', '$element', function($scope, $element) {
//Again, it's not controller's job to handle DOM manipulations. So code required here.

            }],
            link: function($scope, $element){
              var waitButtons = angular.element(document.querySelectorAll('[wait-button]'));// NOTE: I've used querySelectorAll instead of querySelector. This will make this function generic.
              waitButtons.on('click', function(){
                var $this = angular.element(this);
                startSpinner($this);
                setTimeout(function(){
                  stopSpinner($this);
                },3000);
              });

               function startSpinner(el) {
                    el.children('i').text('searching...');
                }

               function stopSpinner(el) {
                    el.children('i').empty();
                }

            }
        };
    });

答案 3 :(得分:0)

将您的函数移动到指令中。将单击处理程序放在链接函数中。

app.directive('waitButton', function($timeout) {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
                   var startSpinner = function() {
                       elem.children('i').text('searching...');
                   };
                   var stopSpinner = function() {
                       elem.children('i').empty();
                   };
                   var clickHandler = function() {
                       startSpinner();
                       $timeout(stopSpinner,3000);
                   };
                   elem.on("click", clickHandler);
               }
     }
});

还使用AngularJS $timeout服务。有关详细信息,请参阅AngularJS $timeout service API Reference