2013-07-15 15 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

相关问题