Join the Stack Overflow Community
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm trying to see if there's a simple way to access the internal scope of a controller through an external javascript function (completely irrelevant to the target controller)

I've seen on a couple of other questions here that

angular.element("#scope").scope();

would retrieve the scope from a DOM element, but my attempts are currently yielding no proper results.

Here's the jsfiddle: http://jsfiddle.net/sXkjc/5/

I'm currently going through a transition from plain JS to Angular. The main reason I'm trying to achieve this is to keep my original library code intact as much as possible; saving the need for me to add each function to the controller.

Any ideas on how I could go about achieving this? Comments on the above fiddle are also welcome.

share|improve this question
    
FYI according to the docs using .scope() requires the Debug Data to be enable but using Debug Data in production is not recommended for speed reasons. The solutions below seem to revolve around scope() – rtpHarry Dec 5 '14 at 15:12
    
@rtpHarry is right. Answers below which requires usage of scope() are deprecated. See my answer here stackoverflow.com/a/34078750/319302 – Cagatay Kalan Mar 8 at 22:38

10 Answers 10

up vote 181 down vote accepted

You need to use $scope.$apply() if you want to make any changes to a scope value from outside the control of angularjs like a jquery/javascript event handler.

function change() {
    alert("a");
    var scope = angular.element($("#outer")).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}

Demo: Fiddle

share|improve this answer
    
Thanks for the answer. I've tried the Fiddle you've attached but I still get the same "You are great" string even after I click on "click me". I get the alert, just not the string change. Are you seeing changes? – dk123 Mar 15 '13 at 5:04
2  
@dk123 angular.element("#scope") is not working, though angular.element($("#scope")) is working, you need to have jquery also – Arun P Johny Mar 15 '13 at 5:11
1  
I know it been a while, but I hope some can answer me on this ... Why does var scope = angular.element($("#outer")).scope(); have to be declared inside the change function? If I move it to the global space it's a no go? – Marc M. Nov 6 '13 at 9:47
1  
@MarcM. I think it has to do with Angular's scope recreation. By the time you're using the change function, the previous scope the global var was pointing to may no longer exist (due to the recreation). – dk123 Dec 14 '13 at 3:43
1  
angular.element($("div[ng-controller='myCtrl']")).scope(); is better than additional #outer in div element, i guess – wyverny Mar 21 at 6:21

It's been a while since I posted this question, but considering the views this still seems to get, here's another solution I've come upon during these last few months:

$scope.safeApply = function( fn ) {
    var phase = this.$root.$$phase;
    if(phase == '$apply' || phase == '$digest') {
        if(fn) {
            fn();
        }
    } else {
        this.$apply(fn);
    }
};

