How to change AngularJS data outside the scope?

After hours of frustrating searches I feel I need to submit my question here. I apologize in advance if this question is somehow answered before but none of my searches has helped so far. So here's my question:

My JavaScript code is creating an object, which is modified and monitored by AngularJS. On some events (like loading a previous setting of the object), I wish to change the properties of this object from outside the scope. The problem is that the inputs does not change...

Here's an example of how I wish to perform these changes:


HTML code:

<div ng-app="myApp" ng-controller="FirstCtrl">
<input type="number" ng-model="data.age">
<h1>{{data.age}}</h1>

<input type="button" value="Change to 20" ng-model="data" onclick="change()">

JavaScript Code:

var person = {
    age: 16
};

// Create module
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
    return person;
});

function FirstCtrl($scope, Data) {
    $scope.data = Data;
}

function change() {
    person.age = 20;
}

When I now press the "Change to 20" button, nothing happens. How can I modify the person's age from the change function?

Answers:

Answer

?? Warning: This answer is old, does not reflect best practices, and may not be compatible with newer versions of Angular.

MaxPRafferty's answer is correct - using a function in the scope is often the nicer way to do this - but there is another option. You can use the angular.element(...).scope() method to access an Angular scope from unrelated JavaScript. Select the top-level scope for the app by targeting the element that has the ng-app attribute specified, with something like in your click handler:

function change() {
    var appElement = document.querySelector('[ng-app=myApp]');
    var $scope = angular.element(appElement).scope();
    $scope.$apply(function() {
        $scope.data.age = 20;
    });
}

Try it out in this Fiddle.

Shaun just pointed out that Angular will only process any "watches" or "bindings" during a $digest() call. If you just modify the properties of the $scope directly, the changes may not be reflected immediately and you may gets bugs.

To trigger this you can call $scope.$apply() which will check for dirty scopes and update anything bound correctly. Passing a function that does the work inside $scope.$apply will allow Angular to catch any exceptions as well. This behaviour is explained in the documentation for Scope.

Answer

Jeremy's answer is really good, though now Angular has changed, and will no longer work, unless you add this line of code:

$scope = $scope.$$childHead;

So, the changed function should look like this

function change() {
    var appElement = document.querySelector('[ng-app=myApp]');
    var $scope = angular.element(appElement).scope();
    $scope = $scope.$$childHead; // add this and it will work
    $scope.$apply(function() {
        $scope.data.age = 20;
    });
}
Answer

http://jsfiddle.net/MaxPRafferty/GS6Qk/

You want set your ng-click attribute to a function in your scope, as follows:

var person = {
    age: 16
};

// Create module
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
    return person;
});

function FirstCtrl($scope, Data) {
    $scope.data = Data;
    $scope.update = function(){
        $scope.data.age = 20;
    }
}
Answer

Using Jeremy Banks' perfect answer, done in one line, though I'm referencing the controller vs the app:

angular.element('[ng-controller=myController]').scope().$apply(function(x){ x.foo = "bar"; });
Answer

Simply use a short $timeout

var whatever = 'xyz';
$timeout(function(){
    $scope.yourModel.yourValue = whatever;
}, 0);

and you are done.

I tried all those $apply hacks from around the www before and they all didn't work. But this $timeout works always like a charme and in every case. All you need is a reference of your $scope and $timeout.

Wy and how it works:

// Imagine an angular buildin-function
angular.$queue = function(fn){
    $timeout(fn, 0);
}

It works basically like a queue. But be aware that it is async.

Answer

With $render method is called by the ng-model directive when the value has been modified outside the directive. Get new value by reading the $viewValue property. Look: https://jsfiddle.net/cesar_ade/g5ybs6ne/

ctrl.$render=function(){
    setSelected(ctrl.$viewValue || 'Not Sure');
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.