AngularJS. Jak wywołać funkcję kontrolera spoza komponentu kontrolera

190

Jak mogę wywołać funkcję zdefiniowaną w kontrolerze z dowolnego miejsca strony internetowej (poza komponentem kontrolera)?

Działa idealnie po naciśnięciu przycisku „pobierz”. Ale muszę to nazwać spoza kontrolera div. Logika brzmi: domyślnie mój div jest ukryty. Gdzieś w menu nawigacyjnym wciskam przycisk, który powinien pokazać () mój div i uruchomić funkcję „get”. Jak mogę to osiągnąć?

Moja strona to:

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

Mój 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
źródło
2
Kiedy mówisz „... gdzieś w menu nawigacyjnym naciskasz przycisk ...”, czy masz na myśli, że ta nawigacja jest częścią innego kontrolera i chcesz zadzwonić do get()MyController z innego kontrolera?
callmekatootie
1
Na razie menu nawigacyjne nie jest kontrolerem. Tylko HTML. Nie jestem pewien, czy można wywołać funkcję kontrolera z html / javascript, dlatego opublikowałem to pytanie. Ale tak, logiczne jest, aby menu nawigacyjne było oddzielnym kontrolerem. Jak mogę wywołać funkcję MyController.get () z NavigationMenu.Controller?
Pavel Zdarov

Odpowiedzi:

331

Oto sposób na wywołanie funkcji kontrolera spoza niej:

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

gdzie get()jest funkcja z twojego kontrolera.

Możesz zmienić

