web-dev-qa-db-ja.com

範囲外のAngularJSデータを変更するにはどうすればよいですか?

何時間もイライラする検索の後、ここに質問を送信する必要があると感じています。この質問が以前に何らかの形で答えられた場合、私は事前に謝罪しますが、私の検索のどれも今のところ助けていないだからここに私の質問があります:

私のJavaScriptコードは、AngularJSによって変更および監視されるオブジェクトを作成しています。一部のイベント(オブジェクトの以前の設定の読み込みなど)で、このオブジェクトのプロパティをスコープ外から変更したい。問題は、入力が変わらないことです...

これらの変更を実行する方法の例を次に示します。


HTMLコード:

<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コード:

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

「20に変更」ボタンを押しても何も起こりません。 change関数から個人の年齢を変更するにはどうすればよいですか?

46
gromit190

⚠️警告:この回答は古く、ベストプラクティスを反映しておらず、Angularの新しいバージョンと互換性がない場合があります。

MaxPRaffertyの答えは正しいです-スコープ内で関数を使用することが、これを行うためのより良い方法であることがよくあります-しかし、別のオプションがあります。 angular.element(...).scope()メソッドを使用して、無関係なJavaScriptからAngularスコープにアクセスします。ng-app属性が指定されている要素をターゲットにして、アプリのトップレベルスコープを選択します。あなたのクリックハンドラのようなもので:

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

それを試してみてください このフィドルで

ショーン指摘したとおり Angularは、$digest()呼び出し中に「ウォッチ」または「バインディング」のみを処理します。$scopeのプロパティを直接変更しただけでは、変更がすぐに反映されず、バグが発生する場合があります。

これをトリガーするには、$scope.$apply()を呼び出して、ダーティスコープをチェックし、正しくバインドされているものをすべて更新します。 $scope.$apply内で作業を行う関数を渡すと、Angularも例外をキャッチできます。この動作は Scopeのドキュメント で説明されています。

71
Jeremy Banks

ジェレミーの答え は本当に良いですが、今ではAngularが変更され、このコード行を追加しない限り機能しなくなります:

$scope = $scope.$$childHead;

したがって、変更された関数は次のようになります。

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

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

次のように、ng-click属性をスコープ内の関数に設定します。

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;
    }
}
5
MaxPRafferty

短い$ timeoutを使用するだけです

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

これで完了です。

以前、wwwからの$ applyハックをすべて試しましたが、すべてうまくいきませんでした。ただし、この$ timeoutは常にチャームのように機能します。必要なのは、$ scopeと$ timeoutの参照だけです。

理由と仕組み:

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

基本的にキューのように機能します。ただし、非同期であることに注意してください。

3
Steffomio

Jeremy Banksの完璧な答えを使用して、1行で行いますが、コントローラーとアプリを参照しています。

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

値がディレクティブ外で変更された場合、$ renderメソッドはng-modelディレクティブによって呼び出されます。 $ viewValueプロパティを読み取って新しい値を取得します。見て: https://jsfiddle.net/cesar_ade/g5ybs6ne/

ctrl.$render=function(){
    setSelected(ctrl.$viewValue || 'Not Sure');
}
0
César Alcívar