从控制器中触发jQuery DOM Manipulation的正确方法是什么?

Dan*_*anH 7 javascript angularjs angularjs-controller

所以我一直在阅读控制器中的jQuery操作是不好的做法,但我不清楚为什么或如何纠正.

下面是来自Youtube教程的代码,即使是视频创建者的评论也是一个坏主意,但无法解释为什么并继续使用不良行为.

来自https://www.youtube.com/watch?v=ilCH2Euobz0#t=553s:

$scope.delete = function() {
    var id = this.todo.Id;
    Todo.delete({id: id}, function() {
        $('todo_' + id).fadeOut();
    });
};
Run Code Online (Sandbox Code Playgroud)

解决方案:

根据兰登在下面的回答,我已经得到了以下我自己的工作代码,这些代码与上面的示例代码略有不同:

var ProjectListCtrl = function ($scope, Project) {
    $scope.projects = Project.query();
    $scope.delete = function() {
        var thisElem = this;
        var thisProject = thisElem.project;
        var id = thisProject.id;
        Project.delete({id: id}, function() {
            var idx = $scope.projects.indexOf(thisProject);
            if (idx !== -1) {
                thisElem.destroy('removeItem('+idx+')');
            }
        });
    }

    $scope.removeItem = function(idx) {
        $scope.projects.splice(idx, 1);
    }

}

app.directive('fadeOnDestroy', function() {
    return function(scope, elem) {
        scope.destroy = function(funcComplete) {
            elem.fadeOut({
                complete: function() {
                    scope.$apply(funcComplete)
                }
            });
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

这与兰登在几个方面的答案不同.我想避免在ngClick回调中添加一个参数,所以我将其存储在thisProject.此外,示例和我的代码需要destroy$http成功回调中调用,因此this不再相关,我将点击的元素存储在thisElem.

更新2:

进一步更新了我的解决方案,以反映funcComplete实际上并未修改原始$ scope.

Lan*_*don 9

处理此问题的Angular方法是通过指令.我找到了一个完美的例子来涵盖你在下面提到的内容,虽然它并不像我想的那样干净.我们的想法是创建一个用作HTML属性的指令.当元素绑定到控制器的范围时,将link触发该函数.该函数淡化元素(完全可选)并为您的控制器公开一个destroy方法以便稍后调用.

更新:根据注释修改以实际影响范围.没有兴奋的解决方案,它甚至更为jankier因为原作者complete.apply(scope)在他的destroy回调中调用,但不在this回调函数内部使用.

更新2:由于该指令是使回调异步的指令,因此在scope.$apply那里使用它可能是一个更好的主意,但请记住,如果您在指令中使用隔离范围,那可能会变得奇怪.

http://jsfiddle.net/langdonx/K4Kx8/114/

HTML:

<div ng-controller="MyCtrl">
  <ul>
      <li ng-repeat="item in items" fadey="500">
          {{item}}
          <a ng-click="clearItem(item)">X</a>
      </li>
  </ul>
  <hr />
  <button ng-click="items.push(items.length)">Add Item</button>    
</div>
Run Code Online (Sandbox Code Playgroud)

JavaScript的:

var myApp = angular.module('myApp', []);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.items = [0, 1, 2];

    $scope.clearItem = function(item) {
        var idx = $scope.items.indexOf(item);
        if (idx !== -1) {
            //injected into repeater scope by fadey directive
            this.destroy(function() {
                $scope.items.splice(idx, 1);
            });
        }
    };
}

myApp.directive('fadey', function() {
    return {
        restrict: 'A', // restricts the use of the directive (use it as an attribute)
        link: function(scope, elm, attrs) { // fires when the element is created and is linked to the scope of the parent controller
            var duration = parseInt(attrs.fadey);
            if (isNaN(duration)) {
                duration = 500;
            }
            elm = jQuery(elm);
            elm.hide();
            elm.fadeIn(duration)

            scope.destroy = function(complete) {
                elm.fadeOut(duration, function() {
                    scope.$apply(function() {
                        complete.$apply(scope);
                    });
                });
            };
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

至于为什么,我认为这只是为了分离关注点和可能性.您的控制器应关注数据流和业务逻辑,而不是接口操作.理想情况下,你的指令应该是为了可用性而编写的(就像在fadey这里编写的那样.注意:我不会把它称为fadey;)).

  • 如果使用jQuery,请在angular.js之前包含它,以便angular然后采用完整的jQuery库而不是它自己的**[jQlite](http://docs.angularjs.org/api/angular.element)**.通过这样做,指令中的`elm`已经是一个jQuery对象,并且不必将它包装在`jQuery(elm)`http://jsfiddle.net/ADukg/2222/中 (2认同)