AngularJS。コントローラーコンポーネントの外部からコントローラー関数を呼び出す方法


190

コントローラーの下で定義された関数をWebページの任意の場所(コントローラーコンポーネントの外部)から呼び出すにはどうすればよいですか?

「取得」ボタンを押すと完全に動作します。しかし、divコントローラーの外部から呼び出す必要があります。ロジックは次のとおりです。デフォルトでは、私のdivは非表示です。ナビゲーションメニューのどこかでボタンを押すと、divがshow()され、「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 2013年

1
現時点では、ナビゲーションメニューはコントローラではありません。ただhtml。html / javascriptからコントローラー関数を呼び出すことが可能かどうかわからないため、この質問を投稿しました。しかし、ええ、ナビゲーションメニューを別のコントローラーとして作成するのは論理的です。NavigationMenu.ControllerからMyController.get()関数を呼び出すにはどうすればよいですか?
Pavel Zdarov 2013年

回答:


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コードを混在させる予定がない場合、Angularコントローラーの変数の変更に応じて特定のJQueryアニメーションを起動したい場合は、どのように実行しますか?コントローラから行うのは簡単ですが、私はそれをきれいに行う方法がわかりません
Jan

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()と機能します。
vrunoa 2015

2
このソリューションがAngular 1.4.9で機能しないことは可能ですか?undefinedを返し、角度要素/オブジェクトのvardump が関数が-object 内にあると言っているためscope()angular.element(...)でアクセスできません。scope__proto__
Smamatti 2016

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>

ジャバスクリプト

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

デモ

*いくつかの変更を行いました。オリジナルを参照してください:font JSfiddle


2
ロジャー、ありがとう。
エドゥアルド

使用する必要のあるレガシーjQuery検証ライブラリーの操作。つまり、1:ライブラリを書き換える、2:ライブラリをラップするディレクティブを作成する、3:有効な場合に角度送信を呼び出す2行のコードを作成する...
Michael K

適用を使用しない場合、ビューデータを更新する関数は反映されませんか?
Monojit Sarkar 2017年

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 = ['$ scope'、 'mySharedService'];


うーん。@Antonは、両方を行う以下のフィドル('13年5月)にコメントしました。
Jesse Chisholm

5

スコープが関連付けられていないメニューを用意するのが正しい方法かどうかを検討する価値があるかもしれません。それは実際には角度のある方法ではありません。

ただし、それが目的の方法である場合は、$ rootScopeに関数を追加し、$ broadcastを使用してそれらの関数内でイベントを送信することで実行できます。次に、コントローラーは$ onを使用してこれらのイベントをリッスンします。

スコープなしでメニューを作成する場合に考慮すべきもう1つのことは、複数のルートがある場合、すべてのコントローラーに独自の更新および取得機能が必要になることです。(これは、複数のコントローラーがあることを前提としています)


ControllerTwoからControllerOneの.get()関数を呼び出す方法の簡単な例を教えてください。私のロジックは、各コントローラーが独自の.get().update()関数を持つようにすることです。必要なコントローラーの.get()を(メニュー項目に従って)実行する必要があるMainMenuControllerを用意します。
Pavel Zdarov 2013年

ps、私のコードではありませんが、複数のコントローラーが機能を共有する方法を示しています
Anton

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

コントローラーの外部から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フレームワークのユーザーであり、現在のコントローラーの$ scopeを常に提供していることがわかりました。

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

これは、特定のコントローラーインスタンスでのみ使用可能な特定のDOM要素を対象とするクエリを見つけることで、フレームワークに関係なく(またはそうではない)ほとんどのシナリオに適合するように変更できると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.