6

考虑以下指令:(现场演示)

app.directive('spinner', function() {
  return {
    restrict: 'A',
    scope: {
      spinner: '=',
      doIt: "&doIt"
    },
    link: function(scope, element, attrs) {
      var spinnerButton = angular.element("<button class='btn disabled'><i class='icon-refresh icon-spin'></i> Doing...</button>");
      element.after(spinnerButton);

      scope.$watch('spinner', function(showSpinner) {
        spinnerButton.toggle(showSpinner);
        element.toggle(!showSpinner);
      });
    }
  };
}); 

像这样使用:

<button ng-click="doIt()" spinner="spinIt">Spin It</button>

spinner' 的值(即$scope.spinIt本例中的 的值)为true时,该元素应该被隐藏而spinnerButton应该出现。当spinner的值为时false,该元素应该是可见的并且spinnerButton应该是隐藏的。

这里的问题doIt()是不在隔离范围内,因此不会在点击时调用。

实现该指令的“Angular 方式”是什么?

4

7 回答 7

8

我的建议是看看这些微调器发生了什么。更加专注于 API

相关部分如下。我们使用常规回调来指示何时完成,因此微调器知道重置按钮的状态。

function SpinDemoCtrl($scope, $timeout, $q) {
  $scope.spinIt = false;

  $scope.longCycle = function(complete) {
    $timeout(function() {
      complete();
    }, 3000);
  };

  $scope.shortCycle = function(complete) {
    $timeout(function() {
      complete();
    }, 1000);
  };
}

app.directive('spinnerClick', function() {
  return {
    restrict: 'A',
    scope: {
      spinnerClick: "=",
    },
    link: function(scope, element, attrs) {
      var spinnerButton = angular.element("<button class='btn disabled'><i class='icon-refresh icon-spin'></i> Doing...</button>").hide();
      element.after(spinnerButton);

      element.click(function() {
        spinnerButton.show();
        element.hide();

        scope.spinnerClick(function() {
          spinnerButton.hide();
          element.show();
        });
      });
    }
  };
});

这是一个期望使用$q. 它将更好地与 Angular 风格的异步操作一起工作,并通过在履行承诺时重置微调器来消除回调函数。

于 2013-05-28T10:54:51.537 回答
2

这是我最终得到的指令的完善版本(基于 Yuki 的建议),以防它帮助某人:(CoffeeScript)

app.directive 'spinnerClick', ->
  restrict: 'A'
  link: (scope, element, attrs) ->
    originalHTML = element.html()
    spinnerHTML = "<i class='icon-refresh icon-spin'></i> #{attrs.spinnerText}"

    element.click ->
      return if element.is('.disabled')

      element.html(spinnerHTML).addClass('disabled')

      scope.$apply(attrs.spinnerClick).then ->
        element.html(originalHTML).removeClass('disabled')

像这样使用它:

<button class="btn btn-primary" spinner-click="createNewTask()" 
                                spinner-text="Creating...">
  Create
</button>

控制器代码:

TasksNewCtrl = ($scope, $location, $q, Task) ->
  $scope.createNewTask = ->
    deferred = $q.defer()

    Task.save $scope.task, ->
      $location.path "/tasks"
    , (error) ->
      // Handle errors here and then:
      deferred.resolve()

    deferred.promise
于 2013-05-29T00:08:19.573 回答
1

是的,它会在您的隔离范围内调用 doIt。

在这种情况下,您可以使用 $parent.doIt

<button ng-click="$parent.doIt()" spinner="spinIt">Spin It</button>
于 2013-05-27T14:39:55.287 回答
1

来自 AngularJS 文档(http://docs.angularjs.org/guide/directive):

& 或 &attr - 提供一种在父作用域的上下文中执行表达式的方法。如果未指定 attr 名称,则假定属性名称与本地名称相同。给定范围的小部件定义:{ localFn:'&myAttr'},则隔离范围属性 localFn 将指向 count = count + value 表达式的函数包装器。通常希望通过表达式将数据从隔离范围传递到父范围,这可以通过将局部变量名称和值的映射传递到表达式包装器 fn 来完成。例如,如果表达式是 increment(amount),那么我们可以通过将 localFn 调用为 localFn({amount: 22}) 来指定数量值。

所以包含doIt: "&doIt"在你的范围声明中,然后你可以在你的隔离范围内使用 doIt 作为一个函数。

于 2013-05-28T01:14:31.023 回答
0

我很困惑为什么您没有将指令中的所有内容都打包成一个独立的模块。至少我会这样做。换句话说,您在 HTML 中有点击处理程序,在指令中有一些行为,在外部控制器中有一些行为。这使您的代码的可移植性和分散性大大降低。

无论如何,您可能有不共享的原因,但我的建议是将所有“Spin It”相关的东西放在 spinner 指令中。这意味着点击处理程序、doIt()函数和模板都在链接函数中。

这样就无需担心共享范围和代码纠缠。或者,我只是错过了什么吗?

于 2013-05-27T22:17:14.490 回答
0

我不知道做事的“角度”方式,但我建议不要使用孤立的范围,而只是创建一个子范围。然后attrs.$observe,您可以获得指令所需的任何属性。

IE :

app.directive('spinner', function() {
    return {
        restrict: 'A',
        scope: true, //Create child scope not isolated scope
        link: function(scope, element, attrs) {
            var spinnerButton = angular.element("<button class='btn disabled'><i class='icon-refresh icon-spin'></i> Doing...</button>");
            element.after(spinnerButton);

            //Using attrs.$observe
            attrs.$observe('spinner', function(showSpinner) {
            spinnerButton.toggle(showSpinner);
            element.toggle(!showSpinner);
            });
        }
    };
});

我发现这种方式比使用 '$parent' 来逃避其他指令(例如 ngClick 或 ngModel)中的隔离范围要好,因为您的指令的最终用户不需要知道使用您的指令是否需要他们使用 '$ parent' 或不在核心 angularjs 指令上。

于 2013-05-28T07:27:36.370 回答
0

使用CoffeeScriptFontAwesome图标。

  • 无需手动指定微调器文本
  • 它只会在加载时添加文本左侧的微调器内容
  • 我们必须使用finally而不是then作为 promise 否则 spinner 会在失败时停留在那里?
  • 我必须使用 $compile 因为按钮的内容是动态编译的,因为我正在使用https://github.com/angular-translate/angular-translate

app.directive 'spinnerClick', ["$compile", ($compile) ->
    restrict: 'A'
    link: (scope, element, attrs) ->
        originalHTML = element.html()
        spinnerHTML = "<i class='fa fa-refresh fa-spin'></i> "

        element.click ->
            return if element.is('.disabled')
            element.html(spinnerHTML + originalHTML).addClass('disabled')
            $compile(element.contents())(scope)
            scope.$apply(attrs.spinnerClick).finally ->
                element.html(originalHTML).removeClass('disabled')
                $compile(element.contents())(scope)
]
于 2014-05-01T01:37:23.660 回答