$(scope).val()scope是什么意思思

angularjs - ng-model is not updating when using jquery element.val() - Stack Overflow
to customize your list.
Join the Stack Overflow Community
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.
J it only takes a minute:
I'm using some version of jquery autocomplete as an angularjs direcitve.
When the jquery updates the input's value using element.val() angular does no notice the change until after the next digest ( i suppose ).
My first thought was to perform the action on the ng-model post digest using $timeout but as you can see it didn't help.
My second approach was to override the element's val function to trigger an input event but I haven`t managed to make it work.
Try to select a value from the autocomplete list and you'll see that the ng-model above is not updating.
Thanks for the response. I didn't know about the onSelect option.
This is the code based on your recommendations
// clone user provided options
scope.options = _.extend({}, scope.AutoCompleteOptions());
// Wrap onSelect - Force update before manipulation on ng-model
var fn = _.has(scope.AutoCompleteOptions(), 'onSelect')
? scope.AutoCompleteOptions().onSelect : _.
scope.options.onSelect = function () {
ngModelCtrl.$setViewValue(element.val());
scope.$apply(fn);
scope.autocomplete = $(element).autocomplete(scope.options);
This way i maintain the interface of the directive while guarantying that ng-model will be up to date.
4,53842051
As you already knew, the problem is angular doesn't aware of the update made by jquery plugin. Luckily, you can use the plugin's onSelect callback to update ngModel, like this:
.directive("autoComplete", function() {
restrict: "A" ,
require: 'ngModel', // require ngModel controller
AutoCompleteOptions : "=autoCompleteOptions", // use '=' instead of '&'
link: function (scope, element, attrs, ngModelCtrl) {
// prevent html5/browser auto completion
attrs.$set('autocomplete','off');
// add onSelect callback to update ngModel
scope.AutoCompleteOptions.onSelect = function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(element.val());
scope.autocomplete = $(element).autocomplete(scope.AutoCompleteOptions);
The plugin you're using has an onSelect callback, so you can simply modify your autocomplete parameters to include a callback which updates the scope
: $scope.current,
tabDisabled : true,
onSelect: function(v, data) {
$scope.selected_tags = v.
$scope.$apply();
To further improve this you can remove the need to use the model name in the callback:
function(v, data) {
var model = $(this).attr("ng-model");
$scope[model] = v.
$scope.$apply();
Your jQuery plugin is updating the view value of the input, but not the model.
You will have to modify the plugin to have access to your model or scope or you will have to bind an event to update the model from the view value manually. Maybe something like this:
$scope.format_tags = function () {
$scope.selected_tags = $('#myInput').val();
$scope.selected_final = _.omit($scope.selected_tags.split(','),_.isEmpty);
You could bind an event to the click of the actual suggested tag element for a better effect.
I don't recommend updating the plugin itself
you can change your directive instead to something like this..
app.directive("autoComplete", function() {
restrict : "A" ,
: '^ngModel',
ngModel: '=',
AutoCompleteOptions : "&autoCompleteOptions"
: function (scope, element,
// prevent html5/browser auto
attrs.$set('autocomplete','off');
var options = scope.
options.onSelect = function(elem) {
scope.$apply(function() {
scope.ngModel = elem.
scope.autocomplete = $(element).autocomplete(scope.AutoCompleteOptions());
As you've noticed in the scope object initialization of the directive I have selected the ngModel attribute as something to be used by the onSelect event of the AutoComplete Jquery Plugin, where I have updated the model using the
method. Use scope.$apply in a directive so that the scope.$digest() will be invoked and have the model affected directly.
14.7k32540
Your Answer
Sign up or
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Post as a guest
By posting your answer, you agree to the
Not the answer you're looking for?
Browse other questions tagged
Stack Overflow works best with JavaScript enabledangular源码分析:angular中$rootscope的实现——scope的一生 - 推酷
angular源码分析:angular中$rootscope的实现——scope的一生
在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope。scope是一个概念,是一个类,而$rootscope和被注入到controller中的一个具体的$scope都是一个个具体的对象。$rootscope之所以被称为&root&的原因就是他是所有scope的祖先,$rootscope是在angular启动流程中建立的(上上期讲过),而被注入到controller中的$scope则是在视图创建的时候通过父辈的$scope.$new制造出来的,在视图销毁的时候,$scope会被跟着销毁。$scope是链接视图和controller的重要手段,controller则是可以与服务进行链接,将服务提供的功能进行组合,然后丢给$scope,$scope则将这些传递给视图,让它显示给用户。如果将一个$scope比作一个人的话,那么他的功能有:生育功能($new)、进食功能($watch)、消化功能($digest)、执行功能($apply)、交流功能($on、$emit、$broadcast)、死亡功能($destory)。
一、在讲$rootscope前,先明确两个概念:工厂函数和构造函数。
1.工厂函数,能够根据传入的参数不同返回同一种性质的对象的函数.
这是一个设计模式级别的概念,不管是面向对象的编程语言还是非面向对象的编程语言,都可以使用这个概念,所谓的工厂模式.感兴趣的同学可以自己查阅资料.下面用js举个例:
function bird_fatory(bird_name,bird_color,bird_fly_way){
var bird = {};
bird.name = bird_
bird.color = bird_
bird.fly_way = bird_fly_
对应到我们将的angular中,factory(name, factoryFn)函数的第二参数要求传入的就是一个工厂函数,这个工厂返回的就是我们需要的服务.
2.构造函数,能够通过new运算处理后返回一个对象
这是一个语法级别的概念,需要面向对象的语法特性来支持.就是说,如果我们有一个构造函数Bird,那么就可以通过
new Bird()
来产出一个Bird类型的对象.
var bird = new Bird()
中bird是一个Bird类型的对象,这意味着可以通过
instanceof的函数来判断bird是不是Bird类型:
bird instanceof Bird`.
function Bird(bird_name,bird_color,bird_fly_way){
this.name = bird_
this.color = bird_
this.fly_way = bird_fly_
//无须返回值
var bird = new Bird('dapeng','white','扶摇直上九万里');
对应到angular中,provider(name, provider_)函数中的第二个参数就要求传入一个构造函数,并且这个构造函数需要构造一个$get的属性.
二、$rootscope服务的提供者
$rootscopeProvider
下面给出$rootscopeProvider的代码梗概:
function $RootScopeProvider() {
var TTL = 10;//TTL是 Time To Live的缩写,这里是借用的网络中的一个术语.在我看来,这个是用来控制消耗不良的.
var $rootScopeMinErr = minErr('$rootScope');
var lastDirtyWatch =
var applyAsyncId =
this.digestTtl = function(value) {//设置TTL
if (arguments.length) {
return TTL;
this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
function($injector, $exceptionHandler, $parse, $browser) {
function Scope() {//scope的构造函数
this.$id = nextUid();
this.$$phase = this.$parent = this.$$watchers =
this.$$nextSibling = this.$$prevSibling =
this.$$childHead = this.$$childTail =
this.$root =
this.$$destroyed =
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$$isolateBindings =
Scope.prototype = {//scope的原型,相当于他的基因图谱了吧.
constructor: Scope
$new: function(isolate, parent) {...},//一个工厂函数,用于生产一个$scope
//下面三个函数与watch有关
$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {...},
$watchGroup: function(watchExpressions, listener) {...},
$watchCollection: function(obj, listener) {...},
$digest: function() {...},//消化处理
$destroy: function() {...},//销毁自己的函数,垃圾回收
$eval: function(expr, locals) {
return $parse(expr)(this, locals);
$evalAsync: function(expr, locals) {...},
//注册消化完后要做的事
$$postDigest: function(fn) {
postDigestQueue.push(fn);
$apply: function(expr) {...},
$applyAsync: function(expr) {...},
//下面三个函数与事件相关
$on: function(name, listener) {...},
$emit: function(name, args) {...},
$broadcast: function(name, args) {...}
var $rootScope = new Scope();
return $rootS
三、$scope的一生
$scope是一个对象,那么就是一个变量,变量都是有生命周期的.从生到死,需要的时候被创造出来,不需要的时候被消灭.但失去存在意思的时候还不离开,就会挤占其他生命的空间,表现在系统层面上就是内存泄漏.
1.$scope的出生,$new
第一代的Scope是天生的,这个天就是$rootscopeProvider,其他的scope都是$scope.$new生出来的.来看看她的代码吧:
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail =
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();//生成一个唯一的id
this.$$ChildScope =
ChildScope.prototype =//将原型设为他妈
return ChildS
function(isolate, parent) {
parent = parent ||
if (isolate) { //孤立作用域时
child = new Scope();//利用构造函数构造一个scope
child.$root = this.$//和老祖宗取得联系
// Only create a child scope class if somebody asks for one,
// but cache it to allow the VM to optimize lookups.
if (!this.$$ChildScope) {
this.$$ChildScope = createChildScopeClass(this);//上面定义了这个函数
child = new this.$$ChildScope();//生产一个孩子
child.$parent =//和他妈建立联系
//孩子多了,要排个大小先后
child.$$prevSibling = parent.$$childT
if (parent.$$childHead) {
parent.$$childTail.$$nextSibling =
parent.$$childTail =
parent.$$childHead = parent.$$childTail =
// When the new scope is not isolated or we inherit from `this`, and
// the parent scope is destroyed, the property `$$destroyed` is inherited
// prototypically. In all other cases, this property needs to be set
// when the parent scope is destroyed.
// The listener needs to be added after the parent is set
if (isolate || parent != this) child.$on('$destroy', destroyChildScope);//监听到消耗事件,销毁自己一家子
``函数需要传入两个参数,第一个参数表示该scope是否是孤立的(就是能不能访问他妈妈的属性),第二参数就是孩子她妈是谁.
2.我将$scope.$watch理解为吃东西.说是看看,其实是吃到肚子里去了.
$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
var get = $parse(watchExp);//$parse将一个表达式转换为一个函数
if (get.$$watchDelegate) {//这里提供了一个代理功能,可以思考下怎么用哦
return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
var scope = this,
array = scope.$$watchers,//用于存储watcher对象
watcher = { //watcher对象
fn: listener,
last: initWatchVal,
exp: prettyPrintExpression || watchExp,
eq: !!objectEquality
lastDirtyWatch =
if (!isFunction(listener)) {
watcher.fn =
if (!array) {
array = scope.$$watchers = [];
// we use unshift since we use a while loop in $digest for speed.
// the while loop reads in reverse order.
array.unshift(watcher);
incrementWatchersCount(this, 1);
return function deregisterWatch() {//返回的是一个函数,这个函数可以用于销毁注册的watch
if (arrayRemove(array, watcher) &= 0) {
incrementWatchersCount(scope, -1);
lastDirtyWatch =
这个函数的功能是注册一个监听,监听的内容是一个表达式,监听者是一个函数.函数通过将表达式和监听者组装成一个wacher,放到一个数组
scope.$$watchers
中.换一个方式,可以理解成他在吃东西,要吃的东西是叫监听对(监听表达式和监听者),吃到胃里.这个胃就是scope.$$watchers.
另外还有两个函数:$watchGroup,用于监听一个表达式数组;$watchCollection,用于监听scope上所有属性的变化.他们最终都会往scope.$$watchers上加入元素.
3.吃了,总得消化:$digest,以及消化完后做的啥$$postDigest
3.1$$postDigest:用于注册在消化后要执行的函数.
3.2消化的源代码:
$digest: function() {
var watch, value, last,
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
logIdx, logMsg, asyncT
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
$browser.$$checkUrlChange();//检测url地址是否发生变化
if (this === $rootScope && applyAsyncId !== null) {
// If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
// cancel the scheduled $apply and flush the queue of expressions to be evaluated.
$browser.defer.cancel(applyAsyncId);
flushApplyAsync();
lastDirtyWatch =
do { // &while dirty& loop
//一轮消化的过程就是将asyncQueue队列中的函数执行完 和将吃到肚子的监听任务依次检查一篇.
while (asyncQueue.length) {//执行异步队列中的函数
asyncTask = asyncQueue.shift();
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
} catch (e) {
$exceptionHandler(e);
lastDirtyWatch =
traverseScopesLoop:
do { // &traverse the scopes& loop
//检查监听,如果满足监听的条件,执行监听者函数
if ((watchers = current.$$watchers)) {
// process our watches
length = watchers.
while (length--) {
watch = watchers[length];
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if (watch) {
if ((value = watch.get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value === 'number' && typeof last === 'number'
&& isNaN(value) && isNaN(last)))) {
lastDirtyWatch =
watch.last = watch.eq ? copy(value, null) :
watch.fn(value, ((last === initWatchVal) ? value : last), current);
if (ttl & 5) {
logIdx = 4 -
if (!watchLog[logIdx]) watchLog[logIdx] = [];
watchLog[logIdx].push({
msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
newVal: value,
oldVal: last
} else if (watch === lastDirtyWatch) {
// If the most recently dirty watcher is now clean, short circuit since the remaining watchers
// have already been tested.
break traverseScopesL
} catch (e) {
$exceptionHandler(e);
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $broadcast
if (!(next = ((current.$$watchersCount && current.$$childHead) ||
(current !== target && current.$$nextSibling)))) {
while (current !== target && !(next = current.$$nextSibling)) {
current = current.$
} while ((current = next));
// `break traverseScopesL` takes us to here
if ((dirty || asyncQueue.length) && !(ttl--)) {//控制消耗的次数,操作了一定限度,抛出异常,这个ttl可以在Provider中设置:digestTtl
clearPhase();
throw $rootScopeMinErr('infdig',
'{0} $digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: {1}',
TTL, watchLog);
} while (dirty || asyncQueue.length);
clearPhase();
while (postDigestQueue.length) {//执行消化完后的函数列表.
postDigestQueue.shift()();
} catch (e) {
$exceptionHandler(e);
消化的过程,实际是一个执行异步队列中的函数和检测监听的过程.但是在这个过程中,很可能在异步队列中产出新的函数,以及让监听满足条件(dirty),那么需要再次消化.但是消化不能没完没了,不然就会占用所用的执行时间,线程会被阻塞在这里(也就是消化不良勒).所以需要有一个最大次数,也就是TTL,这个TTL允许在angular的配置阶段进行修改.
3.3.消化是怎么开始的呢?
angular是事件驱动的,当试图上发生任何事件或者定时器事件到时执行时,消化过程都会被启动.
4.吃饱了还得干点其他事:$apply,$applyAsync,$eval,$evalAsync
4.1.$eval,马上执行某个表达式
4.2.$evalAsync,异步执行某个表达式.
$evalAsync: function(expr, locals) {
// if we are outside of an $digest loop and this is the first time we are scheduling async
// task also schedule async auto-flush
if (!$rootScope.$$phase && !asyncQueue.length) {
$browser.defer(function() {//异步唤起消化过程,关于$browser,后面会讲
if (asyncQueue.length) {
$rootScope.$digest();
asyncQueue.push({scope: this, expression: expr, locals: locals});//这个函数是通过向异步队列中添加对象来完成的.
4.3.$apply,先执行,后唤起消化过程.
$apply: function(expr) {
beginPhase('$apply');
return this.$eval(expr);
} finally {
clearPhase();
} catch (e) {
$exceptionHandler(e);
} finally {
//无论如何都尝试唤起消化过程
$rootScope.$digest();
} catch (e) {
$exceptionHandler(e);
4.4$applyAsync,唤起消化过程来完成 表达式的执行
$applyAsync: function(expr) {
var scope =
expr && applyAsyncQueue.push($applyAsyncExpression);
scheduleApplyAsync();
function $applyAsyncExpression() {
scope.$eval(expr);
5.$scope间的交流:$on,$emit,$broadcast
5.1.$on,注册事件监听.就是当由某事件发生(来自父辈或者子辈),就去执行某个函数.
$on: function(name, listener) {
var namedListeners = this.$$listeners[name];
if (!namedListeners) {
this.$$listeners[name] = namedListeners = [];
namedListeners.push(listener);
var current =
if (!current.$$listenerCount[name]) {
current.$$listenerCount[name] = 0;
current.$$listenerCount[name]++;
} while ((current = current.$parent));
var self =
return function() {
var indexOfListener = namedListeners.indexOf(listener);
if (indexOfListener !== -1) {
namedListeners[indexOfListener] =
decrementListenerCount(self, 1, name);
5.2.$emit,告诉父辈们,某件事发生了.事件会一直抵达$rootscope
$emit: function(name, args) {
var empty = [],
namedListeners,
scope = this,
stopPropagation = false,
name: name,
targetScope: scope,
stopPropagation: function() {stopPropagation =},
preventDefault: function() {
event.defaultPrevented =
defaultPrevented: false
listenerArgs = concat([event], arguments, 1),
namedListeners = scope.$$listeners[name] ||
event.currentScope =
for (i = 0, length = namedListeners. i & i++) {
// if listeners were deregistered, defragment the array
if (!namedListeners[i]) {
namedListeners.splice(i, 1);
//allow all listeners attached to the current scope to run
namedListeners[i].apply(null, listenerArgs);
} catch (e) {
$exceptionHandler(e);
//if any listener on the current scope stops propagation, prevent bubbling
if (stopPropagation) {
event.currentScope =
//traverse upwards
scope = scope.$
} while (scope);
event.currentScope =
5.3.$broadcast,告诉子辈们,某事发生了.事件会传递给所有子辈scope,所以叫广播.
$broadcast: function(name, args) {
var target = this,
current = target,
next = target,
name: name,
targetScope: target,
preventDefault: function() {
event.defaultPrevented =
defaultPrevented: false
if (!target.$$listenerCount[name])
var listenerArgs = concat([event], arguments, 1),
listeners, i,
//down while you can, then up and next sibling or up and next sibling until back at root
while ((current = next)) {
event.currentScope =
listeners = current.$$listeners[name] || [];
for (i = 0, length = listeners. i & i++) {
// if listeners were deregistered, defragment the array
if (!listeners[i]) {
listeners.splice(i, 1);
listeners[i].apply(null, listenerArgs);
} catch (e) {
$exceptionHandler(e);
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $digest
// (though it differs due to having the extra check for $$listenerCount)
if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
(current !== target && current.$$nextSibling)))) {
while (current !== target && !(next = current.$$nextSibling)) {
current = current.$
event.currentScope =
6.老而不死谓之贼:$destory
对于生死,前面说了.这里不再赘述.上代码:
$destroy: function() {
// We can't destroy a scope that has been already destroyed.
if (this.$$destroyed)
var parent = this.$
this.$broadcast('$destroy');//自己死的时候,会把这个消息传递给子代,子代也就死了.没办法,自己死后,没人管得了他们了.所以一起带走.
this.$$destroyed =
if (this === $rootScope) {
//Remove handlers attached to window when $rootScope is removed
$browser.$$applicationDestroyed();
incrementWatchersCount(this, -this.$$watchersCount);
for (var eventName in this.$$listenerCount) {
decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
// sever all the references to parent scopes (after this cleanup, the current scope should
// not be retained by any of our references and should be eligible for garbage collection)
if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextS
if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevS
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextS
if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevS
// Disable listeners, watchers and apply/digest methods
this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync =
this.$on = this.$watch = this.$watchGroup = function() { };
this.$$listeners = {};
// All of the code below is bogus code that works around V8's memory leak via optimized code
// and inline caches.
// - /p/v8/issues/detail?id=2073#c26
// - /angular/angular.js/issues/6794#issuecomment-
// - /angular/angular.js/issues/1313#issuecomment-
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
this.$$childTail = this.$root = this.$$watchers =
下期预告:angular源码分析:angular中脏活累活承担者之$parse
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致}

我要回帖

更多关于 java scope是什么意思 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信