document.getElementById('yourControllerElementID')` 

do

$('#yourControllerElementID')

Jeśli używasz jQuery.

Ponadto, jeśli twoja funkcja oznacza zmianę czegokolwiek w twoim Widoku, powinieneś zadzwonić

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

zastosować zmiany.

Jeszcze jedną rzeczą, na którą należy zwrócić uwagę, że zakresy są inicjowane po załadowaniu strony, więc metody wywoływania spoza zakresu powinny zawsze być wykonywane po załadowaniu strony. W przeciwnym razie w ogóle nie dojdziesz do zakresu.

AKTUALIZACJA:

Z najnowszymi wersjami angulara powinieneś używać

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

I tak, to jest w rzeczywistości zła praktyka , ale czasami potrzebujesz po prostu rzeczy wykonanych szybko i brudno.

Dmitry Mina
źródło
30
Wydaje się to zapachu kodu ponieważ filozofia kątowego nie jest mieszanie kodu DOM z kodem Kątowymi ...
JoeCool
8
więc jeśli nie masz mieszać kodu DOM z kodem Angular, jeśli chcesz, aby niektóre animacje JQuery odpalały w reakcji na zmianę zmiennej w kontrolerze Angular - jak dokładnie to robisz? Byłoby to trywialne z poziomu kontrolera, ale nie mam pojęcia, jak to zrobić czysto
stycznia 13
3
Nie mogłem zmusić tej $applyczęści do pracy, dopóki nie zapakowałem kodu w funkcję, np...scope.$apply(function() { scope.get() });
surfitscrollit
2
Właściwie mogłem uzyskać dostęp do kontrolera za pomocą angular.element(document.getElementById('yourControllerElementID')).scope().controller; np. if use: angular.element(document.getElementById('controller')).scope().get() zgłasza i nieokreślony błąd, ale jeśli angular.element(document.getElementById('controller')).scope().controller.get()go używam , działa.
vrunoa
2
Czy to możliwe, że to rozwiązanie nie działa już w Angular 1.4.9? Nie mogę uzyskać dostępu scope()w angular.element(...), ponieważ zwraca niezdefiniowane, a vardump elementu / obiektu kątowego mówi, że funkcja scopeznajduje się w obiekcie __proto__-object.
Smamatti
37

Znalazłem przykład w Internecie.

Jakiś facet napisał ten kod i działał doskonale

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

Próbny

* Dokonałem pewnych modyfikacji, patrz oryginał: czcionka JSfiddle

Roger Ramos
źródło
2
Dzięki, Roger, bardzo przydatne!
Eduardo,
Praca ze starszą biblioteką sprawdzania poprawności jQuery, której MUSI być używany. Tak więc albo 1: przepisz bibliotekę, 2: utwórz dyrektywę, by owinąć bibliotekę, 3: 2 wiersze kodu, aby wywołać przekazywanie kątowe, gdy jest ważne ...
Michael K
jeśli nie używam zastosuj, to funkcja, która zaktualizuje dane widoku, nie zostanie odzwierciedlona?
Monojit Sarkar
13

Rozwiązanie angular.element(document.getElementById('ID')).scope().get()przestało działać dla mnie w wersji kątowej 1.5.2. Sombody wspomina w komentarzu, że nie działa to również w 1.4.9. Naprawiłem to, przechowując zakres w zmiennej globalnej:

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

zadzwoń z niestandardowego kodu:

scopeHolder.bar()

jeśli chcesz ograniczyć zakres tylko do tej metody. Aby zminimalizować ekspozycję całego zakresu. użyj następującej techniki.

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

zadzwoń z niestandardowego kodu:

scopeHolder()
użytkownik1121883
źródło
Działa to dla mnie świetnie (nawet z wnętrza komponentu). Czy jest to wada inna niż tylko „zła praktyka nazywania rzeczy kątowych z zewnątrz”, których nie jestem w stanie uniknąć w moim scenariuszu? stackoverflow.com/questions/42123120/…
RichC
Dzięki proszę pana za pomoc Od jakiegoś czasu szukałem rozwiązania
Ibrahim Amer
11

Odpowiedź Dmitry'ego działa dobrze. Właśnie zrobiłem prosty przykład przy użyciu tej samej techniki.

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
źródło
7

Wolę uwzględnić fabrykę jako zależności od kontrolerów, niż wstrzyknąć im własną linię kodu: 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'];

getsetbro
źródło
Hmm @Anton skomentował skrzypce poniżej (w maju '13), który ZARÓWNO.
Jesse Chisholm,
5

Warto rozważyć, czy posiadanie menu bez powiązanego zakresu jest właściwą drogą. To naprawdę nie jest kątowy sposób.

Ale jeśli jest to sposób, w jaki musisz iść, możesz to zrobić, dodając funkcje do $ rootScope, a następnie w obrębie tych funkcji, używając $ broadcast do wysyłania zdarzeń. kontroler używa $ on do nasłuchiwania tych zdarzeń.

Inną rzeczą do rozważenia, jeśli ostatecznie skończysz z menu bez zakresu, jest to, że jeśli masz wiele tras, wszystkie kontrolery będą musiały mieć własną aktualizację i uzyskać funkcje. (przy założeniu, że masz wielu kontrolerów)

Anton
źródło
czy możesz podać prosty przykład wywołania funkcji .get () w ControllerOne z ControllerTwo? moja logika jest taka, że ​​każdy kontroler będzie miał swoje własne funkcje .get () .update (). Będę miał MainMenuController, z którego muszę wykonać (zgodnie z pozycją menu) .get () niezbędnego kontrolera.
Pavel Zdarov
ps, nie mój kod, ale pokazuje, jak mieć wiele funkcji współdzielenia kontrolerów
Anton
4

Używam do pracy z $ http, gdy chcę uzyskać informacje z zasobu, wykonuję następujące czynności:

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

I kod w kontrolerze:

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

Przesyłam do serwisu, jakie funkcje mają wywołać w kontrolerze

rodrimmb
źródło
3

Mam wiele tras i wiele kontrolerów, więc nie mogłem uzyskać akceptowanej odpowiedzi do pracy. Odkryłem, że dodanie funkcji do okna działa:

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;

}

Następnie poza kontrolerem można to zrobić:

function ElsewhereOnThePage() {
    if (typeof(fooreinit) == 'function') { fooreinit(); }
}
jaybro
źródło
0

Wywołaj funkcję zakresu kątowego spoza kontrolera.

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

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      
sam ruben
źródło
-1

Jestem użytkownikiem frameworka Ionic i znalazłem, który konsekwentnie zapewnia zakres $ bieżącego kontrolera:

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

Podejrzewam, że można to zmodyfikować, aby pasowało do większości scenariuszy niezależnie od frameworka (lub nie), znajdując zapytanie, które będzie kierowane do określonych elementów DOM, które są dostępne tylko podczas danej instancji kontrolera.

Matt Ray
źródło