2013-07-15 42 views
43

經過幾個小時的沮喪搜索,我覺得我需要在這裏提交我的問題。如果這個問題之前得到了答覆,我很抱歉,但是迄今爲止我的搜索都沒有幫助。所以這是我的問題:如何更改範圍之外的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函數修改人的年齡?

回答

71

⚠️警告:這個答案是舊的,沒有反映最佳實踐,並且可能與新版本的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; 
    }); 
} 

試試吧in this Fiddle

Shaunjust pointed out Angular將在$digest()調用期間處理任何「手錶」或「綁定」。如果您直接修改$scope的屬性,則更改可能不會立即反映出來,並且可能會出現錯誤。

要觸發此操作,您可以調用$scope.$apply()這將檢查髒範圍並更新正確綁定的任何內容。傳遞一個可以在$scope.$apply中執行工作的函數將允許Angular捕獲任何異常。此行爲在the documentation for Scope中解釋。

+0

這完美的作品!先生非常感謝您。 每當我更改控制器外部的對象屬性時,我都必須這樣做。這不是很奇怪嗎? – gromit190

+0

@BirgerSkogengPedersen當您使用完全以Angular風格編寫的代碼(在這種情況下,這意味着在您的作用域中使用'ng-click'並指定一個方法)時,Angular可以非常好,簡潔,但是您是對的:當您需要在其他JavaScript和Angular代碼之間進行通信時,它可能非常冗長。恐怕我沒有足夠的經驗與Angular真正評論如何處理這個問題。 –

+0

啊,打我.scope()傑里米!至於你的問題,@BirgerSkogengPedersen,是的,這是對角度的主要投訴之一 - 一般的角度響應是,如果你使用外部代碼修改範圍,那麼你不遵守MVC,並且讓你的代碼更容易維護。我發現這在處理由項目強制執行的其他第三方庫時最常見,通常處理它們的最佳方法是將第三方功能包含在全局範圍內,或者包含在自定義指令的編譯和鏈接函數中爲了它。 – MaxPRafferty

5

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

你要設置你的NG-點擊屬性,在你的範圍的功能,如下所示:

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

感謝您的回覆,但我仍不明白按鈕如何改變人的年齡。即使如此,我將如何能夠改變管制員之外的人員年齡? 對不起,如果我原來的問題有點不清楚 – gromit190

+0

爲什麼你需要一個工廠,如果你可以直接賦值? – Alex78191

14

Jeremy's answer好纔是真的好,雖然現在角度發生了變化,不再工作,除非你加入這行代碼:

$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; 
    }); 
} 
+0

嗯,'$$ childHead'對我來說是'null' ... –

+0

爲您接受了接受的答案嗎? – Aleksandrus

+0

完美!我建議編輯其他答案 – eddi

0

使用$ render方法時,如果在指令外修改了值,則會由ng-model指令調用。通過閱讀$ viewValue屬性來獲得新值。看:https://jsfiddle.net/cesar_ade/g5ybs6ne/

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

使用傑里米銀行完美的答案,在一行中完成的,雖然我引用控制器VS應用程序:

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

只需用很短的$超時

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

你就完成了。

我試過所有這些$應用黑客從周圍的www以前,他們都沒有工作。但是這個$超時工作總是像一個魅力,在任何情況下。 所有你需要的是你的$範圍和$超時的參考。

Wy和它是如何工作:

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

它的工作原理基本上像一個隊列。但請注意,它是異步的。

+0

像魅力一樣工作。爲什麼?!它是如何工作的! :) –

+0

因爲$超時是在angulars摘要例程中處理的。零(0)時間的$超時與隊列完全相同。或者換句話說:用$ timeout(fn,0),你會說:「嘿角,請在你完成當前工作後儘快完成。」 – Steffomio

相關問題