mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-13 08:59:54 +08:00
Closes #170. Corrected the behavior of select when options are ng:repeated
- Delete $postEval method, as it was a hack
This commit is contained in:
28
regression/issue-170.html
Normal file
28
regression/issue-170.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery-1.4.2.js"></script>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<head>
|
||||
<body>
|
||||
<select name='selection0' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]'">{{value}}</option>
|
||||
</select>
|
||||
{{selection0}} <-- FOO should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection1='ignore'" name='selection1' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]' ng:bind-attr="{selected:'{{value==\'BAR\'}}'}">{{value}}</option>
|
||||
</select>
|
||||
{{selection1}} <-- BAR should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection2=1" name="selection2" style="display:block;">
|
||||
<option value="{{$index}}" ng:repeat="opt in ['zero', 'one']">{{opt}}</option>
|
||||
</select>
|
||||
{{selection2}} <-- 1 should be shown here
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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',
|
||||
|
||||
@@ -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() {
|
||||
|
||||
27
src/Scope.js
27
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') {
|
||||
@@ -549,27 +543,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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('Angular', function(){
|
||||
scope.$init();
|
||||
scope.$eval();
|
||||
expect(onUpdateView).wasCalled();
|
||||
dealoc(scope);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ BinderTest.prototype.setUp = function(){
|
||||
|
||||
this.compile = function(html, initialScope, parent) {
|
||||
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
|
||||
if (self.element) dealoc(self.element);
|
||||
var element = self.element = jqLite(html);
|
||||
var scope = compiler.compile(element)(element);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
describe('compiler', function(){
|
||||
var compiler, markup, directives, widgets, compile, log;
|
||||
var compiler, markup, directives, widgets, compile, log, scope;
|
||||
|
||||
beforeEach(function(){
|
||||
log = "";
|
||||
@@ -32,6 +32,10 @@ describe('compiler', function(){
|
||||
return scope;
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it('should recognize a directive', function(){
|
||||
var e = jqLite('<div directive="expr" ignore="me"></div>');
|
||||
@@ -44,7 +48,8 @@ describe('compiler', function(){
|
||||
};
|
||||
};
|
||||
var template = compiler.compile(e);
|
||||
var init = template(e).$init;
|
||||
scope = template(e);
|
||||
var init = scope.$init;
|
||||
expect(log).toEqual("found");
|
||||
init();
|
||||
expect(e.hasClass('ng-directive')).toEqual(true);
|
||||
@@ -52,12 +57,12 @@ describe('compiler', function(){
|
||||
});
|
||||
|
||||
it('should recurse to children', function(){
|
||||
var scope = compile('<div><span hello="misko"/></div>');
|
||||
scope = compile('<div><span hello="misko"/></div>');
|
||||
expect(log).toEqual("hello misko");
|
||||
});
|
||||
|
||||
it('should watch scope', function(){
|
||||
var scope = compile('<span watch="name"/>');
|
||||
scope = compile('<span watch="name"/>');
|
||||
expect(log).toEqual("");
|
||||
scope.$eval();
|
||||
scope.$set('name', 'misko');
|
||||
@@ -71,7 +76,7 @@ describe('compiler', function(){
|
||||
|
||||
it('should prevent descend', function(){
|
||||
directives.stop = function(){ this.descend(false); };
|
||||
var scope = compile('<span hello="misko" stop="true"><span hello="adam"/></span>');
|
||||
scope = compile('<span hello="misko" stop="true"><span hello="adam"/></span>');
|
||||
expect(log).toEqual("hello misko");
|
||||
});
|
||||
|
||||
@@ -87,7 +92,7 @@ describe('compiler', function(){
|
||||
});
|
||||
};
|
||||
};
|
||||
var scope = compile('before<span duplicate="expr">x</span>after');
|
||||
scope = compile('before<span duplicate="expr">x</span>after');
|
||||
expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment><span>x</span>after</div>');
|
||||
scope.$eval();
|
||||
expect(sortedHtml(scope.$element)).toEqual('<div>before<#comment></#comment><span>x</span><span>x</span>after</div>');
|
||||
@@ -103,7 +108,7 @@ describe('compiler', function(){
|
||||
textNode[0].nodeValue = 'replaced';
|
||||
}
|
||||
});
|
||||
var scope = compile('before<span>middle</span>after');
|
||||
scope = compile('before<span>middle</span>after');
|
||||
expect(sortedHtml(scope.$element[0], true)).toEqual('<div>before<span class="ng-directive" hello="middle">replaced</span>after</div>');
|
||||
expect(log).toEqual("hello middle");
|
||||
});
|
||||
@@ -116,7 +121,7 @@ describe('compiler', function(){
|
||||
log += 'init';
|
||||
};
|
||||
};
|
||||
var scope = compile('<ng:button>push me</ng:button>');
|
||||
scope = compile('<ng:button>push me</ng:button>');
|
||||
expect(lowercase(scope.$element[0].innerHTML)).toEqual('<div>button</div>');
|
||||
expect(log).toEqual('init');
|
||||
});
|
||||
@@ -135,7 +140,7 @@ describe('compiler', function(){
|
||||
if (text == '{{1+2}}')
|
||||
parent.text('3');
|
||||
});
|
||||
var scope = compile('<div><h1>ignore me</h1></div>');
|
||||
scope = compile('<div><h1>ignore me</h1></div>');
|
||||
expect(scope.$element.text()).toEqual('3');
|
||||
});
|
||||
|
||||
@@ -158,7 +163,7 @@ describe('compiler', function(){
|
||||
textNode.remove();
|
||||
}
|
||||
});
|
||||
var scope = compile('A---B---C===D');
|
||||
scope = compile('A---B---C===D');
|
||||
expect(sortedHtml(scope.$element)).toEqual('<div>A<hr></hr>B<hr></hr>C<p></p>D</div>');
|
||||
});
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ describe("resource", function() {
|
||||
var person = Person.get({id:123});
|
||||
$browser.xhr.flush();
|
||||
expect(person.name).toEqual('misko');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it('should return the same object when verifying the cache', function(){
|
||||
@@ -188,6 +189,7 @@ describe("resource", function() {
|
||||
$browser.xhr.flush();
|
||||
expect(person2Cache).toEqual(person2);
|
||||
expect(person2[0].name).toEqual('rob');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
describe('failure mode', function(){
|
||||
|
||||
@@ -1,52 +1,64 @@
|
||||
describe("ScenarioSpec: Compilation", function(){
|
||||
it("should compile dom node and return scope", function(){
|
||||
var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0];
|
||||
var scope = compile(node);
|
||||
scope.$init();
|
||||
expect(scope.a).toEqual(1);
|
||||
expect(scope.b).toEqual(2);
|
||||
var scope;
|
||||
|
||||
beforeEach(function(){
|
||||
scope = null;
|
||||
});
|
||||
|
||||
it("should compile jQuery node and return scope", function(){
|
||||
var scope = compile(jqLite('<div>{{a=123}}</div>')).$init();
|
||||
expect(jqLite(scope.$element).text()).toEqual('123');
|
||||
|
||||
afterEach(function(){
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it("should compile text node and return scope", function(){
|
||||
var scope = compile('<div>{{a=123}}</div>').$init();
|
||||
expect(jqLite(scope.$element).text()).toEqual('123');
|
||||
|
||||
describe('compilation', function(){
|
||||
it("should compile dom node and return scope", function(){
|
||||
var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0];
|
||||
scope = compile(node);
|
||||
scope.$init();
|
||||
expect(scope.a).toEqual(1);
|
||||
expect(scope.b).toEqual(2);
|
||||
});
|
||||
|
||||
it("should compile jQuery node and return scope", function(){
|
||||
scope = compile(jqLite('<div>{{a=123}}</div>')).$init();
|
||||
expect(jqLite(scope.$element).text()).toEqual('123');
|
||||
});
|
||||
|
||||
it("should compile text node and return scope", function(){
|
||||
scope = compile('<div>{{a=123}}</div>').$init();
|
||||
expect(jqLite(scope.$element).text()).toEqual('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ScenarioSpec: Scope", function(){
|
||||
it("should have set, get, eval, $init, updateView methods", function(){
|
||||
var scope = compile('<div>{{a}}</div>').$init();
|
||||
scope.$eval("$invalidWidgets.push({})");
|
||||
expect(scope.$set("a", 2)).toEqual(2);
|
||||
expect(scope.$get("a")).toEqual(2);
|
||||
expect(scope.$eval("a=3")).toEqual(3);
|
||||
scope.$eval();
|
||||
expect(jqLite(scope.$element).text()).toEqual('3');
|
||||
|
||||
describe('scope', function(){
|
||||
it("should have set, get, eval, $init, updateView methods", function(){
|
||||
scope = compile('<div>{{a}}</div>').$init();
|
||||
scope.$eval("$invalidWidgets.push({})");
|
||||
expect(scope.$set("a", 2)).toEqual(2);
|
||||
expect(scope.$get("a")).toEqual(2);
|
||||
expect(scope.$eval("a=3")).toEqual(3);
|
||||
scope.$eval();
|
||||
expect(jqLite(scope.$element).text()).toEqual('3');
|
||||
});
|
||||
|
||||
it("should have $ objects", function(){
|
||||
scope = compile('<div></div>', {$config: {a:"b"}});
|
||||
expect(scope.$get('$location')).toBeDefined();
|
||||
expect(scope.$get('$eval')).toBeDefined();
|
||||
expect(scope.$get('$config')).toBeDefined();
|
||||
expect(scope.$get('$config.a')).toEqual("b");
|
||||
});
|
||||
});
|
||||
|
||||
it("should have $ objects", function(){
|
||||
var scope = compile('<div></div>', {$config: {a:"b"}});
|
||||
expect(scope.$get('$location')).toBeDefined();
|
||||
expect(scope.$get('$eval')).toBeDefined();
|
||||
expect(scope.$get('$config')).toBeDefined();
|
||||
expect(scope.$get('$config.a')).toEqual("b");
|
||||
|
||||
describe("configuration", function(){
|
||||
it("should take location object", function(){
|
||||
var url = "http://server/#?book=moby";
|
||||
scope = compile("<div>{{$location}}</div>");
|
||||
var $location = scope.$location;
|
||||
var $browser = scope.$inject('$browser');
|
||||
expect($location.hashSearch.book).toBeUndefined();
|
||||
$browser.setUrl(url);
|
||||
$browser.poll();
|
||||
expect($location.hashSearch.book).toEqual('moby');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ScenarioSpec: configuration", function(){
|
||||
it("should take location object", function(){
|
||||
var url = "http://server/#?book=moby";
|
||||
var scope = compile("<div>{{$location}}</div>");
|
||||
var $location = scope.$location;
|
||||
var $browser = scope.$inject('$browser');
|
||||
expect($location.hashSearch.book).toBeUndefined();
|
||||
$browser.setUrl(url);
|
||||
$browser.poll();
|
||||
expect($location.hashSearch.book).toEqual('moby');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -209,28 +209,6 @@ describe('scope/model', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('$postEval', function(){
|
||||
it('should eval function once and last', function(){
|
||||
var log = '';
|
||||
var scope = createScope();
|
||||
function onceOnly(){log+= '@';}
|
||||
scope.$onEval(function(){log+= '.';});
|
||||
scope.$postEval(function(){log+= '!';});
|
||||
scope.$postEval(onceOnly);
|
||||
scope.$postEval(onceOnly);
|
||||
scope.$postEval(); // ignore
|
||||
scope.$eval();
|
||||
expect(log).toEqual('.!@');
|
||||
scope.$eval();
|
||||
expect(log).toEqual('.!@.');
|
||||
|
||||
scope.$postEval(onceOnly);
|
||||
scope.$postEval(onceOnly);
|
||||
scope.$eval();
|
||||
expect(log).toEqual('.!@..@');
|
||||
});
|
||||
});
|
||||
|
||||
describe('$new', function(){
|
||||
it('should $new should create new child scope and $become controller', function(){
|
||||
var parent = createScope(null, {exampleService: function(){return 'Example Service';}});
|
||||
|
||||
@@ -104,12 +104,6 @@ describe('Validator:asynchronous', function(){
|
||||
|
||||
afterEach(function(){
|
||||
if (self.$element) self.$element.remove();
|
||||
var oldCache = jqCache;
|
||||
jqCache = {};
|
||||
if (size(oldCache)) {
|
||||
dump(oldCache);
|
||||
}
|
||||
expect(size(oldCache)).toEqual(0);
|
||||
});
|
||||
|
||||
it('should make a request and show spinner', function(){
|
||||
|
||||
@@ -13,8 +13,7 @@ describe("directive", function(){
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (model && model.$element) model.$element.remove();
|
||||
expect(size(jqCache)).toEqual(0);
|
||||
dealoc(model);
|
||||
});
|
||||
|
||||
it("should ng:init", function() {
|
||||
|
||||
@@ -14,8 +14,7 @@ describe("markups", function(){
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
if (element) element.remove();
|
||||
expect(size(jqCache)).toEqual(0);
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
it('should translate {{}} in text', function(){
|
||||
@@ -63,92 +62,91 @@ describe("markups", function(){
|
||||
compile('<a ng:href="{{url}}" rel="{{rel}}"></a>');
|
||||
expect(sortedHtml(element)).toEqual('<a ng:bind-attr="{"href":"{{url}}","rel":"{{rel}}"}"></a>');
|
||||
});
|
||||
|
||||
it('should Parse Text With No Bindings', function(){
|
||||
var parts = parseBindings("a");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
});
|
||||
|
||||
it('should Parse Empty Text', function(){
|
||||
var parts = parseBindings("");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "");
|
||||
assertTrue(!binding(parts[0]));
|
||||
});
|
||||
|
||||
it('should Parse Inner Binding', function(){
|
||||
var parts = parseBindings("a{{b}}C");
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
assertEquals(parts[1], "{{b}}");
|
||||
assertEquals(binding(parts[1]), "b");
|
||||
assertEquals(parts[2], "C");
|
||||
assertTrue(!binding(parts[2]));
|
||||
});
|
||||
|
||||
it('should Parse Ending Binding', function(){
|
||||
var parts = parseBindings("a{{b}}");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
assertEquals(parts[1], "{{b}}");
|
||||
assertEquals(binding(parts[1]), "b");
|
||||
});
|
||||
|
||||
it('should Parse Begging Binding', function(){
|
||||
var parts = parseBindings("{{b}}c");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "c");
|
||||
assertTrue(!binding(parts[1]));
|
||||
});
|
||||
|
||||
it('should Parse Loan Binding', function(){
|
||||
var parts = parseBindings("{{b}}");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
});
|
||||
|
||||
it('should Parse Two Bindings', function(){
|
||||
var parts = parseBindings("{{b}}{{c}}");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "{{c}}");
|
||||
assertEquals(binding(parts[1]), "c");
|
||||
});
|
||||
|
||||
it('should Parse Two Bindings With Text In Middle', function(){
|
||||
var parts = parseBindings("{{b}}x{{c}}");
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "x");
|
||||
assertTrue(!binding(parts[1]));
|
||||
assertEquals(parts[2], "{{c}}");
|
||||
assertEquals(binding(parts[2]), "c");
|
||||
});
|
||||
|
||||
it('should Parse Multiline', function(){
|
||||
var parts = parseBindings('"X\nY{{A\nB}}C\nD"');
|
||||
assertTrue(!!binding('{{A\nB}}'));
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], '"X\nY');
|
||||
assertEquals(parts[1], '{{A\nB}}');
|
||||
assertEquals(parts[2], 'C\nD"');
|
||||
});
|
||||
|
||||
it('should Has Binding', function(){
|
||||
assertTrue(hasBindings(parseBindings("{{a}}")));
|
||||
assertTrue(!hasBindings(parseBindings("a")));
|
||||
assertTrue(hasBindings(parseBindings("{{b}}x{{c}}")));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
var BindingMarkupTest = TestCase("BindingMarkupTest");
|
||||
|
||||
BindingMarkupTest.prototype.testParseTextWithNoBindings = function(){
|
||||
var parts = parseBindings("a");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseEmptyText = function(){
|
||||
var parts = parseBindings("");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "");
|
||||
assertTrue(!binding(parts[0]));
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseInnerBinding = function(){
|
||||
var parts = parseBindings("a{{b}}c");
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
assertEquals(parts[1], "{{b}}");
|
||||
assertEquals(binding(parts[1]), "b");
|
||||
assertEquals(parts[2], "c");
|
||||
assertTrue(!binding(parts[2]));
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseEndingBinding = function(){
|
||||
var parts = parseBindings("a{{b}}");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "a");
|
||||
assertTrue(!binding(parts[0]));
|
||||
assertEquals(parts[1], "{{b}}");
|
||||
assertEquals(binding(parts[1]), "b");
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseBeggingBinding = function(){
|
||||
var parts = parseBindings("{{b}}c");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "c");
|
||||
assertTrue(!binding(parts[1]));
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseLoanBinding = function(){
|
||||
var parts = parseBindings("{{b}}");
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseTwoBindings = function(){
|
||||
var parts = parseBindings("{{b}}{{c}}");
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "{{c}}");
|
||||
assertEquals(binding(parts[1]), "c");
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseTwoBindingsWithTextInMiddle = function(){
|
||||
var parts = parseBindings("{{b}}x{{c}}");
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], "{{b}}");
|
||||
assertEquals(binding(parts[0]), "b");
|
||||
assertEquals(parts[1], "x");
|
||||
assertTrue(!binding(parts[1]));
|
||||
assertEquals(parts[2], "{{c}}");
|
||||
assertEquals(binding(parts[2]), "c");
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testParseMultiline = function(){
|
||||
var parts = parseBindings('"X\nY{{A\nB}}C\nD"');
|
||||
assertTrue(!!binding('{{A\nB}}'));
|
||||
assertEquals(parts.length, 3);
|
||||
assertEquals(parts[0], '"X\nY');
|
||||
assertEquals(parts[1], '{{A\nB}}');
|
||||
assertEquals(parts[2], 'C\nD"');
|
||||
};
|
||||
|
||||
BindingMarkupTest.prototype.testHasBinding = function(){
|
||||
assertTrue(hasBindings(parseBindings("{{a}}")));
|
||||
assertTrue(!hasBindings(parseBindings("a")));
|
||||
assertTrue(hasBindings(parseBindings("{{b}}x{{c}}")));
|
||||
};
|
||||
|
||||
@@ -17,8 +17,7 @@ describe("service", function(){
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
if (scope && scope.$element)
|
||||
scope.$element.remove();
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
|
||||
@@ -202,7 +201,7 @@ describe("service", function(){
|
||||
});
|
||||
|
||||
it('should update hash before any processing', function(){
|
||||
var scope = compile('<div>');
|
||||
scope = compile('<div>');
|
||||
var log = '';
|
||||
scope.$watch('$location.hash', function(){
|
||||
log += this.$location.hashPath + ';';
|
||||
@@ -259,7 +258,7 @@ describe("service", function(){
|
||||
|
||||
describe("$invalidWidgets", function(){
|
||||
it("should count number of invalid widgets", function(){
|
||||
var scope = compile('<input name="price" ng:required ng:validate="number"></input>');
|
||||
scope = compile('<input name="price" ng:required ng:validate="number"></input>');
|
||||
jqLite(document.body).append(scope.$element);
|
||||
scope.$init();
|
||||
expect(scope.$invalidWidgets.length).toEqual(1);
|
||||
@@ -291,8 +290,8 @@ describe("service", function(){
|
||||
function BookChapter() {
|
||||
this.log = '<init>';
|
||||
}
|
||||
var scope = compile('<div></div>').$init();
|
||||
var $route = scope.$inject('$route');
|
||||
scope = compile('<div></div>').$init();
|
||||
$route = scope.$inject('$route');
|
||||
$route.when('/Book/:book/Chapter/:chapter', {controller: BookChapter, template:'Chapter.html'});
|
||||
$route.when('/Blank');
|
||||
$route.onChange(function(){
|
||||
|
||||
@@ -50,6 +50,25 @@ beforeEach(function(){
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(clearJqCache);
|
||||
|
||||
function clearJqCache(){
|
||||
var count = 0;
|
||||
foreachSorted(jqCache, function(value, key){
|
||||
count ++;
|
||||
delete jqCache[key];
|
||||
foreach(value, function(value, key){
|
||||
if (value.$element)
|
||||
dump(key, sortedHtml(value.$element));
|
||||
else
|
||||
dump(key, toJson(value));
|
||||
});
|
||||
});
|
||||
if (count) {
|
||||
fail('Found jqCache references that were not deallocated!');
|
||||
}
|
||||
}
|
||||
|
||||
function nakedExpect(obj) {
|
||||
return expect(angular.fromJson(angular.toJson(obj)));
|
||||
}
|
||||
@@ -58,6 +77,11 @@ function childNode(element, index) {
|
||||
return jqLite(element[0].childNodes[index]);
|
||||
}
|
||||
|
||||
function dealoc(obj) {
|
||||
var element = (obj||{}).$element || obj;
|
||||
if (element && element.dealoc) element.dealoc();
|
||||
}
|
||||
|
||||
extend(angular, {
|
||||
'element': jqLite,
|
||||
'compile': compile,
|
||||
|
||||
@@ -16,8 +16,7 @@ describe("widget", function(){
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
if (element && element.dealoc) element.dealoc();
|
||||
expect(size(jqCache)).toEqual(0);
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
describe("input", function(){
|
||||
@@ -362,7 +361,7 @@ describe("widget", function(){
|
||||
'<option value="{{$index}}" ng:repeat="name in [\'A\', \'B\', \'C\']">{{name}}</option>' +
|
||||
'</select>');
|
||||
// childNodes[0] is repeater comment
|
||||
expect(scope.selection).toEqual(undefined);
|
||||
expect(scope.selection).toEqual(0);
|
||||
|
||||
browserTrigger(element[0].childNodes[2], 'change');
|
||||
expect(scope.selection).toEqual(1);
|
||||
@@ -398,6 +397,32 @@ describe("widget", function(){
|
||||
scope.$eval();
|
||||
expect(element[0].childNodes[1].selected).toEqual(true);
|
||||
});
|
||||
|
||||
it('should select default option on repeater', function(){
|
||||
compile(
|
||||
'<select name="selection">' +
|
||||
'<option ng:repeat="no in [1,2]">{{no}}</option>' +
|
||||
'</select>');
|
||||
expect(scope.selection).toEqual('1');
|
||||
});
|
||||
|
||||
it('should select selected option on repeater', function(){
|
||||
compile(
|
||||
'<select name="selection">' +
|
||||
'<option ng:repeat="no in [1,2]">{{no}}</option>' +
|
||||
'<option selected>ABC</option>' +
|
||||
'</select>');
|
||||
expect(scope.selection).toEqual('ABC');
|
||||
});
|
||||
|
||||
it('should select dynamically selected option on repeater', function(){
|
||||
compile(
|
||||
'<select name="selection">' +
|
||||
'<option ng:repeat="no in [1,2]" ng:bind-attr="{selected:\'{{no==2}}\'}">{{no}}</option>' +
|
||||
'</select>');
|
||||
expect(scope.selection).toEqual('2');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should support type="select-multiple"', function(){
|
||||
@@ -476,6 +501,7 @@ describe("widget", function(){
|
||||
scope.url = '/Book/Moby';
|
||||
scope.$init();
|
||||
expect(scope.$element.text()).toEqual('Moby');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it("should match sandwich ids", function(){
|
||||
@@ -491,6 +517,7 @@ describe("widget", function(){
|
||||
scope.$init();
|
||||
expect(scope.name).toEqual(undefined);
|
||||
expect(scope.$element.text()).toEqual('works');
|
||||
dealoc(scope);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -504,6 +531,7 @@ describe("widget", function(){
|
||||
scope.$inject('$xhr.cache').data.myUrl = {value:'{{name}}'};
|
||||
scope.$init();
|
||||
expect(element.text()).toEqual('misko');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it('should remove previously included text if a falsy value is bound to src', function() {
|
||||
@@ -521,6 +549,7 @@ describe("widget", function(){
|
||||
scope.$eval();
|
||||
|
||||
expect(element.text()).toEqual('');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it('should allow this for scope', function(){
|
||||
@@ -532,6 +561,7 @@ describe("widget", function(){
|
||||
// This should not be 4, but to fix this properly
|
||||
// we need to have real events on the scopes.
|
||||
expect(element.text()).toEqual('4');
|
||||
dealoc(scope);
|
||||
});
|
||||
|
||||
it('should evaluate onload expression when a partial is loaded', function() {
|
||||
@@ -545,6 +575,7 @@ describe("widget", function(){
|
||||
scope.$init();
|
||||
expect(element.text()).toEqual('my partial');
|
||||
expect(scope.loaded).toBe(true);
|
||||
dealoc(scope);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user