为什么$ element可用/注入控制器?

时间:2013-12-11 08:48:21

标签: angularjs model-view-controller

在AngularJS中,我注意到控制器注入了$element,这是控制器控制的元素的JQuery / JQLite包装器。例如:

<body ng-controller="MainCtrl">

然后,您可以通过注入$element

来访问控制器中的body元素
app.controller('MainCtrl', function($scope, $element) { ...

这可以在this Plunkr工作。

似乎被确认为docs for $compile

中的故意功能

我的问题是:

  • 根据各种指南和教程,建议您不要在控制器中访问DOM,为什么这样做呢?

  • 是否有任何非hacky用例?

  • 在某处可用代码中是否有使用此示例?

感谢。

4 个答案:

答案 0 :(得分:15)

快速摘要

可扩展和/或与其他指令交互的编写良好的指令将具有控制器。该控制器需要访问DOM,因为它是定义该指令的功能的地方。指令实际上是将控制器/范围绑定到页面上元素的不同方式;向DOM添加功能的首选方法。据我所知,最佳做法是:不要同时使用控制器和链接功能。所以指令控制器需要$element

详细解答

  

根据各种指南和教程,建议您不应该在控制器中访问DOM,为什么这甚至可能?

一旦你深入研究这些指南是如何工作的,这些指南就会有点误导。

导游说:

控制器处理定义函数并分配视图使用的变量。将这些函数和变量绑定到视图的正确方法是使用指令。这是我对最佳实践的理解,在过去的一年里,它与大型和不断增长的角度应用程序一起工作。

为何令人困惑:

棘手的是该指令基本上将控制器绑定到DOM。 ng-model是一个指令,并且有一个可以从其他指令访问的控制器。如果您执行添加自定义验证功能等操作,则需要利用此功能。该指令的该控制器应该操纵DOM。 所以通用控制器实际上是一组超级视图控制器;教程通常会釉面的细节。

  

是否有任何非hacky用例?

&#39;正确&#39;使用$element

的方法

例如在指令的控制器中使用它。

  

在某处可用代码中是否有使用此示例?

示例:

Angular源代码虽然可能有点密集,但是代码很好并且评论很好。可能需要一点点才能看到正在发生的事情,但通常信息非常丰富。

NgModelController(复杂示例) https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js#L1660

可能是一个简单的例子,但是使用了编译函数,例如eventDirectives(例如ng-click), https://github.com/angular/angular.js/blob/master/src/ng/directive/ngEventDirs.js#L3

答案 1 :(得分:6)

根据各种指南和教程,建议您不应该在控制器中访问DOM,为什么这甚至可能?

无论是否注入$ element,控制器的范围都绑定在该元素上。

angular.element('#element-with-controller').scope();

Angular围绕指令。它是MVC中粘合在一起的东西。 如果你考虑一下,ng-controller本身就是一个指令。

是否有任何非hacky用例?

我想当你将一个控制器用于多个指令时,这可以派上用场。

.controller('MyController', function($scope, $element){
    $scope.doSomething = function(){
        // do something with $element...
    }
})
.directive('myDirective1', function(){
    return {
        controller: 'MyController'
    }
})
.directive('myDirective2', function(){
    return {
        controller: 'MyController'
    }
})

每个指令都有一个指定控制器的新实例,但基本上是共享它的属性,依赖关系。

在某处可用代码中是否有使用此示例?

我写了一个表单处理程序控制器,用于注册/登录/联系人等等。

答案 2 :(得分:4)

实际上,因为您在参数列表中将其指定为依赖项,所以会注入$ element。 如果从列表中删除它,则不会注入它。

http://plnkr.co/edit/CPHGM1awvTvpXMcjxMKM?p=preview

如评论所述,有些情况下你需要控制器中的$元素,尽管我现在想不到任何元素。

答案 3 :(得分:4)

将评论作为答案发布,因为评论中的字符限制以及包含部分答案的感觉。

  

根据各种指南和教程,建议您不要在控制器中访问DOM,为什么这可能呢?

如前所述,人们建议您在代码中采取特定方法并不要求他们限制您。


  

是否有任何非hacky用例?

在大多数情况下,我无法想到一个好处(回复你的评论)。有一次我使用这种方法是实现youtube iframe API指令。 当有人停止播放器时,必须从DOM中删除该元素。


  

在某处可用代码中是否有使用此示例?

这是一些代码,虽然它来自很久以前,我删除了一些部分,被认为是hacky?

angular.module('mainApp.youtube').directive('youtubePlayer', function($window,$element logging, ui,) {
    return {
        restrict: 'A', // only activate on element attribute
            scope: true, // New scope to use but rest inherit proto from parent
            compile: function(tElement, tAttrs) {
            // Load the Youtube js api
            var tag = document.createElement('script');
            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        },
        controller: function($scope, $element, $attrs) {

        // This is called when the player is loaded from YT
        $window.onYouTubeIframeAPIReady = function() {
            $scope.player = new YT.Player('player', {
                    height: '250',
                    width: '400',
                    playerVars: {
                'autoplay': 0,
                        'controls': 1,
                        'autohide': 2
                    },
                    //videoId: $scope.live_track.video_id,
                    events: {
                'onReady': $scope.onPlayerReady,
                        'onStateChange': $scope.onPlayerStateChange,
                        'onError': $scope.onError
                    }
                });
            };  

            // When the player has been loaded and is ready to play etc
            $scope.onPlayerReady = function (event) {
                $scope.$apply(function(){
                    logging.info("Playa is ready");
                    logging.info($scope.player);
                    // Lets also broadcast a change state for the others to catch up
                    player_service.broadcast_change_state({"state": $scope.player.getPlayerState()});
                    // Should try to just load the track so that the users can press play on the playa
                });
            };



            // When the player has been loaded and is ready to play etc
            $scope.onError = function (event) {
                $scope.$apply(function(){
                    logging.info("Playa Encountered and ERROR");
                    logging.info(event)
                    });
            };

            $scope.start_playing = function (jukebox_id){
                logging.info('Yes I am starting...');

            };



            $scope.$on('handleStartPlaying', function(event, jukebox_id) {
                console.log('Got the message I ll play');
                $scope.start_playing(jukebox_id);
            });

            $scope.$on('handlePausePlaying', function() {
                console.log('Got the message I ll pause');
                $scope.player.pauseVideo();
            });

            $scope.$on('handleResumePlaying', function() {
                console.log('Got the message I ll resume');
                $scope.player.playVideo();
            });

            $scope.$on('handleStopPlaying', function() {
                console.log('Got the message I ll stop');
                $scope.player.stopVideo();
            });

            $scope.$on('HandleCloseframe', function() {
                console.log('Got the message I ll stop');
                $scope.player.stopVideo();
                //Should destroy obje etc
                // Look here
                $element.remove(); // blah blah blah
            });

        },
            ink: function(scope, elm, attrs, ctrl) {

        }
        }
    });

随意纠正我或提供更好的方法。那时候这似乎是合法的。至少如果我们不犯错误,我们就不会学习。