Dans AngularJS, j'ai remarqué qu'un contrôleur est injecté avec $element
, qui est un wrapper JQuery/JQLite de l'élément que le contrôleur contrôle. Par exemple:
<body ng-controller="MainCtrl">
Ensuite, vous pouvez avoir accès à l'élément body dans le contrôleur en injectant $element
app.controller('MainCtrl', function($scope, $element) { ...
Cela peut être vu fonctionner à ce Plunkr .
Et semble être confirmé comme une fonctionnalité délibérée dans le docs for $ compile
Mes questions sont:
À la lumière des différents guides et tutoriels qui suggèrent que vous ne devriez pas accéder au DOM dans un contrôleur, pourquoi est-ce même possible?
Existe-t-il un cas d'utilisation non hacky pour cela?
Existe-t-il des exemples de cette utilisation dans le code disponible quelque part?
Merci.
Une directive bien écrite qui est extensible et/ou interagit avec d'autres directives aura un contrôleur. Ce contrôleur doit accéder au DOM car c'est là que la fonctionnalité de cette directive est définie. Les directives sont en fait une manière différente de lier un contrôleur/portée à un élément de la page; la façon préférée d'ajouter des fonctionnalités au DOM. D'après ce que je comprends, la meilleure pratique est: n'utilisez pas à la fois un contrôleur et une fonction de liaison. Les contrôleurs de directive ont donc besoin d'un $element
.
À la lumière des différents guides et tutoriels qui suggèrent que vous ne devriez pas accéder au DOM dans un contrôleur, pourquoi est-ce même possible?
Les guides sont un peu trompeurs une fois que vous avez exploré comment tout cela fonctionne.
Les contrôleurs gèrent la définition des fonctions et affectent les variables à utiliser par la vue. Et la bonne façon de lier ces fonctions et variables à la vue est avec une directive. Telle est ma compréhension des meilleures pratiques, ayant travaillé avec des applications angular angulaires au cours de la dernière année).
La chose délicate est que la directive lie fondamentalement un contrôleur au DOM. ng-model
est une directive et possède un contrôleur accessible à partir d'autres directives. Vous voudrez en profiter si vous faites des choses comme ajouter une fantaisie de validation personnalisée. Ce contrôleur de la directive est supposé le faire manipuler le DOM. Donc, un contrôleur générique est en fait un super ensemble de contrôleurs de vue; un détail sur lequel les didacticiels se glacent généralement.
Existe-t-il un cas d'utilisation non hacky pour cela?
$element
:L'utiliser dans le contrôleur d'une directive par exemple.
Existe-t-il des exemples de cette utilisation dans le code disponible quelque part?
Le code source angulaire, bien que peut-être un peu dense d'une lecture, est un bon code et bien commenté. Cela peut prendre un peu de temps pour voir ce qui se passe, mais généralement assez instructif.
NgModelController (exemple complexe) 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
Ce qui pourrait être un exemple simple, mais utilise une fonction de compilation à la place, eventDirectives (ng-click
par exemple), https://github.com/angular/angular.js/blob/master/src/ng/directive/ngEventDirs.js#L
Que vous injectiez ou non $ element, la portée du contrôleur est liée à cet élément.
angular.element('#element-with-controller').scope();
L'angulaire tourne autour des directives. C'est ce qui colle les choses ensemble dans le MVC. Et si vous y réfléchissez, ng-controller, est une directive elle-même.
Je suppose que cela peut être utile lorsque vous utilisez un seul contrôleur pour plusieurs directives.
.controller('MyController', function($scope, $element){
$scope.doSomething = function(){
// do something with $element...
}
})
.directive('myDirective1', function(){
return {
controller: 'MyController'
}
})
.directive('myDirective2', function(){
return {
controller: 'MyController'
}
})
Chaque directive aura une nouvelle instance du contrôleur assigné, mais partagera essentiellement ses propriétés, ses dépendances.
J'ai écrit une fois un contrôleur de gestionnaire de formulaire, pour l'inscription/la connexion/le contact, etc.
Publier mon commentaire comme réponse à cause des limites de caractères dans les commentaires et à cause du sentiment qui contient une partie de la réponse.
À la lumière des différents guides et tutoriels qui suggèrent que vous ne devriez pas accéder au DOM dans un contrôleur, pourquoi est-ce même possible?
Comme dit précédemment, les gens suggèrent d'adopter une approche spécifique dans votre code ne les obligeant pas à vous limiter.
Existe-t-il un cas d'utilisation non hacky pour cela?
Du haut de ma tête, je ne peux pas penser à un avantage (réponse sur votre commentaire) dans la plupart des cas. Une fois que j'ai utilisé cette approche, j'ai implémenté une directive API iframe youtube. Lorsque quelqu'un a arrêté le lecteur, l'élément a dû être supprimé du DOM.
Existe-t-il des exemples de cette utilisation dans le code disponible quelque part?
Voici un code pour cela, bien qu'il date d'il y a un certain temps et que j'ai supprimé certaines parties et qu'il soit considéré comme 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) {
}
}
});
N'hésitez pas à me corriger ou à proposer de meilleures approches. A cette époque, cela semblait légitime. Au moins, si nous ne commettons pas d'erreurs, nous n'apprenons pas.
En fait, $ element est injecté car vous l'avez spécifié comme dépendance dans la liste d'arguments. Si vous le supprimez de la liste, il ne sera pas injecté.
http://plnkr.co/edit/CPHGM1awvTvpXMcjxMKM?p=preview
Et comme commenté, il y a des cas où vous avez besoin de $ element dans le contrôleur, bien que je ne puisse pas en penser pour le moment.