Comment appeler une fonction définie sous le contrôleur à partir de n'importe quel endroit de la page Web (en dehors du composant du contrôleur)?
Cela fonctionne parfaitement lorsque j'appuie sur le bouton "get". Mais je dois l'appeler de l'extérieur du contrôleur div. La logique est la suivante: par défaut ma div est masquée. Quelque part dans le menu de navigation, j'appuie sur un bouton et il devrait afficher () mon div et exécuter la fonction "get". Comment je peux y arriver?
Ma page web est:
<div ng-controller="MyController">
<input type="text" ng-model="data.firstname" required>
<input type='text' ng-model="data.lastname" required>
<form ng-submit="update()"><input type="submit" value="update"></form>
<form ng-submit="get()"><input type="submit" value="get"></form>
</div>
Mon js:
function MyController($scope) {
// default data and structure
$scope.data = {
"firstname" : "Nicolas",
"lastname" : "Cage"
};
$scope.get = function() {
$.ajax({
url: "/php/get_data.php?",
type: "POST",
timeout: 10000, // 10 seconds for getting result, otherwise error.
error:function() { alert("Temporary error. Please try again...");},
complete: function(){ $.unblockUI();},
beforeSend: function(){ $.blockUI()},
success: function(data){
json_answer = eval('(' + data + ')');
if (json_answer){
$scope.$apply(function () {
$scope.data = json_answer;
});
}
}
});
};
$scope.update = function() {
$.ajax({
url: "/php/update_data.php?",
type: "POST",
data: $scope.data,
timeout: 10000, // 10 seconds for getting result, otherwise error.
error:function() { alert("Temporary error. Please try again...");},
complete: function(){ $.unblockUI();},
beforeSend: function(){ $.blockUI()},
success: function(data){ }
});
};
}
Voici un moyen d'appeler la fonction du contrôleur depuis l'extérieur:
angular.element(document.getElementById('yourControllerElementID')).scope().get();
où get()
est une fonction de votre contrôleur.
Vous pouvez passer
document.getElementById('yourControllerElementID')`
à
$('#yourControllerElementID')
Si vous utilisez jQuery.
De plus, si votre fonction implique de changer quoi que ce soit dans votre vue, vous devez appeler
angular.element(document.getElementById('yourControllerElementID')).scope().$apply();
appliquer les modifications.
Une dernière chose à noter est que les étendues sont initialisées après le chargement de la page. Par conséquent, l'appel de méthodes en dehors de l'étendue doit toujours être effectué après le chargement de la page. Sinon, vous n'irez pas du tout à la portée.
PDATE:
Avec les dernières versions de angular, vous devriez utiliser
angular.element(document.getElementById('yourControllerElementID')).injector().get('$rootScope')
Et oui, c’est en fait ne mauvaise pratique, mais vous avez parfois besoin que les choses soient faites rapidement et de manière sale.
J'ai trouvé un exemple sur Internet.
Un gars a écrit ce code et a parfaitement fonctionné
HTML
<div ng-cloak ng-app="ManagerApp">
<div id="MainWrap" class="container" ng-controller="ManagerCtrl">
<span class="label label-info label-ext">Exposing Controller Function outside the module via onClick function call</span>
<button onClick='ajaxResultPost("Update:Name:With:JOHN","accept",true);'>click me</button>
<br/> <span class="label label-warning label-ext" ng-bind="customParams.data"></span>
<br/> <span class="label label-warning label-ext" ng-bind="customParams.type"></span>
<br/> <span class="label label-warning label-ext" ng-bind="customParams.res"></span>
<br/>
<input type="text" ng-model="sampletext" size="60">
<br/>
</div>
</div>
JAVASCRIPT
var angularApp = angular.module('ManagerApp', []);
angularApp.controller('ManagerCtrl', ['$scope', function ($scope) {
$scope.customParams = {};
$scope.updateCustomRequest = function (data, type, res) {
$scope.customParams.data = data;
$scope.customParams.type = type;
$scope.customParams.res = res;
$scope.sampletext = "input text: " + data;
};
}]);
function ajaxResultPost(data, type, res) {
var scope = angular.element(document.getElementById("MainWrap")).scope();
scope.$apply(function () {
scope.updateCustomRequest(data, type, res);
});
}
* J'ai fait quelques modifications, voir l'original: font JSfiddle
La réponse de Dmitry fonctionne bien. Je viens de faire un exemple simple en utilisant la même technique.
jsfiddle: http://jsfiddle.net/o895a8n8/5/
<button onclick="call()">Call Controller's method from outside</button>
<div id="container" ng-app="" ng-controller="testController">
</div>
.
function call() {
var scope = angular.element(document.getElementById('container')).scope();
scope.$apply(function(){
scope.msg = scope.msg + ' I am the newly addded message from the outside of the controller.';
})
alert(scope.returnHello());
}
function testController($scope) {
$scope.msg = "Hello from a controller method.";
$scope.returnHello = function() {
return $scope.msg ;
}
}
La solution angular.element(document.getElementById('ID')).scope().get()
a cessé de fonctionner pour moi dans angular 1.5.2. Sombody mentionne dans un commentaire que cela ne marche pas non plus dans la version 1.4.9. Je l'ai corrigé en stockant la portée dans une variable globale:
var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
$scope = function bar(){
console.log("foo");
};
scopeHolder = $scope;
})
appel du code personnalisé:
scopeHolder.bar()
si vous voulez restreindre la portée à cette méthode uniquement. Pour minimiser l'exposition de la portée entière. utiliser la technique suivante.
var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
$scope.bar = function(){
console.log("foo");
};
scopeHolder = $scope.bar;
})
appel du code personnalisé:
scopeHolder()
Je préférerais inclure l’usine comme dépendances des contrôleurs que de leur injecter leur propre ligne de code: http://jsfiddle.net/XqDxG/550/
myModule.factory('mySharedService', function($rootScope) {
return sharedService = {thing:"value"};
});
function ControllerZero($scope, mySharedService) {
$scope.thing = mySharedService.thing;
ControllerZero. $ Inject = ['$ scope', 'mySharedService'];
Il peut être intéressant de considérer si le menu sans menu associé est la bonne solution. Ce n'est pas vraiment le chemin angular.
Mais, si c'est ce que vous devez faire, vous pouvez le faire en ajoutant les fonctions à $ rootScope, puis en utilisant celles-ci en utilisant $ broadcast pour envoyer des événements. votre contrôleur utilise ensuite $ on pour écouter ces événements.
Une autre chose à considérer si vous finissez par avoir votre menu sans portée est que si vous avez plusieurs routes, tous vos contrôleurs devront avoir leur propre fonction upate et get. (en supposant que vous ayez plusieurs contrôleurs)
J'avais l'habitude de travailler avec $ http, quand je veux obtenir des informations d'une ressource, je fais ce qui suit:
angular.module('services.value', [])
.service('Value', function($http, $q) {
var URL = "http://localhost:8080/myWeb/rest/";
var valid = false;
return {
isValid: valid,
getIsValid: function(callback){
return $http.get(URL + email+'/'+password, {cache: false})
.success(function(data){
if(data === 'true'){ valid = true; }
}).then(callback);
}}
});
Et le code dans le contrôleur:
angular.module('controllers.value', ['services.value'])
.controller('ValueController', function($scope, Value) {
$scope.obtainValue = function(){
Value.getIsValid(function(){$scope.printValue();});
}
$scope.printValue = function(){
console.log("Do it, and value is " Value.isValid);
}
}
J'envoie au service quelle fonction doit appeler le contrôleur
J'ai plusieurs itinéraires et plusieurs contrôleurs, je ne pouvais donc pas obtenir la réponse acceptée au travail. J'ai trouvé que l'ajout de la fonction à la fenêtre fonctionne:
fooModule.controller("fooViewModel", function ($scope, fooService, $http, $q, $routeParams, $window, $location, viewModelHelper, $interval) {
$scope.initFoo = function () {
// do angular stuff
}
var initialize = function () {
$scope.initFoo();
}
initialize();
window.fooreinit = initialize;
}
Ensuite, en dehors du contrôleur, cela peut être fait:
function ElsewhereOnThePage() {
if (typeof(fooreinit) == 'function') { fooreinit(); }
}
Je suis un utilisateur du cadre Ionic et celui que je trouve qui fournirait systématiquement la portée $ du contrôleur actuel est le suivant:
angular.element(document.querySelector('ion-view[nav-view="active"]')).scope()
Je suppose que cela peut être modifié pour s’adapter à la plupart des scénarios, indépendamment du cadre (ou non) en recherchant la requête qui ciblera le ou les éléments DOM spécifiques disponibles uniquement au cours d’une instance de contrôleur donnée.