7

所以我一直在阅读,从控制器中进行 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();
    });
};

解决方案

根据下面兰登的回答,我为自己的工作得出了以下工作代码,该代码略微源自上面的示例代码:

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)
                }
            });
        }
    }
});

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

更新 2

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

4

2 回答 2

9

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

更新:根据评论修改以实际影响范围。对解决方案并不感到兴奋,甚至更糟糕,因为原作者complete.apply(scope)在他的销毁回调中调用了,但没有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>

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);
                    });
                });
            };
        }
    };
});

至于为什么,我认为这只是为了分离关注点,也许是为了可用性。您的控制器应该关注数据流和业务逻辑,而不是界面操作。理想情况下,您的指令应该是为了可用性而编写的(就像fadey这里的情况一样 -- ed。注意:我不会称它为 fadey ;))。

于 2013-04-01T14:44:46.683 回答
0

这篇文章中显示的代码对我理解关系控制器 - 指令非常有帮助,但是它抛出了一个 js 错误。

TypeError: Object function (scope) {
  $scope.items.splice(idx, 1);
  console.log($scope.items)
} has no method '$apply'

我稍微更新了指令,现在它对我有用:

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

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

                $scope.items.splice(idx, 1);

                //this now shows the expected results
                console.log($scope.items)
            });
        }
    };
}

myApp.directive('fadey', function() {
    return {
        restrict: 'A', // restricts the use of the directive (use it as an attribute)
        // fires when the element is created and is linked to the scope of the parent controller
        link: function(scope, elm, attrs) { 
            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() {
                        //note the change here 
                        complete(scope);
                    });
                });
            };
        }
    };
});
于 2014-02-02T19:53:16.017 回答