Подтвердить что ты не робот

Почему $element доступен/введен в контроллер?

В AngularJS я заметил, что контроллер вводится $element, который представляет собой оболочку JQuery/JQLite элемента, контролируемого контроллером. Например:

<body ng-controller="MainCtrl">

Затем вы можете получить доступ к элементу body в контроллере, введя $element

app.controller('MainCtrl', function($scope, $element) { ...

Это можно увидеть в этот Plunkr.

И, похоже, это подтверждается как преднамеренная функция в docs для компиляции.

Мои вопросы:

  • В свете различных руководств и руководств, которые предполагают, что вам не следует обращаться к DOM в контроллере, почему это возможно?

  • Есть ли какой-либо небедово используемый вариант для этого?

  • Есть ли примеры того, что это где-то используется в доступном коде?

Спасибо.

4b9b3361

Ответ 1

Краткое резюме

Хорошо написанная директива, которая может быть расширена и/или взаимодействовать с другими директивами, будет иметь контроллер. Этот контроллер должен получить доступ к DOM, потому что именно там определена эта директивная функциональность. Директивы - это отличный способ привязать контроллер/область к элементу на странице; предпочтительный способ добавления функциональности в DOM. Насколько я понимаю, наилучшей практикой является: не используйте как контроллер, так и функцию связи. Поэтому директивным контроллерам нужен $element.

Подробные ответы

В свете различных руководств и руководств, которые предполагают, что вы не должны обращаться к DOM в контроллере, почему это возможно?

Гиды немного вводят в заблуждение, как только вы вникнете в то, как все это работает.

что говорят гиды:

Контроллеры обрабатывают определяющие функции и назначают переменные, которые будут использоваться представлением. И правильный способ привязать эти функции и переменные к представлению - это директива. Это мое понимание лучших практик, работавших с большими и растущими приложениями angular за последний год.

почему это запутывает:

Трудность в том, что директива в основном связывает контроллер с DOM. ng-model является директивой и имеет контроллер, к которому можно получить доступ из других директив. Вы захотите воспользоваться этим, если будете делать такие вещи, как добавление пользовательской проверки. Этот контроллер директивы должен управлять DOM. Таким образом, общий контроллер на самом деле представляет собой супер набор контроллеров представлений; деталь, которую обычно глазуруют учебные пособия.

Есть ли какой-нибудь небедово используемый случай?

"правильные" способы использования $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 (например, t23 > ), https://github.com/angular/angular.js/blob/master/src/ng/directive/ngEventDirs.js#L3

Ответ 2

В свете различных руководств и руководств, которые предполагают, что вы не должны обращаться к DOM в контроллере, почему это возможно?

Если вы вводите $element или нет, область контроллера связана с этим элементом.

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

Angular вращается вокруг директив. Это то, что склеивает вещи в MVC. И если вы думаете об этом, ng-controller, это сама директива.

Есть ли для этого небедово используемый вариант?

Я думаю, это может пригодиться, когда вы используете один контроллер для нескольких директив.

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

Каждая директива будет иметь новый экземпляр назначенного контроллера, но в основном использовать его свойства, зависимости.

Есть ли примеры того, что это где-то используется в доступном коде?

Я написал контроллер обработчика формы один раз, для регистрации/входа/контакта и т.д.

Ответ 3

На самом деле, $element вводится, потому что вы указали его как зависимость в списке аргументов. Если вы удалите его из списка, он не будет введен.

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

И как прокомментировано, есть случай, когда вам нужен элемент $element в контроллере, хотя в данный момент я не могу думать о нем.

Ответ 4

Публикация моего комментария как ответа из-за ограничений символов в комментариях и из-за чувства, которое содержит часть ответа.

В свете различных руководств и руководств, которые предполагают, что вы не должны обращаться к DOM в контроллере, почему это возможно?

Как уже говорилось ранее, люди предлагают принять конкретный подход в вашем коде, не требуя, чтобы они ограничивали вас.


Есть ли какой-нибудь небедово используемый случай?

В верхней части моей головы я не могу придумать преимущество (ответ на ваш комментарий) в большинстве случаев. Один раз, когда я использовал этот подход, была реализована директива API youtube iframe. Когда кто-то остановил игрока, элемент должен был быть удален из DOM.


Есть ли примеры того, что это где-то используется в доступном коде?

Вот какой код для этого, хотя это довольно давно, и я удалил некоторые части и считается взломанным?

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) {

        }
        }
    });

Не стесняйтесь исправлять меня или предлагать лучшие подходы. В то время это казалось законным. По крайней мере, если мы не ошибаемся, мы не узнаем.