AngularJS. Aufrufen der Controller-Funktion von außerhalb der Controller-Komponente

190

Wie kann ich die unter Controller definierte Funktion von jedem Ort der Webseite (außerhalb der Controller-Komponente) aufrufen?

Es funktioniert perfekt, wenn ich die Taste "get" drücke. Aber ich muss es von außerhalb des Div-Controllers aufrufen. Die Logik ist: Standardmäßig ist mein Div versteckt. Irgendwo im Navigationsmenü drücke ich eine Taste und sie sollte () mein div anzeigen und die Funktion "get" ausführen. Wie kann ich das erreichen?

Meine Webseite ist:

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

Mein 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){ }
      });
    };
   }
Pavel Zdarov
quelle
2
Wenn Sie "... irgendwo im Navigationsmenü drücken Sie eine Taste ..." sagen, möchten Sie damit sagen, dass diese Navigation Teil eines anderen Controllers ist und Sie den get()MyController von einem anderen Controller aus aufrufen möchten?
Callmekatootie
1
Derzeit ist das Navigationsmenü kein Controller. Nur HTML. Ich bin mir nicht sicher, ob es möglich ist, die Controller-Funktion von HTML / Javascript aus aufzurufen. Deshalb habe ich diese Frage gestellt. Aber ja, es ist logisch, das Navigationsmenü als separaten Controller zu erstellen. Wie kann ich die Funktion MyController.get () von NavigationMenu.Controller aus aufrufen?
Pavel Zdarov

Antworten:

331

Hier ist eine Möglichkeit, die Funktion des Controllers von außerhalb aufzurufen:

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

Wo get()ist eine Funktion von Ihrem Controller.

Sie können wechseln

