J'ai une directive, voici le code:
.directive('map', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
var center = new google.maps.LatLng(50.1, 14.4);
$scope.map_options = {
zoom: 14,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
var dirService= new google.maps.DirectionsService();
var dirRenderer= new google.maps.DirectionsRenderer()
var showDirections = function(dirResult, dirStatus) {
if (dirStatus != google.maps.DirectionsStatus.OK) {
alert('Directions failed: ' + dirStatus);
return;
}
// Show directions
dirRenderer.setMap(map);
//$scope.dirRenderer.setPanel(Demo.dirContainer);
dirRenderer.setDirections(dirResult);
};
// Watch
var updateMap = function(){
dirService.route($scope.dirRequest, showDirections);
};
$scope.$watch('dirRequest.Origin', updateMap);
google.maps.event.addListener(map, 'zoom_changed', function() {
$scope.map_options.zoom = map.getZoom();
});
dirService.route($scope.dirRequest, showDirections);
}
}
})
Je voudrais appeler updateMap()
sur une action de l'utilisateur. Le bouton d'action n'est pas sur la directive.
Quel est le meilleur moyen d’appeler updateMap()
depuis un contrôleur?
Si vous souhaitez utiliser des étendues isolées, vous pouvez transmettre un objet de contrôle à l'aide de la liaison bidirectionnelle =
d'une variable de l'étendue du contrôleur. Vous pouvez également contrôler plusieurs instances de la même directive sur une page avec le même objet de contrôle.
angular.module('directiveControlDemo', [])
.controller('MainCtrl', function($scope) {
$scope.focusinControl = {};
})
.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link: function(scope, element, attrs) {
scope.internalControl = scope.control || {};
scope.internalControl.takenTablets = 0;
scope.internalControl.takeTablet = function() {
scope.internalControl.takenTablets += 1;
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
<div ng-controller="MainCtrl">
<button ng-click="focusinControl.takeTablet()">Call directive function</button>
<p>
<b>In controller scope:</b>
{{focusinControl}}
</p>
<p>
<b>In directive scope:</b>
<focusin control="focusinControl"></focusin>
</p>
<p>
<b>Without control object:</b>
<focusin></focusin>
</p>
</div>
</div>
En supposant que le bouton d'action utilise le même contrôleur $scope
que la directive, définissez simplement la fonction updateMap
sur $scope
à l'intérieur de la fonction link. Votre contrôleur peut alors appeler cette fonction lorsque vous cliquez sur le bouton d'action.
<div ng-controller="MyCtrl">
<map></map>
<button ng-click="updateMap()">call updateMap()</button>
</div>
app.directive('map', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
$scope.updateMap = function() {
alert('inside updateMap()');
}
}
}
});
Selon le commentaire de @ FlorianF, si la directive utilise une portée isolée, les choses sont plus compliquées. Voici un moyen de le faire fonctionner: ajoutez un attribut set-fn
à la directive map
qui enregistrera la fonction de directive avec le contrôleur:
<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
scope.updateMap = function() {
alert('inside updateMap()');
}
scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
$scope.setDirectiveFn = function(directiveFn) {
$scope.directiveFn = directiveFn;
};
}
Même s'il peut être tentant d'exposer un objet sur le champ d'application isolé d'une directive pour faciliter la communication avec lui, cela peut entraîner une confusion du code "spaghetti", en particulier si vous devez chaîner cette communication sur plusieurs niveaux (contrôleur, directive, directive imbriquée, etc.)
Au départ, nous avions emprunté cette voie, mais après quelques recherches supplémentaires, nous avons constaté qu’il était plus logique de créer un code plus facile à gérer et à lire pour exposer les événements et les propriétés qu’une directive utilisera pour la communication via un service puis utilisant la directive ou tout autre contrôle qui devrait réagir à ces changements pour la communication.
Cette abstraction fonctionne très bien avec le cadre d'injection de dépendance d'AngularJS, car vous pouvez injecter le service dans tous les éléments devant réagir à ces événements. Si vous examinez le fichier Angular.js, vous verrez que les directives qui y sont contenues utilisent également des services et que $ watch de cette manière, elles n'exposent pas les événements situés au-delà de la portée isolée.
Enfin, dans le cas où vous devez communiquer entre des directives qui dépendent les unes des autres, je vous recommanderais de partager un contrôleur entre ces directives comme moyen de communication.
Le wiki des meilleures pratiques d’AngularJS mentionne également ceci:
Utilisez uniquement. $ Broadcast (),. $ Emit () et. $ On () pour les événements atomiques Événements pertinents à l'échelle mondiale pour l'ensemble de l'application (tels que l'authentification d'un utilisateur ou la fermeture de l'application). Si vous souhaitez des événements spécifiques à des modules, services ou widgets, vous devez envisager Services, Contrôleurs de contrôle ou Bibliothèques tierces.
- $ scope. $ watch () devrait remplacer le besoin d'événements
- L’injection directe de services et de méthodes d’appel est également utile pour la communication directe
- Les directives peuvent communiquer directement les unes avec les autres via des contrôleurs de directives
En vous appuyant sur la réponse d'Oliver, vous n'aurez peut-être pas toujours besoin d'accéder aux méthodes internes d'une directive. Dans ces cas, vous ne voudrez probablement pas avoir à créer un objet vide et à ajouter un control
attr à la directive juste pour l'empêcher de générer une erreur. (cannot set property 'takeTablet' of undefined
).
Vous pouvez également utiliser la méthode à d’autres endroits de la directive.
J'ajouterais une vérification pour m'assurer que scope.control
existe et définirais les méthodes de la même manière que le modèle de module révélateur
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{control}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
var takenTablets = 0;
var takeTablet = function() {
takenTablets += 1;
}
if (scope.control) {
scope.control = {
takeTablet: takeTablet
};
}
}
};
});
Pour être honnête, aucune des réponses à ce sujet ne m'a convaincu. Alors, voici mes solutions:
Cette méthode ne dépend pas du fait que le $scope
de la directive soit partagé ou isolé.
Un factory
pour enregistrer les instances de directive
angular.module('myModule').factory('MyDirectiveHandler', function() {
var instance_map = {};
var service = {
registerDirective: registerDirective,
getDirective: getDirective,
deregisterDirective: deregisterDirective
};
return service;
function registerDirective(name, ctrl) {
instance_map[name] = ctrl;
}
function getDirective(name) {
return instance_map[name];
}
function deregisterDirective(name) {
instance_map[name] = null;
}
});
Le code de directive, je mets généralement toute la logique qui ne traite pas de DOM dans le contrôleur de directive. Et en enregistrant l'instance de contrôleur dans notre gestionnaire
angular.module('myModule').directive('myDirective', function(MyDirectiveHandler) {
var directive = {
link: link,
controller: controller
};
return directive;
function link() {
//link fn code
}
function controller($scope, $attrs) {
var name = $attrs.name;
this.updateMap = function() {
//some code
};
MyDirectiveHandler.registerDirective(name, this);
$scope.$on('destroy', function() {
MyDirectiveHandler.deregisterDirective(name);
});
}
})
code de modèle
<div my-directive name="foo"></div>
Accédez à l'instance du contrôleur à l'aide de factory
& exécutez les méthodes exposées publiquement.
angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) {
$scope.someFn = function() {
MyDirectiveHandler.get('foo').updateMap();
};
});
Prendre une feuille du livre d'angular sur la façon dont ils traitent
<form name="my_form"></form>
en utilisant $ parse et en enregistrant le contrôleur sur $parent
. Cette technique ne fonctionne pas sur les directives $scope
isolées.
angular.module('myModule').directive('myDirective', function($parse) {
var directive = {
link: link,
controller: controller,
scope: true
};
return directive;
function link() {
//link fn code
}
function controller($scope, $attrs) {
$parse($attrs.name).assign($scope.$parent, this);
this.updateMap = function() {
//some code
};
}
})
Accédez-y à l'intérieur du contrôleur en utilisant $scope.foo
angular.module('myModule').controller('MyController', function($scope) {
$scope.someFn = function() {
$scope.foo.updateMap();
};
});
Un peu tard, mais c'est une solution avec la portée isolée et des "événements" pour appeler une fonction dans la directive. Cette solution s’inspire de this SO post de - satchmorun et ajoute un module et une API.
//Create module
var MapModule = angular.module('MapModule', []);
//Load dependency dynamically
angular.module('app').requires.Push('MapModule');
Créez une API pour communiquer avec la directive. AddUpdateEvent ajoute un événement au tableau d'événements et updateMap appelle chaque fonction d'événement.
MapModule.factory('MapApi', function () {
return {
events: [],
addUpdateEvent: function (func) {
this.events.Push(func);
},
updateMap: function () {
this.events.forEach(function (func) {
func.call();
});
}
}
});
(Vous devez peut-être ajouter une fonctionnalité pour supprimer un événement.)
Dans la directive, définissez une référence à MapAPI et ajoutez $ scope.updateMap en tant qu'événement lorsque MapApi.updateMap est appelé.
app.directive('map', function () {
return {
restrict: 'E',
scope: {},
templateUrl: '....',
controller: function ($scope, $http, $attrs, MapApi) {
$scope.api = MapApi;
$scope.updateMap = function () {
//Update the map
};
//Add event
$scope.api.addUpdateEvent($scope.updateMap);
}
}
});
Dans le contrôleur "principal", ajoutez une référence à MapApi et appelez simplement MapApi.updateMap () pour mettre à jour la carte.
app.controller('mainController', function ($scope, MapApi) {
$scope.updateMapButtonClick = function() {
MapApi.updateMap();
};
}
Vous pouvez spécifier un attribut DOM pouvant être utilisé pour permettre à la directive de définir une fonction sur la portée parente. La portée parente peut alors appeler cette méthode comme une autre. Voici un plunker. Et ci-dessous est le code pertinent.
clearfn
est un attribut de l'élément de directive dans lequel la portée parente peut transmettre une propriété de portée que la directive peut ensuite définir pour une fonction accomplissant le comportement souhaité.
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<style>
my-box{
display:block;
border:solid 1px #aaa;
min-width:50px;
min-height:50px;
padding:.5em;
margin:1em;
outline:0px;
box-shadow:inset 0px 0px .4em #aaa;
}
</style>
</head>
<body ng-controller="mycontroller">
<h1>Call method on directive</h1>
<button ng-click="clear()">Clear</button>
<my-box clearfn="clear" contentEditable=true></my-box>
<script>
var app = angular.module('myapp', []);
app.controller('mycontroller', function($scope){
});
app.directive('myBox', function(){
return {
restrict: 'E',
scope: {
clearFn: '=clearfn'
},
template: '',
link: function(scope, element, attrs){
element.html('Hello World!');
scope.clearFn = function(){
element.html('');
};
}
}
});
</script>
</body>
</html>
Il suffit d'utiliser scope. $ Parent pour associer une fonction appelée à une fonction de directive
angular.module('myApp', [])
.controller('MyCtrl',['$scope',function($scope) {
}])
.directive('mydirective',function(){
function link(scope, el, attr){
//use scope.$parent to associate the function called to directive function
scope.$parent.myfunction = function directivefunction(parameter){
//do something
}
}
return {
link: link,
restrict: 'E'
};
});
en HTML
<div ng-controller="MyCtrl">
<mydirective></mydirective>
<button ng-click="myfunction(parameter)">call()</button>
</div>
Vous pouvez indiquer le nom de la méthode à la directive pour définir celui que vous voulez appeler depuis le contrôleur, mais sans isoler la portée
angular.module("app", [])
.directive("palyer", [
function() {
return {
restrict: "A",
template:'<div class="player"><span ng-bind="text"></span></div>',
link: function($scope, element, attr) {
if (attr.toPlay) {
$scope[attr.toPlay] = function(name) {
$scope.text = name + " playing...";
}
}
}
};
}
])
.controller("playerController", ["$scope",
function($scope) {
$scope.clickPlay = function() {
$scope.play('AR Song');
};
}
]);
.player{
border:1px solid;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="playerController">
<p>Click play button to play
<p>
<p palyer="" to-play="play"></p>
<button ng-click="clickPlay()">Play</button>
</div>
</div>
TEST&EACUTE; J'espère que cela aidera quelqu'un.
Mon approche simple (pensez aux balises comme votre code d'origine)
<html>
<div ng-click="myfuncion">
<my-dir callfunction="myfunction">
</html>
<directive "my-dir">
callfunction:"=callfunction"
link : function(scope,element,attr) {
scope.callfunction = function() {
/// your code
}
}
</directive>
Ce n'est peut-être pas le meilleur choix, mais vous pouvez utiliser angular.element("#element").isolateScope()
ou $("#element").isolateScope()
pour accéder à l'étendue et/ou au contrôleur de votre directive.
La solution ci-dessous sera utile lorsque vous rencontrez des contrôleurs (à la fois parent et directive (isolés)) au format 'controller As'
cela pourrait être utile,
directive:
var directive = {
link: link,
restrict: 'E',
replace: true,
scope: {
clearFilters: '='
},
templateUrl: "/temp.html",
bindToController: true,
controller: ProjectCustomAttributesController,
controllerAs: 'vmd'
};
return directive;
function link(scope, element, attrs) {
scope.vmd.clearFilters = scope.vmd.SetFitlersToDefaultValue;
}
}
directive contrôleur:
function DirectiveController($location, dbConnection, uiUtility) {
vmd.SetFitlersToDefaultValue = SetFitlersToDefaultValue;
function SetFitlersToDefaultValue() {
//your logic
}
}
code HTML :
<Test-directive clear-filters="vm.ClearFilters"></Test-directive>
<a class="pull-right" style="cursor: pointer" ng-click="vm.ClearFilters()"><u>Clear</u></a>
//this button is from parent controller which will call directive controller function
Comment obtenir le contrôleur d'une directive dans un contrôleur de page:
écrivez une directive personnalisée pour obtenir la référence au contrôleur de directive à partir de l'élément DOM:
angular.module('myApp')
.directive('controller', controller);
controller.$inject = ['$parse'];
function controller($parse) {
var directive = {
restrict: 'A',
link: linkFunction
};
return directive;
function linkFunction(scope, el, attrs) {
var directiveName = attrs.$normalize(el.prop("tagName").toLowerCase());
var directiveController = el.controller(directiveName);
var model = $parse(attrs.controller);
model.assign(scope, directiveController);
}
}
utilisez-le dans le code HTML du contrôleur de page:
<my-directive controller="vm.myDirectiveController"></my-directive>
Utilisez le contrôleur de directive dans le contrôleur de page:
vm.myDirectiveController.callSomeMethod();
Remarque: la solution donnée ne fonctionne que pour les contrôleurs des directives d'élément (le nom de balise est utilisé pour obtenir le nom de la directive recherchée).