КутовийJS. Як викликати функцію контролера за межами компонента контролера


190

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

Він прекрасно працює, коли натискаю кнопку "отримати". Але мені потрібно зателефонувати за межі диві-контролера. Логіка така: за замовчуванням мій дів приховано. Десь у навігаційному меню я натискаю кнопку, і вона повинна показувати () мій 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 з
кутовим

8
Так що якщо ви не повинні змішувати код DOM з кутовим кодом, якщо ви хочете, щоб певні анімації JQuery вимикалися у відповідь на змінні зміни вашого кутового контролера - як саме це робити? Це було б банально з контролера, але я поняття не маю, як це зробити чисто
січня

3
Я не міг змусити $applyчастину працювати на мене, поки я не ..scope.$apply(function() { scope.get() });
зав'язав

2
Я фактично міг отримати доступ до контролера за допомогою angular.element(document.getElementById('yourControllerElementID')).scope().controller; Ex ex. якщо використовувати: angular.element(document.getElementById('controller')).scope().get() кидки та невизначена помилка, але якщо я використовую, angular.element(document.getElementById('controller')).scope().controller.get()це працює.
vrunoa

2
Чи можливо, що це рішення більше не працює в Angular 1.4.9? Я не можу отримати доступ scope()до angular.element(...), тому що він повертається невизначеним, а vardump кутового елемента / об'єкта говорить про те, що функція scopeрозташована в межах __proto__-об'єкта.
Smamatti

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 рядки коду для виклику кутового подання, коли дійсні ...
Michael K

якщо я не використовую застосовувати, то функція, яка буде оновлювати дані перегляду, не відображатиметься?
Монойт Саркар

13

Розчин angular.element(document.getElementById('ID')).scope().get()перестав працювати для мене в кутовій 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()

Це чудово працювало для мене (навіть зсередини компонента). Чи є якийсь мінус зробити це іншим, ніж просто "погана практика називати кутові речі поза кутовими", яких я не в змозі уникнути у своєму сценарії? stackoverflow.com/questions/42123120 / ...
RichC

Дякую, сер за вашу допомогу. Я деякий час шукав рішення для цього
Ibrahim Amer

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 = ['$ obseg', 'mySharedService'];


Хм. @Anton прокоментував нижче загадку (у травні '13), що робить БОТИ.
Джессі Чісгольм

5

Можливо, варто подумати, якщо правильне дію має ваше меню без будь-якої асоційованої області. Це не дуже кутовий шлях.

Але, якщо це шлях, який вам потрібно пройти, то ви можете це зробити, додавши функції до $ rootScope, а потім усередині цих функцій, використовуючи $ широкомовлення для надсилання подій. потім ваш контролер використовує $ on для прослуховування цих подій.

Інша річ, яку слід врахувати, якщо у вас все-таки є меню без розмаху, це те, що якщо у вас є кілька маршрутів, то всі ваші контролери повинні мати власний upate та отримати функції. (це за умови, що у вас є кілька контролерів)


Ви можете навести якийсь простий приклад того, як викликати .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);
    }
}

Я надсилаю службі, яку функцію потрібно викликати в контролері


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

0

Виклик функції кутового діапазону поза контролером.

// 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, доступні лише під час певного екземпляра контролера.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.