AngularJS. Come chiamare la funzione controller dall'esterno del componente controller


190

Come posso chiamare la funzione definita nel controller da qualsiasi luogo della pagina Web (al di fuori del componente controller)?

Funziona perfettamente quando premo il pulsante "get". Ma devo chiamarlo dall'esterno del controller div. La logica è: di default il mio div è nascosto. Da qualche parte nel menu di navigazione premo un pulsante e dovrebbe mostrare () il mio div ed eseguire la funzione "get". Come posso raggiungere questo obiettivo?

La mia pagina web è:

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

My 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){ }
      });
    };
   }

2
Quando dici "... da qualche parte nel menu di navigazione premi un pulsante ...", intendi dire che questa navigazione fa parte di un altro controller e desideri chiamare get()MyController dall'altro controller?
callmekatootie,

1
Per ora il menu di navigazione non è un controller. Solo HTML. Non sono sicuro che sia possibile chiamare la funzione controller da html / javascript, ecco perché ho pubblicato questa domanda. Ma sì, logico per rendere il menu di navigazione come controller separato. Come posso chiamare la funzione MyController.get () da NavigationMenu.Controller?
Pavel Zdarov

Risposte:


331

Ecco un modo per chiamare la funzione del controller dall'esterno:

angular.element(document.getElementById('yourControllerElementID')).scope().get();

dov'è get()una funzione dal tuo controller.

Puoi cambiare

