简体   繁体   中英

Call angular controller's method from outside

Here's the code: http://jsbin.com/rucatemujape/1/edit?html,js,console,output

My question is how do I manually call method changeUser from JavaScript so the output HTML changes?

I can do this by executing (relies on jQuery)

angular.element('body').scope().changeUser({fullName: 'John Doe'});
angular.element('body').scope().$apply()

But I want to know is there any better way?

For example, in knockout.js I can execute viewModel.someFunction() any time and knockout correctly handles this.

Why do I want to do this: because I want be able to change model from browser's console when debugging a code.

Edit: Another reason why I need this it's getting information from Restful Services and updating a model. Yes I can trigger that event by clicking a button with "ng-click" attribute but how to deal with events which are not initiated by user? For example, repeating ations from setInterval or something

 var myApp = angular.module('myApp', []); myApp.controller('MyController', function($scope, $timeout) { $scope.user = {}; $scope.count = 0; $scope.changeUser = function(user) { $scope.user = "MyName"; $scope.count++; // call function after 1 sec. $timeout($scope.changeUser, 1000); }; // initiate function $scope.changeUser(); }); 
 <!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="utf-8"> <title>JS Bin</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script> </head> <body ng-controller="MyController" <span>Hello, {{user}}</span> <span>count, {{count}}</span> </body> </html> 

Use ng-click="changeUser()" to call the fucntion

http://jsbin.com/rucatemujape/2/edit

Controllers do not really expose "APIs" outside their own 'scope' (and below). So either you do the Ajax call within the controller or you expose the "user data" to display.

For example: Kostia Mololkin's answer's (in main comments) uses a global variable to share the data.

Angular Way

Here I present a more "angular way": the user is being handled by a "Service". Not clear which direction you wanted exactly. Anyway, this cannot hurt.

Angular works best when you use "data" to "communicate state" instead of "events". So the idea again is to arrange things so your data instance (ie "user") is the same between "{{user.fullname}}" and where the Ajax call is taking place. There are many ways to skin a cat and it highly depends on the needs of your application. For example, if you build a singleton service for the ajax call, you can also have the data own by it AND exposed to the controller in some form or another (again, many ways of doing this).

NOTE: If you use angular's system to perform ajax calls (ie $http or $resource for instance) then you should never need to manually call "$apply()". Btw, you should "wrap" calls with $apply() instead of calling it "afterwards" reason being to properly handle "throws".

Plunker example with Ajax/Rest call:

http://plnkr.co/edit/SCF2XZCK5KQWkb4hZfOO?p=preview

var myApp = angular.module('myApp', []);

myApp.factory('UserService', ['$http',function($http){

    // Extra parent object to keep as a shared object to 'simplify'
    // updating a child object
    var userData = {};

    userData.user = { fullName:'none' };

    function loadUserData(userid) {
      $http.get('restapi_getuserdata_'+userid+'.json').
        success(function(data, status, headers, config) {
          // update model
          userData.user = data;
        }).
        error(function(data, status, headers, config) {
          console.log("error: ", status);
        });
    }

    return {
      userData: userData,
      loadUserData: loadUserData
    };

}]);

myApp.controller('MyController', ['$scope', 'UserService', '$timeout', 
  function($scope, UserService, $timeout) {

    // shared object from the Service stored in the scope
    // there are many other ways, like using an accessor method that is 
    // "called" within the HTML  
    $scope.userData = UserService.userData;

}]);


myApp.controller('SomeOtherController', ['UserService', '$timeout', 
  function(UserService, $timeout) {

    // $timeout is only to simulate a transition within an app 
    // without relying on a "button".
    $timeout(function(){

      UserService.loadUserData(55);

    }, 1500);

}]);

HTML:

<html ng-app="myApp">
...
<body ng-controller="MyController">

  <span>Hello, {{userData.user.fullName}}</span>

  <!-- simulating another active piece of code within the App -->
  <div ng-controller="SomeOtherController"></div>

...

A variant using a getter method instead of data:

http://plnkr.co/edit/0Y8gJolCAFYNBTGkbE5e?p=preview

var myApp = angular.module('myApp', []);

myApp.factory('UserService', ['$http',function($http){

    var user;

    function loadUserData(userid) {
      $http.get('restapi_getuserdata_'+userid+'.json').
        success(function(data, status, headers, config) {
          console.log("loaded: ", data);
          // update model
          user = data;
        }).
        error(function(data, status, headers, config) {
          console.log("error: ", status);
          user = undefined;
        });
    }

    return {
      getCurrentUser: function() { 
        return user || { fullName:"<none>" };
      },
      userLoggedIn: function() { return !!user; },
      loadUserData: loadUserData
    };

}]);

myApp.controller('MyController', ['$scope', 'UserService', '$timeout', 
  function($scope, UserService, $timeout) {

    // getter method shared
    $scope.getCurrentUser = UserService.getCurrentUser;
}]);


myApp.controller('SomeOtherController', ['UserService', '$timeout', 
  function(UserService, $timeout) {

    // $timeout is only to simulate a transition within an app 
    // without relying on a "button".
    $timeout(function(){

      UserService.loadUserData(55);

    }, 1500);

}]);

HTML:

<html ng-app="myApp">
...
<body ng-controller="MyController">

  <span>Hello, {{ getCurrentUser().fullName }}</span>

  <!-- simulating another active piece of code within the App -->
  <div ng-controller="SomeOtherController"></div>

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM