aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMisko Hevery2010-12-01 20:29:54 -0800
committerMisko Hevery2010-12-02 22:45:57 -0800
commit5a8ad8fe329fc09898ff43a060710265d38393be (patch)
tree95058036d40b1dd993e2a9c4094ebd34b2751707 /src
parent41d5938883a3d06ffe8a88a51efd8d1896f7d747 (diff)
downloadangular.js-5a8ad8fe329fc09898ff43a060710265d38393be.tar.bz2
Closes #170. Corrected the behavior of select when options are ng:repeated
- Delete $postEval method, as it was a hack
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js5
-rw-r--r--src/Compiler.js13
-rw-r--r--src/Scope.js27
-rw-r--r--src/directives.js21
-rw-r--r--src/validators.js2
-rw-r--r--src/widgets.js49
6 files changed, 72 insertions, 45 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 4d3c360f..97792868 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -53,6 +53,9 @@ function fromCharCode(code) { return String.fromCharCode(code); }
var _undefined = undefined,
_null = null,
$$element = '$element',
+ $$update = '$update',
+ $$scope = '$scope',
+ $$validate = '$validate',
$angular = 'angular',
$array = 'array',
$boolean = 'boolean',
@@ -69,6 +72,8 @@ var _undefined = undefined,
$number = 'number',
$object = 'object',
$string = 'string',
+ $value = 'value',
+ $selected = 'selected',
$undefined = 'undefined',
NG_EXCEPTION = 'ng-exception',
NG_VALIDATION_ERROR = 'ng-validation-error',
diff --git a/src/Compiler.js b/src/Compiler.js
index 10d19ea8..a98bd502 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -30,6 +30,7 @@ Template.prototype = {
if (this.newScope) {
childScope = createScope(scope);
scope.$onEval(childScope.$eval);
+ element.data($$scope, childScope);
}
foreach(this.inits, function(fn) {
queue.push(function() {
@@ -68,6 +69,17 @@ Template.prototype = {
}
};
+/*
+ * Function walks up the element chain looking for the scope associated with the give element.
+ */
+function retrieveScope(element) {
+ var scope;
+ while (element && !(scope = element.data($$scope))) {
+ element = element.parent();
+ }
+ return scope;
+}
+
///////////////////////////////////
//Compiler
//////////////////////////////////
@@ -97,6 +109,7 @@ Compiler.prototype = {
element = jqLite(element);
var scope = parentScope && parentScope.$eval ?
parentScope : createScope(parentScope);
+ element.data($$scope, scope);
return extend(scope, {
$element:element,
$init: function() {
diff --git a/src/Scope.js b/src/Scope.js
index 09779453..203507a3 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -243,7 +243,6 @@ function createScope(parent, providers, instanceCache) {
parent = Parent.prototype = (parent || {});
var instance = new Parent();
var evalLists = {sorted:[]};
- var postList = [], postHash = {}, postId = 0;
extend(instance, {
'this': instance,
@@ -371,11 +370,6 @@ function createScope(parent, providers, instanceCache) {
instance.$tryEval(queue[j].fn, queue[j].handler);
}
}
- while(postList.length) {
- fn = postList.shift();
- delete postHash[fn.$postEvalId];
- instance.$tryEval(fn);
- }
} else if (type === $function) {
return exp.call(instance);
} else if (type === 'string') {
@@ -552,27 +546,6 @@ function createScope(parent, providers, instanceCache) {
/**
* @workInProgress
* @ngdoc function
- * @name angular.scope.$postEval
- * @function
- */
- $postEval: function(expr) {
- if (expr) {
- var fn = expressionCompile(expr);
- var id = fn.$postEvalId;
- if (!id) {
- id = '$' + instance.$id + "_" + (postId++);
- fn.$postEvalId = id;
- }
- if (!postHash[id]) {
- postList.push(postHash[id] = fn);
- }
- }
- },
-
-
- /**
- * @workInProgress
- * @ngdoc function
* @name angular.scope.$become
* @function
* @deprecated This method will be removed before 1.0
diff --git a/src/directives.js b/src/directives.js
index 8a76f17a..d40d6120 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -304,7 +304,8 @@ angularDirective("ng:bind-template", function(expression, element){
var REMOVE_ATTRIBUTES = {
'disabled':'disabled',
'readonly':'readOnly',
- 'checked':'checked'
+ 'checked':'checked',
+ 'selected':'selected'
};
/**
* @workInProgress
@@ -359,27 +360,31 @@ var REMOVE_ATTRIBUTES = {
angularDirective("ng:bind-attr", function(expression){
return function(element){
var lastValue = {};
- var updateFn = element.parent().data('$update');
+ var updateFn = element.data($$update) || noop;
this.$onEval(function(){
- var values = this.$eval(expression);
+ var values = this.$eval(expression),
+ dirty = noop;
for(var key in values) {
var value = compileBindTemplate(values[key]).call(this, element),
specialName = REMOVE_ATTRIBUTES[lowercase(key)];
if (lastValue[key] !== value) {
lastValue[key] = value;
if (specialName) {
- if (element[specialName] = toBoolean(value)) {
- element.attr(specialName, value);
+ if (toBoolean(value)) {
+ element.attr(specialName, specialName);
+ element.attr('ng-' + specialName, value);
} else {
- element.removeAttr(key);
+ element.removeAttr(specialName);
+ element.removeAttr('ng-' + specialName);
}
- (element.data('$validate')||noop)();
+ (element.data($$validate)||noop)();
} else {
element.attr(key, value);
}
- this.$postEval(updateFn);
+ dirty = updateFn;
}
}
+ dirty();
}, element);
};
});
diff --git a/src/validators.js b/src/validators.js
index ea35558e..7318333a 100644
--- a/src/validators.js
+++ b/src/validators.js
@@ -394,7 +394,7 @@ extend(angularValidator, {
element.removeClass('ng-input-indicator-wait');
scope.$invalidWidgets.markValid(element);
}
- element.data('$validate')();
+ element.data($$validate)();
scope.$root.$eval();
});
} else if (inputState.inFlight) {
diff --git a/src/widgets.js b/src/widgets.js
index f34ff05c..04353bd5 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -282,7 +282,7 @@ function valueAccessor(scope, element) {
required = requiredExpr === '';
}
- element.data('$validate', validate);
+ element.data($$validate, validate);
return {
get: function(){
if (lastError)
@@ -391,6 +391,7 @@ var textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, initW
// 'file': fileWidget???
};
+
function initWidgetValue(initValue) {
return function (model, view) {
var value = view.get();
@@ -461,18 +462,13 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {
this.$eval(element.attr('ng:init')||'');
// Don't register a handler if we are a button (noopAccessor) and there is no action
if (action || modelAccessor !== noopAccessor) {
- element.bind(events, function(event){
+ element.bind(events, function (){
model.set(view.get());
lastValue = model.get();
scope.$tryEval(action, element);
scope.$root.$eval();
});
}
- function updateView(){
- view.set(lastValue = model.get());
- }
- updateView();
- element.data('$update', updateView);
scope.$watch(model.get, function(value){
if (lastValue !== value) {
view.set(lastValue = value);
@@ -494,15 +490,50 @@ angularWidget('select', function(element){
return inputWidgetSelector.call(this, element);
});
+
+/*
+ * Consider this:
+ * <select name="selection">
+ * <option ng:repeat="x in [1,2]">{{x}}</option>
+ * </select>
+ *
+ * The issue is that the select gets evaluated before option is unrolled.
+ * This means that the selection is undefined, but the browser
+ * default behavior is to show the top selection in the list.
+ * To fix that we register a $update function on the select element
+ * and the option creation then calls the $update function when it is
+ * unrolled. The $update function then calls this update function, which
+ * then tries to determine if the model is unassigned, and if so it tries to
+ * chose one of the options from the list.
+ */
angularWidget('option', function(){
this.descend(true);
this.directives(true);
return function(element) {
- this.$postEval(element.parent().data('$update'));
+ var select = element.parent();
+ var scope = retrieveScope(select);
+ var model = modelFormattedAccessor(scope, select);
+ var view = valueAccessor(scope, select);
+ var option = element;
+ var lastValue = option.attr($value);
+ var lastSelected = option.attr('ng-' + $selected);
+ element.data($$update, function(){
+ var value = option.attr($value);
+ var selected = option.attr('ng-' + $selected);
+ var modelValue = model.get();
+ if (lastSelected != selected || lastValue != value) {
+ lastSelected = selected;
+ lastValue = value;
+ if (selected || modelValue == _null || modelValue == _undefined)
+ model.set(value);
+ if (value == modelValue) {
+ view.set(lastValue);
+ }
+ }
+ });
};
});
-
/**
* @workInProgress
* @ngdoc widget