document.getElementById('yourControllerElementID')` 

per

$('#yourControllerElementID')

Se stai usando jQuery.

Inoltre, se la tua funzione significa cambiare qualcosa sulla tua vista, dovresti chiamare

angular.element(document.getElementById('yourControllerElementID')).scope().$apply();

per applicare le modifiche.

Un'altra cosa da notare è che gli ambiti vengono inizializzati dopo il caricamento della pagina, quindi i metodi di chiamata dall'esterno dell'ambito devono sempre essere eseguiti dopo il caricamento della pagina. Altrimenti non arriverai affatto all'ambito.

AGGIORNARE:

Con le ultime versioni di angular, dovresti usare

angular.element(document.getElementById('yourControllerElementID')).injector().‌​get('$rootScope')

E sì, questa è, in effetti, una cattiva pratica , ma a volte hai solo bisogno di cose fatte veloci e sporche.


30
Sembra un odore di codice poiché la filosofia di Angular non è quella di mescolare il codice DOM con il codice Angular ...
JoeCool

8
quindi se non dovresti mescolare il codice DOM con il codice angolare, se vuoi che certe animazioni JQuery si attivino in risposta a una variazione variabile nel tuo controller angolare, come lo fai esattamente? Sarebbe banale fare dal controller, ma non ho idea di come farlo in modo pulito
gennaio

3
Non sono riuscito a far funzionare la $applyparte per me, fino a quando non ho inserito il codice in una funzione, ad es...scope.$apply(function() { scope.get() });
surfitscrollit

2
In realtà ho potuto accedere al controller con angular.element(document.getElementById('yourControllerElementID')).scope().controller; For ex. se uso: angular.element(document.getElementById('controller')).scope().get() genera un errore indefinito, ma se lo uso angular.element(document.getElementById('controller')).scope().controller.get()funziona.
vrunoa,

2
È possibile che questa soluzione non funzioni più in Angular 1.4.9? Non riesco ad accedere scope()a angular.element(...), perché restituisce indefinito e un vardump dell'elemento / oggetto angolare dice che la funzione scopesi trova all'interno dell'oggetto __proto__.
Smamatti,

37

Ho trovato un esempio su Internet.

Qualcuno ha scritto questo codice e ha funzionato perfettamente

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);
    });
}

dimostrazione

* Ho apportato alcune modifiche, vedi originale: font JSfiddle


2
Grazie Roger, molto utile!
Eduardo,

Lavorare con una libreria di convalida jQuery legacy che DEVE essere utilizzata. Quindi, è 1: riscrivi la libreria, 2: crea una direttiva per avvolgere la libreria, 3: 2 righe di codice per chiamare l'invio angolare quando valido ...
Michael K,

se non uso applicare quindi la funzione che aggiornerà i dati della vista non si rifletterà?
Monojit Sarkar,

13

La soluzione ha angular.element(document.getElementById('ID')).scope().get()smesso di funzionare per me in 1.5.2 angolare. Sombody menziona in un commento che questo non funziona anche in 1.4.9. L'ho risolto memorizzando l'ambito in una variabile globale:

var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
    $scope = function bar(){
        console.log("foo");        
    };
    scopeHolder = $scope;
})

chiama dal codice personalizzato:

scopeHolder.bar()

se si desidera limitare l'ambito solo a questo metodo. Ridurre al minimo l'esposizione dell'intero ambito. utilizzare la seguente tecnica.

var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
    $scope.bar = function(){
        console.log("foo");        
    };
    scopeHolder = $scope.bar;
})

chiama dal codice personalizzato:

scopeHolder()

Questo ha funzionato benissimo per me (anche dall'interno di un componente). Esiste un aspetto negativo nel fare questo oltre alla "cattiva pratica di chiamare roba angolare dall'esterno angolare" che non sono in grado di evitare nel mio scenario? stackoverflow.com/questions/42123120/...
RichC

Grazie signore per il tuo aiuto, stavo cercando una soluzione per questo da un po '
Ibrahim Amer,

11

La risposta di Dmitry funziona bene. Ho appena fatto un semplice esempio usando la stessa tecnica.

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 ; 
    }
}

7

Preferirei includere la fabbrica come dipendenze dai controller piuttosto che iniettarli con la loro riga di codice: 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'];


Hmm. @Anton ha commentato un violino qui sotto (nel maggio '13) che ENTRAMBI.
Jesse Chisholm,

5

Vale la pena considerare se avere il tuo menu senza alcun ambito associato è la strada giusta da percorrere. Non è proprio il modo angolare.

Ma, se è il modo in cui devi andare, puoi farlo aggiungendo le funzioni a $ rootScope e quindi all'interno di quelle funzioni usando $ broadcast per inviare eventi. il controller utilizza quindi $ on per ascoltare quegli eventi.

Un'altra cosa da considerare se si finisce per avere il proprio menu senza un ambito è che se si hanno più percorsi, tutti i controller dovranno avere il proprio upate e ottenere funzioni. (questo presuppone che tu abbia più controller)


puoi fare un semplice esempio di come chiamare la funzione .get () di ControllerOne da ControllerTwo? la mia logica è che ogni controller avrà le sue funzioni .get () .update (). Avrò MainMenuController da cui devo eseguire (secondo la voce di menu) .get () del controller necessario.
Pavel Zdarov,

ps, non il mio codice, ma mostra come avere più controller che condividono la funzionalità
Anton

4

Uso per lavorare con $ http, quando desidero ottenere alcune informazioni da una risorsa faccio quanto segue:

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);
    }}
    });

E il codice nel controller:

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);
    }
}

Mando al servizio quale funzione deve chiamare nel controller


3

Ho più percorsi e più controller, quindi non sono riuscito a far funzionare la risposta accettata. Ho scoperto che l'aggiunta della funzione alla finestra funziona:

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;

}

Quindi al di fuori del controller, questo può essere fatto:

function ElsewhereOnThePage() {
    if (typeof(fooreinit) == 'function') { fooreinit(); }
}

0

Chiamare la funzione Angular Scope dall'esterno del controller.

// Simply Use "Body" tag, Don't try/confuse using id/class.

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      

-1

Sono un utente di framework ionico e quello che ho trovato che fornirebbe costantemente l'ambito $ dell'attuale controller è:

angular.element(document.querySelector('ion-view[nav-view="active"]')).scope()

Ho il sospetto che questo possa essere modificato per adattarsi alla maggior parte degli scenari indipendentemente dal framework (o meno) trovando la query che avrà come target gli elementi DOM specifici che sono disponibili solo durante una determinata istanza del controller.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.