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

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.


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


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

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

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'];


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)


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


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

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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.