web-dev-qa-db-ja.com

コントローラーで$ elementが使用可能/挿入されているのはなぜですか?

AngularJSでは、コントローラーに$elementが挿入されていることに気付きました。これは、コントローラーが制御している要素のJQuery/JQLiteラッパーです。例えば:

<body ng-controller="MainCtrl">

その後、$elementを挿入することで、コントローラーのbody要素にアクセスできます

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

これは this Plunkr で動作していることがわかります。

$ compileのドキュメント で意図的な機能として確認されているようです

私の質問は:

  • コントローラーでDOMにアクセスすべきではないことを示唆するさまざまなガイドやチュートリアルに照らして、なぜこれが可能なのでしょうか?

  • このためのハック以外のユースケースはありますか?

  • これはどこかの利用可能なコードで使用されている例はありますか?

ありがとう。

32
Michal Charemza

簡単な要約

拡張可能であり、および/または他のディレクティブと対話する適切に記述されたディレクティブには、コントローラーがあります。このコントローラは、ディレクティブの機能が定義されている場所であるため、DOMにアクセスする必要があります。ディレクティブは、実際にはコントローラー/スコープをページ上の要素にバインドするための別の方法です。 DOMに機能を追加する好ましい方法。私が理解していることから、ベストプラクティスはコントローラーとリンク機能の両方を使用しないことです。したがって、ディレクティブコントローラーには$elementが必要です。

詳細な回答

コントローラーでDOMにアクセスすべきではないことを示唆するさまざまなガイドやチュートリアルに照らして、なぜこれが可能なのでしょうか?

ガイドは、すべてがどのように機能するかを掘り下げると、少し誤解を招きます。

ガイドが言うこと:

コントローラは定義関数を処理し、ビューで使用される変数を割り当てます。そして、これらの関数と変数をビューにバインドする正しい方法は、ディレクティブを使用することです。これは、過去1年間に大規模で成長しているangularアプリケーションで作業してきたベストプラクティスに関する私の理解です。

なぜ混乱するのか:

トリッキーなことは、ディレクティブが基本的にコントローラーをDOMにバインドすることです。 ng-modelはディレクティブであり、他のディレクティブからアクセスできるコントローラーがあります。カスタム検証の空想を追加するようなことをする場合、これを利用したいと思うでしょう。ディレクティブのこのコントローラーは想定 DOMを操作します。 つまり、汎用コントローラーは、実際にはビューコントローラーのスーパーセットです。チュートリアルで通常見かける詳細

これに非ハッキングなユースケースはありますか?

$elementを使用する「正しい」方法:

たとえば、ディレクティブのコントローラーで使用します。

これはどこかの利用可能なコードで使用されている例はありますか?

例:

角度のあるソースコードは、おそらく少し読みやすいですが、優れたコードであり、十分にコメントされています。何が起こっているかを見るには少し時間がかかるかもしれませんが、通常は非常に有益です。

NgModelController(複雑な例) https://github.com/angular/angular.js/blob/master/src/ng/directive/input.jshttps://github.com /angular/angular.js/blob/master/src/ng/directive/input.js#L166

単純な例ですが、代わりにコンパイル関数、eventDirectives(たとえば[ng-click)]を使用します https://github.com/angular/angular.js/blob/master/src/ ng/directive/ngEventDirs.js#L

15
alockwood05

コントローラーでDOMにアクセスすべきではないことを示唆するさまざまなガイドやチュートリアルに照らして、なぜこれが可能なのでしょうか?

$ elementを注入するかどうかに関係なく、コントローラーのスコープはその要素にバインドされます。

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

角度はディレクティブを中心に展開します。 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'
    }
})

各ディレクティブには、割り当てられたコントローラーの新しいインスタンスがありますが、基本的にはそのプロパティ、依存関係を共有します。

これはどこかの利用可能なコードで使用されている例はありますか?

登録/ログイン/連絡先などのために、フォームハンドラーコントローラーを1回作成しました。

コメントの性格の制限と、回答の一部が含まれているという感覚のために、コメントを回答として投稿する。

コントローラーでDOMにアクセスすべきではないことを示唆するさまざまなガイドやチュートリアルに照らして、なぜこれが可能なのでしょうか?

前に述べたように、人々はあなたのコードで特定のアプローチを取ることを提案しますが、あなたを制限する必要はありません。


これに非ハッキングなユースケースはありますか?

私の頭の一番上から、私はほとんどの場合に利益(あなたのコメントに答える)を考えることができません。私がこのアプローチを使用したのは、youtube iframe APIディレクティブを実装することでした。誰かがプレーヤーを停止したとき、要素は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) {

        }
        }
    });

私を修正するか、より良いアプローチを提供してください。当時、これは合法のようでした。少なくとも私たちが間違いをしない限り、私たちは学びません。

4
Jimmy Kane

実際には、引数リストで依存関係として指定したため、$ elementが挿入されます。リストから削除すると、挿入されません。

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

そして、コメントされているように、現時点では何も考えられませんが、コントローラーに$ elementが必要な場合があります。

4
Lucius