AngularJS。如何从控制器组件外部调用控制器功能


190

如何从网页的任何位置(控制器组件外部)调用控制器下定义的函数?

当我按下“获取”按钮时,它可以完美工作。但是我需要从div控制器外部调用它。逻辑是:默认情况下,我的div是隐藏的。在导航菜单中的某个位置,我按了一个按钮,它应该显示()我的div并执行“获取”功能。我该如何实现?

我的网页是:

<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调用控制器功能,这就是为什么我发布了这个问题。但是,是的,将导航菜单作为单独的控制器是合乎逻辑的。如何从NavigationMenu.Controller调用MyController.get()函数?
帕维尔·兹达洛夫

Answers:


331

这是从外部调用控制器功能的一种方法:

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

get()您的控制器的功能在哪里。

您可以切换

document.getElementById('yourControllerElementID')` 

$('#yourControllerElementID')

如果您使用的是jQuery。

另外,如果您的函数意味着要更改View上的任何内容,则应调用

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

应用更改。

还有一点,您应该注意的是,作用域是在页面加载后初始化的,因此,应始终在加载页面后从作用域外部调用方法。否则,您将根本无法解决问题。

更新:

使用最新版本的angular,您应该使用

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

是的,这实际上是一个不好的做法,但是有时您只需要快速而又肮脏地完成工作即可。


30
这似乎是一种代码气味,因为Angular的理念是不将DOM代码与Angular代码混合使用
JoeCool 2013年

8
因此,如果您不希望将DOM代码与Angular代码混合使用,如果您想触发某些JQuery动画来响应Angular控制器中的变量更改,那么您该怎么做呢?从控制器上做起来很琐碎,但我不知道如何干净地做
1

3
$apply在我将代码包装到函数中之前,我无法为我工作,例如..scope.$apply(function() { scope.get() });
surfitscrollit 2015年

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

2
此解决方案是否有可能在Angular 1.4.9中不再起作用?我无法访问scope()中的angular.element(...),因为它返回未定义的值,并且角度元素/对象的参数表示该函数scope位于-object 内__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
感谢罗杰,非常有用!
Eduardo

使用必须使用的旧版jQuery验证库。因此,它是:1:重写库,2:创建指令以包装库,3:2行代码在有效时调用有角度的提交...
Michael K

如果我不使用apply然后更新视图数据的功能将不会得到体现?
Monojit Sarkar

13

该解决方案 angular.element(document.getElementById('ID')).scope().get()在角度1.5.2中停止为我工作。Sombody在评论中提到这在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月)评论了一个小提琴。
杰西·奇斯霍尔姆

5

如果您的菜单没有任何关联的范围是正确的选择,那么可能值得考虑。它不是真正的角度方式。

但是,如果这是您需要的方法,则可以通过将功能添加到$ rootScope,然后在这些功能中使用$ broadcast发送事件来实现。然后,您的控制器使用$ on监听那些事件。

如果最终菜单没有作用域,要考虑的另一件事是,如果您有多个路由,则所有控制器都必须具有自己的upate并获取功能。(这是假设您有多个控制器)


您能否举一个简单的示例说明如何从ControllerTwo调用ControllerOne的.get()函数?我的逻辑是,每个控制器将具有自己的.get().update()函数。我将有MainMenuController,需要从中执行(根据菜单项)必要控制器的.get()。
帕维尔·兹达洛夫

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元素(仅在给定控制器实例中可用)的查询,将其修改为适合大多数情况,而不考虑框架(或不考虑框架)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.