AngularJS. Как вызвать функцию контроллера извне компонента контроллера

190

Как я могу вызвать функцию, определенную под контроллером, из любого места веб-страницы (вне компонента контроллера)?

Он отлично работает, когда я нажимаю кнопку «получить». Но мне нужно позвонить со стороны контроллера div. Логика такова: по умолчанию мой div скрыт. Где-то в меню навигации я нажимаю кнопку, и она должна показать () мой div и выполнить функцию «get». Как мне этого добиться?

Моя веб-страница:

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

Мой 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
Когда вы говорите «... где-то в меню навигации вы нажимаете кнопку ...», вы хотите сказать, что эта навигация является частью другого контроллера, и вы хотите вызвать get()MyController из другого контроллера?
callmekatootie
1
На данный момент навигационное меню не является контроллером. Просто HTML. Не уверен, что можно вызвать функцию контроллера из html / javascript, поэтому я разместил этот вопрос. Но да, логично сделать навигационное меню отдельным контроллером. Как я могу вызвать функцию MyController.get () из NavigationMenu.Controller?
Павел Здаров

Ответы:

331

Вот способ вызвать функцию контроллера извне:

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

где get()функция от вашего контроллера.

Вы можете переключиться

document.getElementById('yourControllerElementID')` 

в

$('#yourControllerElementID')

Если вы используете JQuery.

Кроме того, если ваша функция означает что-то изменить в вашем представлении, вы должны вызвать

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

применить изменения.

Еще одна вещь, которую вы должны заметить, это то, что области инициализируются после загрузки страницы, поэтому вызов методов из-за пределов области всегда должен выполняться после загрузки страницы. Иначе вы вообще не попадете в сферу.

ОБНОВИТЬ:

С последними версиями угловых вы должны использовать

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

И да, это на самом деле плохая практика , но иногда вам просто нужно сделать что-то быстрое и грязное.

Дмитрий Мина
источник
30
Это похоже на запах кода, так как философия Angular заключается не в том, чтобы смешивать код DOM с кодом Angular ...
JoeCool
8
так что если вы не должны смешивать код DOM с кодом Angular, если вы хотите, чтобы определенные анимации JQuery запускались в ответ на изменение переменной в вашем контроллере Angular - как именно вы это сделаете? Это было бы тривиально сделать из контроллера, но я понятия не имею, как сделать это чисто
января
3
Я не мог заставить $applyчасть работать на меня, пока я не обернул код в функцию, например..scope.$apply(function() { scope.get() });
surfitscrollit
2
Я на самом деле мог получить доступ к контроллеру с angular.element(document.getElementById('yourControllerElementID')).scope().controller; For ex. Если использовать: angular.element(document.getElementById('controller')).scope().get() бросает и неопределенная ошибка, но если я использую angular.element(document.getElementById('controller')).scope().controller.get()это работает.
Вруноа
2
Возможно ли, что это решение больше не работает в Angular 1.4.9? Я не могу получить доступ scope()в angular.element(...), потому что он возвращает неопределенное и vardump углового элемента / объекта говорит, что функция scopeнаходится внутри __proto__-объекта.
Смаматти
37

Я нашел пример в интернете.

Какой-то парень написал этот код и работал отлично

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

демонстрация

* Я сделал некоторые модификации, см. Оригинал: шрифт JSfiddle

Роджер Рамос
источник
2
Спасибо, Роджер, очень полезно!
Эдуардо
Работа с устаревшей библиотекой проверки jQuery, которая ДОЛЖНА использоваться. Таким образом, это либо 1: переписать библиотеку, 2: создать директиву, чтобы обернуть библиотеку, 3: 2 строки кода, чтобы вызвать угловую
Майкл К,
Если я не использую «Применить», функция обновления данных о просмотре не будет отражена?
Моножит Саркар
13

Решение angular.element(document.getElementById('ID')).scope().get()у меня перестало работать в angular 1.5.2. Кто-то упомянул в комментарии, что это не работает и в 1.4.9. Я исправил это, сохранив область видимости в глобальной переменной:

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

звонок из пользовательского кода:

scopeHolder.bar()

если вы хотите ограничить область действия только этим методом. Чтобы минимизировать воздействие всего объема. используйте следующую технику.

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

звонок из пользовательского кода:

scopeHolder()
user1121883
источник
Это прекрасно сработало для меня (даже изнутри компонента). Есть ли у этого недостаток, кроме «плохой практики называть угловатые вещи извне угловыми», которых я не могу избежать в моем сценарии? stackoverflow.com/questions/42123120/…
RichC
Спасибо, сэр, за вашу помощь. Я некоторое время искал решение для этого
Ибрагим Амер
11

Дмитрий отвечает отлично. Я просто сделал простой пример, используя ту же технику.

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

Я бы предпочел включить фабрику в качестве зависимостей от контроллеров, чем вводить их с собственной строкой кода: 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
источник
Хм. @Anton прокомментировал скрипку ниже (в мае 13 года), которая делает ОБА.
Джесси Чисхолм
5

Возможно, стоит подумать, если ваше меню без какой-либо связанной области является правильным способом. Это не совсем угловой путь.

Но, если вам нужно идти по этому пути, вы можете сделать это, добавив функции в $ rootScope, а затем в эти функции, используя $ broadcast для отправки событий. Затем ваш контроллер использует $ on для прослушивания этих событий.

Еще одна вещь, которую стоит учесть, если вы в конечном итоге получите свое меню без области действия, это то, что если у вас есть несколько маршрутов, то все ваши контроллеры должны будут иметь свои собственные функции обновления и получения. (это предполагает, что у вас есть несколько контроллеров)

Антон
источник
Можете ли вы привести какой-нибудь простой пример того, как вызвать функцию .get () ControllerOne из ControllerTwo? Моя логика такова, что каждый контроллер будет иметь свои собственные функции .get () .update (). У меня будет MainMenuController, из которого мне нужно выполнить (в соответствии с пунктом меню) .get () необходимого контроллера.
Павел Здаров
PS, не мой код, но он показывает, как иметь несколько функций совместного использования контроллеров
Антон
4

Я использую для работы с $ http, когда хочу получить некоторую информацию из ресурса, я делаю следующее:

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

И код в контроллере:

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

Отправляю в сервис какую функцию должны вызывать в контроллере

rodrimmb
источник
3

У меня есть несколько маршрутов и несколько контроллеров, поэтому я не смог получить принятый ответ для работы. Я обнаружил, что добавление функции в окно работает:

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;

}

Тогда вне контроллера это можно сделать:

function ElsewhereOnThePage() {
    if (typeof(fooreinit) == 'function') { fooreinit(); }
}
jaybro
источник
0

Вызовите функцию Angular Scope снаружи контроллера.

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

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      
Сэм Рубен
источник
-1

Я - пользователь Ionic Framework, и я обнаружил, что он будет постоянно предоставлять область $ текущего контроллера:

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

Я подозреваю, что это может быть изменено, чтобы соответствовать большинству сценариев независимо от структуры (или нет), находя запрос, который будет нацелен на определенные элементы DOM, которые доступны только во время данного экземпляра контроллера.

Мэтт Рэй
источник