2011-07-17 01:05:43 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
2011-10-07 11:27:49 -07:00
|
|
|
describe('Binder', function() {
|
2011-11-04 12:33:01 -07:00
|
|
|
|
2011-11-22 21:28:39 -08:00
|
|
|
var element;
|
|
|
|
|
|
2011-11-04 12:33:01 -07:00
|
|
|
function childNode(element, index) {
|
|
|
|
|
return jqLite(element[0].childNodes[index]);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-07 11:27:49 -07:00
|
|
|
beforeEach(function() {
|
2014-10-22 17:31:27 -04:00
|
|
|
this.compileToHtml = function(content) {
|
2011-10-17 16:56:56 -07:00
|
|
|
var html;
|
2014-10-20 21:59:40 -04:00
|
|
|
inject(function($rootScope, $compile) {
|
2011-10-17 16:56:56 -07:00
|
|
|
content = jqLite(content);
|
2011-10-25 22:21:21 -07:00
|
|
|
$compile(content)($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
html = sortedHtml(content);
|
2012-01-12 11:06:10 -08:00
|
|
|
});
|
2011-10-17 16:56:56 -07:00
|
|
|
return html;
|
2011-01-06 14:34:21 -08:00
|
|
|
};
|
|
|
|
|
});
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-07 11:27:49 -07:00
|
|
|
afterEach(function() {
|
2011-11-22 21:28:39 -08:00
|
|
|
dealoc(element);
|
|
|
|
|
dealoc(this.element);
|
2011-01-06 14:34:21 -08:00
|
|
|
});
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('BindUpdate', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
$compile('<div ng-init="a=123"/>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$digest();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect($rootScope.a).toBe(123);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ExecuteInitialization', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
$compile('<div ng-init="a=123">')($rootScope);
|
2012-01-02 20:50:17 -08:00
|
|
|
expect($rootScope.a).toBe(123);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ExecuteInitializationStatements', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
$compile('<div ng-init="a=123;b=345">')($rootScope);
|
2012-01-02 20:50:17 -08:00
|
|
|
expect($rootScope.a).toBe(123);
|
|
|
|
|
expect($rootScope.b).toBe(345);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ApplyTextBindings', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-bind="model.a">x</div>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.model = {a:123};
|
|
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(element.text()).toBe('123');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('InputTypeButtonActionExecutesInScope', inject(function($rootScope, $compile) {
|
2011-01-06 14:34:21 -08:00
|
|
|
var savedCalled = false;
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2012-03-09 00:00:05 -08:00
|
|
|
'<input type="button" ng-click="person.save()" value="Apply">')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.person = {};
|
|
|
|
|
$rootScope.person.save = function() {
|
2011-01-06 14:34:21 -08:00
|
|
|
savedCalled = true;
|
2011-03-23 09:33:29 -07:00
|
|
|
};
|
2011-10-17 16:56:56 -07:00
|
|
|
browserTrigger(element, 'click');
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(savedCalled).toBe(true);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('InputTypeButtonActionExecutesInScope2', inject(function($rootScope, $compile) {
|
2016-08-10 12:13:14 +02:00
|
|
|
var log = '';
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<input type="image" ng-click="action()">')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.action = function() {
|
2011-01-06 14:34:21 -08:00
|
|
|
log += 'click;';
|
2011-03-23 09:33:29 -07:00
|
|
|
};
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(log).toEqual('');
|
2011-10-17 16:56:56 -07:00
|
|
|
browserTrigger(element, 'click');
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(log).toEqual('click;');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ButtonElementActionExecutesInScope', inject(function($rootScope, $compile) {
|
2011-01-06 14:34:21 -08:00
|
|
|
var savedCalled = false;
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<button ng-click="person.save()">Apply</button>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.person = {};
|
|
|
|
|
$rootScope.person.save = function() {
|
2011-01-06 14:34:21 -08:00
|
|
|
savedCalled = true;
|
2011-03-23 09:33:29 -07:00
|
|
|
};
|
2011-10-17 16:56:56 -07:00
|
|
|
browserTrigger(element, 'click');
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(savedCalled).toBe(true);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('RepeaterUpdateBindings', inject(function($rootScope, $compile) {
|
|
|
|
|
var form = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<ul>' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<LI ng-repeat="item in model.items" ng-bind="item.a"></LI>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</ul>')($rootScope);
|
2012-01-03 14:49:41 -08:00
|
|
|
var items = [{a: 'A'}, {a: 'B'}];
|
|
|
|
|
$rootScope.model = {items: items};
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(form)).toBe(
|
|
|
|
|
'<ul>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">A</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">B</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</ul>');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-01-03 14:49:41 -08:00
|
|
|
items.unshift({a: 'C'});
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(form)).toBe(
|
|
|
|
|
'<ul>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">C</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">A</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">B</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</ul>');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
items.shift();
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(form)).toBe(
|
|
|
|
|
'<ul>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">A</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-bind="item.a" ng-repeat="item in model.items">B</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</ul>');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
items.shift();
|
|
|
|
|
items.shift();
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
|
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('RepeaterContentDoesNotBind', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<ul>' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<LI ng-repeat="item in model.items"><span ng-bind="item.a"></span></li>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</ul>')($rootScope);
|
2012-01-03 14:49:41 -08:00
|
|
|
$rootScope.model = {items: [{a: 'A'}]};
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(element)).toBe(
|
|
|
|
|
'<ul>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: item in model.items -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<li ng-repeat="item in model.items"><span ng-bind="item.a">A</span></li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: item in model.items -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</ul>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-07 11:27:49 -07:00
|
|
|
it('DoNotOverwriteCustomAction', function() {
|
2011-01-06 14:34:21 -08:00
|
|
|
var html = this.compileToHtml('<input type="submit" value="Save" action="foo();">');
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(html.indexOf('action="foo();"')).toBeGreaterThan(0);
|
2011-01-06 14:34:21 -08:00
|
|
|
});
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ItShouldRemoveExtraChildrenWhenIteratingOverHash', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div><div ng-repeat="i in items">{{i}}</div></div>')($rootScope);
|
2011-01-06 14:34:21 -08:00
|
|
|
var items = {};
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.items = items;
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2013-09-23 11:24:42 -07:00
|
|
|
expect(element[0].childNodes.length).toEqual(1);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-01-03 14:49:41 -08:00
|
|
|
items.name = 'misko';
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2013-09-23 11:24:42 -07:00
|
|
|
expect(element[0].childNodes.length).toEqual(3);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
delete items.name;
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2013-09-23 11:24:42 -07:00
|
|
|
expect(element[0].childNodes.length).toEqual(1);
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2012-01-12 11:06:10 -08:00
|
|
|
it('IfAttrBindingThrowsErrorDecorateTheAttribute', function() {
|
2014-10-20 21:59:40 -04:00
|
|
|
module(function($exceptionHandlerProvider) {
|
2012-01-12 11:06:10 -08:00
|
|
|
$exceptionHandlerProvider.mode('log');
|
|
|
|
|
});
|
|
|
|
|
inject(function($rootScope, $exceptionHandler, $compile) {
|
|
|
|
|
$compile('<div attr="before {{error.throw()}} after"></div>', null, true)($rootScope);
|
|
|
|
|
var errorLogs = $exceptionHandler.errors;
|
|
|
|
|
var count = 0;
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-01-12 11:06:10 -08:00
|
|
|
$rootScope.error = {
|
2012-01-03 14:49:41 -08:00
|
|
|
'throw': function() {throw new Error('ErrorMsg' + (++count));}
|
2012-01-12 11:06:10 -08:00
|
|
|
};
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
expect(errorLogs.length).not.toEqual(0);
|
|
|
|
|
expect(errorLogs.shift()).toMatch(/ErrorMsg1/);
|
|
|
|
|
errorLogs.length = 0;
|
2011-03-23 09:33:29 -07:00
|
|
|
|
2012-01-12 11:06:10 -08:00
|
|
|
$rootScope.error['throw'] = function() { return 'X';};
|
|
|
|
|
$rootScope.$apply();
|
2016-03-13 13:20:30 +01:00
|
|
|
expect(errorLogs.length).toMatch('0');
|
2012-01-12 11:06:10 -08:00
|
|
|
});
|
|
|
|
|
});
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('NestedRepeater', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<div>' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<div ng-repeat="m in model" name="{{m.name}}">' +
|
|
|
|
|
'<ul name="{{i}}" ng-repeat="i in m.item"></ul>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</div>' +
|
|
|
|
|
'</div>')($rootScope);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.model = [{name:'a', item:['a1', 'a2']}, {name:'b', item:['b1', 'b2']}];
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(element)).toBe(
|
2014-11-08 11:26:29 -05:00
|
|
|
'<div>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: m in model -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<div name="a" ng-repeat="m in model">' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: i in m.item -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<ul name="a1" ng-repeat="i in m.item"></ul>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in m.item -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<ul name="a2" ng-repeat="i in m.item"></ul>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in m.item -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'</div>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: m in model -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<div name="b" ng-repeat="m in model">' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: i in m.item -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<ul name="b1" ng-repeat="i in m.item"></ul>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in m.item -->' +
|
2014-11-08 11:26:29 -05:00
|
|
|
'<ul name="b2" ng-repeat="i in m.item"></ul>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in m.item -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</div>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: m in model -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</div>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('HideBindingExpression', inject(function($rootScope, $compile) {
|
2016-07-20 15:45:04 +02:00
|
|
|
element = $compile('<div ng-hide="hidden === 3"/>')($rootScope);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.hidden = 3;
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertHidden(element);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.hidden = 2;
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertVisible(element);
|
|
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('HideBinding', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-hide="hidden"/>')($rootScope);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.hidden = 'true';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertHidden(element);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.hidden = 'false';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
fix(core): drop the toBoolean function
So far Angular have used the toBoolean function to decide if the parsed value
is truthy. The function made more values falsy than regular JavaScript would,
e.g. strings 'f' and 'no' were both treated as falsy. This creates suble bugs
when backend sends a non-empty string with one of these values and something
suddenly hides in the application
Thanks to lgalfaso for test ideas.
BREAKING CHANGE: values 'f', '0', 'false', 'no', 'n', '[]' are no longer
treated as falsy. Only JavaScript falsy values are now treated as falsy by the
expression parser; there are six of them: false, null, undefined, NaN, 0 and "".
Closes #3969
Closes #4277
Closes #7960
2014-06-24 00:07:30 +02:00
|
|
|
assertHidden(element);
|
|
|
|
|
|
|
|
|
|
$rootScope.hidden = 0;
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
|
|
|
|
|
assertVisible(element);
|
|
|
|
|
|
|
|
|
|
$rootScope.hidden = false;
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertVisible(element);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.hidden = '';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertVisible(element);
|
|
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ShowBinding', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-show="show"/>')($rootScope);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.show = 'true';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertVisible(element);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.show = 'false';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
fix(core): drop the toBoolean function
So far Angular have used the toBoolean function to decide if the parsed value
is truthy. The function made more values falsy than regular JavaScript would,
e.g. strings 'f' and 'no' were both treated as falsy. This creates suble bugs
when backend sends a non-empty string with one of these values and something
suddenly hides in the application
Thanks to lgalfaso for test ideas.
BREAKING CHANGE: values 'f', '0', 'false', 'no', 'n', '[]' are no longer
treated as falsy. Only JavaScript falsy values are now treated as falsy by the
expression parser; there are six of them: false, null, undefined, NaN, 0 and "".
Closes #3969
Closes #4277
Closes #7960
2014-06-24 00:07:30 +02:00
|
|
|
assertVisible(element);
|
|
|
|
|
|
|
|
|
|
$rootScope.show = false;
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
|
|
|
|
|
assertHidden(element);
|
|
|
|
|
|
|
|
|
|
$rootScope.show = false;
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertHidden(element);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.show = '';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
assertHidden(element);
|
|
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('BindClass', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-class="clazz"/>')($rootScope);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.clazz = 'testClass';
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-03-09 00:00:05 -08:00
|
|
|
expect(sortedHtml(element)).toBe('<div class="testClass" ng-class="clazz"></div>');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.clazz = ['a', 'b'];
|
|
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-03-09 00:00:05 -08:00
|
|
|
expect(sortedHtml(element)).toBe('<div class="a b" ng-class="clazz"></div>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('BindClassEvenOdd', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<div>' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<div ng-repeat="i in [0,1]" ng-class-even="\'e\'" ng-class-odd="\'o\'"></div>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</div>')($rootScope);
|
|
|
|
|
$rootScope.$apply();
|
2013-09-23 11:24:42 -07:00
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
var d1 = jqLite(element[0].childNodes[1]);
|
2013-09-23 11:24:42 -07:00
|
|
|
var d2 = jqLite(element[0].childNodes[3]);
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(d1.hasClass('o')).toBeTruthy();
|
|
|
|
|
expect(d2.hasClass('e')).toBeTruthy();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(element)).toBe(
|
2012-12-04 18:24:15 +01:00
|
|
|
'<div>' +
|
|
|
|
|
'<!-- ngRepeat: i in [0,1] -->' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<div class="o" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat="i in [0,1]"></div>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in [0,1] -->' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<div class="e" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat="i in [0,1]"></div>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: i in [0,1] -->' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'</div>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('BindStyle', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-style="style"/>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
|
|
|
|
|
$rootScope.$eval('style={height: "10px"}');
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
|
2012-01-03 14:49:41 -08:00
|
|
|
expect(element.css('height')).toBe('10px');
|
2011-10-17 16:56:56 -07:00
|
|
|
|
|
|
|
|
$rootScope.$eval('style={}');
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
}));
|
|
|
|
|
|
2012-01-12 11:06:10 -08:00
|
|
|
it('ActionOnAHrefThrowsError', function() {
|
2014-10-20 21:59:40 -04:00
|
|
|
module(function($exceptionHandlerProvider) {
|
2011-11-02 16:32:46 -07:00
|
|
|
$exceptionHandlerProvider.mode('log');
|
2012-01-12 11:06:10 -08:00
|
|
|
});
|
|
|
|
|
inject(function($rootScope, $exceptionHandler, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
var input = $compile('<a ng-click="action()">Add Phone</a>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.action = function() {
|
|
|
|
|
throw new Error('MyError');
|
|
|
|
|
};
|
|
|
|
|
browserTrigger(input, 'click');
|
|
|
|
|
expect($exceptionHandler.errors[0]).toMatch(/MyError/);
|
2012-01-12 11:06:10 -08:00
|
|
|
});
|
|
|
|
|
});
|
2011-10-17 16:56:56 -07:00
|
|
|
|
2016-10-28 10:51:03 +00:00
|
|
|
it('ShouldIgnoreVbNonBindable', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2016-08-10 12:13:14 +02:00
|
|
|
'<div>{{a}}' +
|
|
|
|
|
'<div ng-non-bindable>{{a}}</div>' +
|
|
|
|
|
'<div ng-non-bindable=\'\'>{{b}}</div>' +
|
|
|
|
|
'<div ng-non-bindable=\'true\'>{{c}}</div>' +
|
|
|
|
|
'</div>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.a = 123;
|
|
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(element.text()).toBe('123{{a}}{{b}}{{c}}');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2014-10-22 17:31:27 -04:00
|
|
|
it('ShouldTemplateBindPreElements', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile('<pre>Hello {{name}}!</pre>')($rootScope);
|
2016-08-10 12:13:14 +02:00
|
|
|
$rootScope.name = 'World';
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2012-01-29 21:59:35 -08:00
|
|
|
expect(sortedHtml(element)).toBe('<pre>Hello World!</pre>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('FillInOptionValueWhenMissing', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2012-03-09 00:00:05 -08:00
|
|
|
'<select ng-model="foo">' +
|
2011-09-08 13:56:29 -07:00
|
|
|
'<option selected="true">{{a}}</option>' +
|
|
|
|
|
'<option value="">{{b}}</option>' +
|
|
|
|
|
'<option>C</option>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</select>')($rootScope);
|
|
|
|
|
$rootScope.a = 'A';
|
|
|
|
|
$rootScope.b = 'B';
|
|
|
|
|
$rootScope.$apply();
|
|
|
|
|
var optionA = childNode(element, 0);
|
|
|
|
|
var optionB = childNode(element, 1);
|
|
|
|
|
var optionC = childNode(element, 2);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(optionA.attr('value')).toEqual('A');
|
|
|
|
|
expect(optionA.text()).toEqual('A');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(optionB.attr('value')).toEqual('');
|
|
|
|
|
expect(optionB.text()).toEqual('B');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
expect(optionC.attr('value')).toEqual('C');
|
|
|
|
|
expect(optionC.text()).toEqual('C');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
fix(input): listen on "change" instead of "click" for radio/checkbox ngModels
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.
It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html
Closes #4516
Closes #14667
Closes #14685
BREAKING CHANGE:
`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.
Two scenarios might need migration:
- Custom click events:
Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.
If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.
- Triggering click events:
Conventional trigger functions:
The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method,
the change event will not be fired when the input isn't attached to the document.
Before:
```js
it('should update the model', inject(function($compile, $rootScope) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
`$rootElement`, and the `$rootElement` to the `$document`.
After:
```js
it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
$rootElement.append(inputElm);
$document.append($rootElement);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
`triggerHandler()`:
If you are using this jQuery / jqLite function on the input elements, you don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
2017-10-13 15:39:53 +02:00
|
|
|
it('ItShouldSelectTheCorrectRadioBox', inject(function($rootScope, $compile, $rootElement, $document) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<div>' +
|
2012-03-09 00:00:05 -08:00
|
|
|
'<input type="radio" ng-model="sex" value="female">' +
|
|
|
|
|
'<input type="radio" ng-model="sex" value="male">' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</div>')($rootScope);
|
fix(input): listen on "change" instead of "click" for radio/checkbox ngModels
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.
It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html
Closes #4516
Closes #14667
Closes #14685
BREAKING CHANGE:
`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.
Two scenarios might need migration:
- Custom click events:
Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call event.preventDefault()
to prevent the input from updating.
If an app uses a click event listener that expects ngModel to be updated when it is called, it now
needs to register a change event listener instead.
- Triggering click events:
Conventional trigger functions:
The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and
trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method,
the change event will not be fired when the input isn't attached to the document.
Before:
```js
it('should update the model', inject(function($compile, $rootScope) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
With this patch, `$rootScope.checkbox` might not be true, because the click event
hasn't triggered the change event. To make the test, work append the inputElm to the app's
`$rootElement`, and the `$rootElement` to the `$document`.
After:
```js
it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
$rootElement.append(inputElm);
$document.append($rootElement);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
`triggerHandler()`:
If you are using this jQuery / jqLite function on the input elements, you don't have to attach
the elements to the document, but instead change the triggered event to "change". This is because
`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
2017-10-13 15:39:53 +02:00
|
|
|
|
|
|
|
|
// Append the app to the document so that "click" on a radio/checkbox triggers "change"
|
|
|
|
|
// Support: Chrome, Safari 8, 9
|
|
|
|
|
jqLite($document[0].body).append($rootElement.append(element));
|
|
|
|
|
|
2011-10-17 16:56:56 -07:00
|
|
|
var female = jqLite(element[0].childNodes[0]);
|
|
|
|
|
var male = jqLite(element[0].childNodes[1]);
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
browserTrigger(female);
|
2012-01-03 14:49:41 -08:00
|
|
|
expect($rootScope.sex).toBe('female');
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(female[0].checked).toBe(true);
|
|
|
|
|
expect(male[0].checked).toBe(false);
|
2012-01-03 14:49:41 -08:00
|
|
|
expect(female.val()).toBe('female');
|
2011-01-19 15:42:11 -08:00
|
|
|
|
2011-01-06 14:34:21 -08:00
|
|
|
browserTrigger(male);
|
2012-01-03 14:49:41 -08:00
|
|
|
expect($rootScope.sex).toBe('male');
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(female[0].checked).toBe(false);
|
|
|
|
|
expect(male[0].checked).toBe(true);
|
2012-01-03 14:49:41 -08:00
|
|
|
expect(male.val()).toBe('male');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ItShouldRepeatOnHashes', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile(
|
2011-10-17 16:56:56 -07:00
|
|
|
'<ul>' +
|
2016-07-20 15:45:04 +02:00
|
|
|
'<li ng-repeat="(k,v) in {a:0,b:1}" ng-bind="k + v"></li>' +
|
2011-10-17 16:56:56 -07:00
|
|
|
'</ul>')($rootScope);
|
|
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect(sortedHtml(element)).toBe(
|
|
|
|
|
'<ul>' +
|
2012-12-04 18:24:15 +01:00
|
|
|
'<!-- ngRepeat: (k,v) in {a:0,b:1} -->' +
|
2016-07-20 15:45:04 +02:00
|
|
|
'<li ng-bind="k + v" ng-repeat="(k,v) in {a:0,b:1}">a0</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: (k,v) in {a:0,b:1} -->' +
|
2016-07-20 15:45:04 +02:00
|
|
|
'<li ng-bind="k + v" ng-repeat="(k,v) in {a:0,b:1}">b1</li>' +
|
2013-09-23 11:24:42 -07:00
|
|
|
'<!-- end ngRepeat: (k,v) in {a:0,b:1} -->' +
|
2012-01-02 20:50:17 -08:00
|
|
|
'</ul>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ItShouldFireChangeListenersBeforeUpdate', inject(function($rootScope, $compile) {
|
2012-03-09 00:00:05 -08:00
|
|
|
element = $compile('<div ng-bind="name"></div>')($rootScope);
|
2012-01-03 14:49:41 -08:00
|
|
|
$rootScope.name = '';
|
2014-10-22 17:31:27 -04:00
|
|
|
$rootScope.$watch('watched', function() {
|
2014-07-14 09:46:56 -07:00
|
|
|
$rootScope.name = 123;
|
|
|
|
|
});
|
2012-01-03 14:49:41 -08:00
|
|
|
$rootScope.watched = 'change';
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-02 20:50:17 -08:00
|
|
|
expect($rootScope.name).toBe(123);
|
2012-03-09 00:00:05 -08:00
|
|
|
expect(sortedHtml(element)).toBe('<div ng-bind="name">123</div>');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
|
|
|
|
|
2011-10-25 22:21:21 -07:00
|
|
|
it('ItShouldHandleMultilineBindings', inject(function($rootScope, $compile) {
|
2011-11-22 21:28:39 -08:00
|
|
|
element = $compile('<div>{{\n 1 \n + \n 2 \n}}</div>')($rootScope);
|
2011-10-17 16:56:56 -07:00
|
|
|
$rootScope.$apply();
|
2012-01-03 14:49:41 -08:00
|
|
|
expect(element.text()).toBe('3');
|
2011-10-17 16:56:56 -07:00
|
|
|
}));
|
2011-01-19 15:42:11 -08:00
|
|
|
|
|
|
|
|
});
|