document.getElementById('yourControllerElementID')` 

zu

$('#yourControllerElementID')

Wenn Sie jQuery verwenden.

Wenn Ihre Funktion bedeutet, dass Sie etwas in Ihrer Ansicht ändern, sollten Sie aufrufen

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

um die Änderungen anzuwenden.

Eine weitere Sache, die Sie beachten sollten, ist, dass Bereiche nach dem Laden der Seite initialisiert werden. Daher sollten Methoden von außerhalb des Bereichs immer nach dem Laden der Seite aufgerufen werden. Andernfalls gelangen Sie überhaupt nicht zum Anwendungsbereich.

AKTUALISIEREN:

Mit den neuesten Versionen von Angular sollten Sie verwenden

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

Und ja, das ist in der Tat eine schlechte Praxis , aber manchmal muss man einfach Dinge schnell und schmutzig erledigen.

Dmitry Mina
quelle
30
Dies scheint ein Code-Geruch zu sein, da Angulars Philosophie darin besteht, DOM-Code nicht mit Angular-Code zu mischen ...
JoeCool
8
Wenn Sie also keinen DOM-Code mit Angular-Code mischen sollen und bestimmte JQuery-Animationen als Reaktion auf eine variable Änderung in Ihrem Angular-Controller ausgelöst werden sollen - wie genau machen Sie das? Es wäre trivial sein von der Steuerung zu tun, aber ich habe keine Ahnung , wie es sauber zu machen
Jan
3
Ich konnte das $applyTeil nicht für mich arbeiten lassen, bis ich den Code in eine Funktion eingewickelt hatte, zB..scope.$apply(function() { scope.get() });
surfitscrollit
2
Ich könnte tatsächlich mit angular.element(document.getElementById('yourControllerElementID')).scope().controller; For zum Beispiel auf den Controller zugreifen . if use: angular.element(document.getElementById('controller')).scope().get() wirft und undefinierten Fehler, aber wenn ich es benutze angular.element(document.getElementById('controller')).scope().controller.get(), funktioniert es.
Vrunoa
2
Ist es möglich, dass diese Lösung in Angular 1.4.9 nicht mehr funktioniert? Ich kann nicht den Zugang scope()in das angular.element(...), weil es und ein vardump des Winkelelement / Objekt nicht definiert Rückgabe sagt , dass die Funktion scopein der Lage ist __proto__-Objekt.
Smamatti
37

Ich habe ein Beispiel im Internet gefunden.

Ein Typ hat diesen Code geschrieben und perfekt funktioniert

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

Demo

* Ich habe einige Änderungen vorgenommen, siehe Original: font JSfiddle

Roger Ramos
quelle
2
Danke Roger, sehr nützlich!
Eduardo
Arbeiten mit einer älteren jQuery-Validierungsbibliothek, die verwendet werden muss. Also ist es entweder 1: Schreiben Sie die Bibliothek neu, 2: Erstellen Sie eine Direktive, um die Bibliothek zu verpacken, 3: 2 Codezeilen, um die eckige Übermittlung aufzurufen, wenn sie gültig ist ...
Michael K
Wenn ich nicht anwenden verwende, wird die Funktion, die die Ansichtsdaten aktualisiert, nicht wiedergegeben.
Monojit Sarkar
13

Die Lösung angular.element(document.getElementById('ID')).scope().get()hat in Winkel 1.5.2 für mich nicht mehr funktioniert. Sombody erwähnt in einem Kommentar, dass dies auch in 1.4.9 nicht funktioniert. Ich habe es behoben, indem ich den Bereich in einer globalen Variablen gespeichert habe:

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

Aufruf vom benutzerdefinierten Code:

scopeHolder.bar()

Wenn Sie den Bereich nur auf diese Methode beschränken möchten. Minimierung der Exposition des gesamten Anwendungsbereichs. Verwenden Sie die folgende Technik.

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

Aufruf vom benutzerdefinierten Code:

scopeHolder()
user1121883
quelle
Dies hat bei mir sehr gut funktioniert (auch innerhalb einer Komponente). Gibt es einen anderen Nachteil als nur die "schlechte Praxis, eckige Dinge von außerhalb eckig zu nennen", die ich in meinem Szenario nicht vermeiden kann? stackoverflow.com/questions/42123120/…
RichC
Vielen Dank, Sir, für Ihre Hilfe. Ich habe seit einiger Zeit nach einer Lösung dafür gesucht
Ibrahim Amer
11

Dmitrys Antwort funktioniert gut. Ich habe gerade ein einfaches Beispiel mit der gleichen Technik gemacht.

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 ; 
    }
}
Razan Paul
quelle
7

Ich würde die Factory lieber als Abhängigkeiten von den Controllern einschließen, als ihnen eine eigene Codezeile hinzuzufügen : 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'];

getetbro
quelle
Hmm. @Anton hat unten (im Mai '13) eine Geige kommentiert, die BEIDE macht.
Jesse Chisholm
5

Es kann eine Überlegung wert sein, ob ein Menü ohne zugehörigen Bereich der richtige Weg ist. Es ist nicht wirklich der eckige Weg.

Wenn Sie dies jedoch tun müssen, können Sie dies tun, indem Sie die Funktionen zu $ ​​rootScope hinzufügen und dann innerhalb dieser Funktionen $ Broadcast zum Senden von Ereignissen verwenden. Ihr Controller verwendet dann $ on, um auf diese Ereignisse zu warten.

Eine andere Sache, die Sie berücksichtigen sollten, wenn Sie Ihr Menü ohne Bereich haben, ist, dass, wenn Sie mehrere Routen haben, alle Ihre Controller ihre eigene Aktualisierung haben und Funktionen erhalten müssen. (Dies setzt voraus, dass Sie mehrere Controller haben)

Anton
quelle
Können Sie ein einfaches Beispiel für den Aufruf der .get () -Funktion von ControllerOne von ControllerTwo aus geben? Meine Logik ist, dass jeder Controller seine eigenen .get () .update () -Funktionen hat. Ich werde MainMenuController haben, von dem aus ich (je nach Menüpunkt) .get () des notwendigen Controllers ausführen muss.
Pavel Zdarov
ps, nicht mein Code, aber es zeigt, wie mehrere Controller Funktionen gemeinsam nutzen können
Anton
4

Ich arbeite mit $ http, wenn ich Informationen von einer Ressource erhalten möchte, gehe ich wie folgt vor:

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

Und der Code in der Steuerung:

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

Ich sende an den Dienst, welche Funktion in der Steuerung aufgerufen werden muss

rodrimmb
quelle
3

Ich habe mehrere Routen und mehrere Controller, sodass ich die akzeptierte Antwort nicht zum Laufen bringen konnte. Ich fand, dass das Hinzufügen der Funktion zum Fenster funktioniert:

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;

}

Dann kann dies außerhalb des Controllers durchgeführt werden:

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

Rufen Sie die Angular Scope-Funktion von außerhalb des Controllers auf.

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

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      
Sam Ruben
quelle
-1

Ich bin ein Ionic-Framework-Benutzer und derjenige, den ich gefunden habe, der den $ -Bereich des aktuellen Controllers konsistent bereitstellt, ist:

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

Ich vermute, dass dies geändert werden kann, um den meisten Szenarien unabhängig vom Framework (oder nicht) zu entsprechen, indem die Abfrage gefunden wird, die auf die spezifischen DOM-Elemente abzielt, die nur während einer bestimmten Controller-Instanz verfügbar sind.

Matt Ray
quelle