The above code basically creates a function called safeApply that calles the $apply function (as stated in Arun's answer) if and only Angular currently isn't going through the $digest stage. On the other hand, if Angular is currently digesting things, it will just execute the function as it is, since that will be enough to signal to Angular to make the changes.

Numerous errors occur when trying to use the $apply function while AngularJs is currently in its $digest stage. The safeApply code above is a safe wrapper to prevent such errors.

(note: I personally like to chuck in safeApply as a function of $rootScope for convenience purposes)

Example:

function change() {
    alert("a");
    var scope = angular.element($("#outer")).scope();
    scope.safeApply(function(){
        scope.msg = 'Superhero';
    })
}

Demo: http://jsfiddle.net/sXkjc/227/

share|improve this answer
1  
Why does your safeApply function work? Seems like what you are saying is "execute the function by itself if Angular is in the $apply or $digest stages, otherwise use $apply() to apply the function".... But if you execute the function by itself.... how does that update any models? Seems like that would not be favorable behavior, unless there something going on I don't know about. Does some mechanism in Angular go an poll the $scope for changes that may have happened directly to it??? – trusktr Mar 26 '14 at 5:37
    
Plus, if you need to safeguard against those states, then I'd consider that a bug of the $apply() method that needs to be fixed. – trusktr Mar 26 '14 at 5:37
    
@trusktr From what I understand, executing the function normally is caught by angular if the function changes any models, and hence angular updates them in the next digest stage. – dk123 Mar 27 '14 at 0:54
    
@trusktr I'd agree though that if the normal $apply() can be applied without the safeguards, there would be nothing better. In essence, the only purpose of safeApply is to safeguard against the $apply() errors. Not sure though if this was a reported issue and now fixed, or still an ongoing one. – dk123 Mar 27 '14 at 0:56
1  
Just because I stumbled over it: github.com/angular/angular.js/wiki/When-to-use-$scope.$apply‌​(). _If you are doing if (!$scope.$$phase) $scope.$apply() it's because you are not high enough in the call stack._ – scheffield Sep 29 '14 at 14:44

we can call it after loaded

http://jsfiddle.net/gentletech/s3qtv/3/

<div id="wrap" ng-controller="Ctrl">
    {{message}}<br>
    {{info}}
    </div>
    <a  onClick="hi()">click me </a>

    function Ctrl($scope) {
        $scope.message = "hi robi";
        $scope.updateMessage = function(_s){
            $scope.message = _s;    
        };
    }

function hi(){
    var scope = angular.element(document.getElementById("wrap")).scope();
        scope.$apply(function() {
        scope.info = "nami";
        scope.updateMessage("i am new fans like nami");
    });
}
share|improve this answer

Another way to do that is:

var extScope;
var app = angular.module('myApp', []);
app.controller('myController',function($scope, $http){
    extScope = $scope;
})
//below you do what you want to do with $scope as extScope
extScope.$apply(function(){
    extScope.test = 'Hello world';
})
share|improve this answer
1  
After looking for ways to do this for almost 4 hours, finally a solution that solves my problem. You sir just saved my day! – ayushgp May 4 at 19:56
1  
I still don't understand why this comment isn't the best answer for this. After digging the internet for couple of days, with frustration and anger, finally this is what solved my issue. Thank you @Charleston. You are great, sir! – Rajkumar Aug 24 at 4:44

It's been a long time since I asked this question, but here's an answer that doesn't require jquery:

function change() {
    var scope = angular.element(document.querySelector('#outside')).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
share|improve this answer

Here's a reusable solution: http://jsfiddle.net/flobar/r28b0gmq/

function accessScope(node, func) {
    var scope = angular.element(document.querySelector(node)).scope();
    scope.$apply(func);
}

window.onload = function () {

    accessScope('#outer', function (scope) {
        // change any property inside the scope
        scope.name = 'John';
        scope.sname = 'Doe';
        scope.msg = 'Superhero';
    });

};
share|improve this answer

The accepted answer is great. I wanted to look at what happens to the Angular scope in the context of ng-repeat. The thing is, Angular will create a sub-scope for each repeated item. When calling into a method defined on the original $scope, that retains its original value (due to javascript closure). However, the this refers the calling scope/object. This works out well, so long as you're clear on when $scope and this are the same and when they are different. hth

Here is a fiddle that illustrates the difference: https://jsfiddle.net/creitzel/oxsxjcyc/

share|improve this answer
<input type="text" class="form-control timepicker2" ng-model='programRow.StationAuxiliaryTime.ST88' />

accessing scope value

assume that programRow.StationAuxiliaryTime is an array of object

 $('.timepicker2').on('click', function () 
    {
            var currentElement = $(this);

            var scopeValues = angular.element(currentElement).scope();
            var model = currentElement.attr('ng-model');
            var stationNumber = model.split('.')[2];
            var val = '';
            if (model.indexOf("StationWaterTime") > 0) {
                val = scopeValues.programRow.StationWaterTime[stationNumber];
            }
            else {
                val = scopeValues.programRow.StationAuxiliaryTime[stationNumber];
            }
            currentElement.timepicker('setTime', val);
        });
share|improve this answer

I'm newbie, so sorry if is a bad practice. Based on the chosen answer, I did this function:

function x_apply(selector, variable, value) {
    var scope = angular.element( $(selector) ).scope();
    scope.$apply(function(){
        scope[variable] = value;
    });
}

I'm using it this way:

x_apply('#fileuploader', 'thereisfiles', true);

By the way, sorry for my english

share|improve this answer

You can also try:

function change() {
    var scope = angular.element( document.getElementById('outer') ).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
share|improve this answer

protected by Pankaj Parkar Mar 9 at 19:36

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.