Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix(core): drop the toBoolean function #7960

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
"toJsonReplacer": false,
"toJson": false,
"fromJson": false,
"toBoolean": false,
"startingTag": false,
"tryDecodeURIComponent": false,
"parseKeyValue": false,
Expand Down
13 changes: 0 additions & 13 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
-toJsonReplacer,
-toJson,
-fromJson,
-toBoolean,
-startingTag,
-tryDecodeURIComponent,
-parseKeyValue,
Expand Down Expand Up @@ -1033,18 +1032,6 @@ function fromJson(json) {
}


function toBoolean(value) {
if (typeof value === 'function') {
value = true;
} else if (value && value.length !== 0) {
var v = lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
} else {
value = false;
}
return value;
}

/**
* @returns {string} Returns the string representation of the element.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// e.g. <input ng-model="foo" ng-trim="false">
if (toBoolean(attr.ngTrim || 'T')) {
if (!attr.ngTrim || attr.ngTrim !== 'false') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just if (attr.ngTrim !== 'false') { ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catches the case where attr.ngTrim === false as well as attr.ngTrim === 'false'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not clear that we really want to test for it to equal 'false' if the point of this PR is to get rid of toBoolean-like behaviour, though. Why not just !attr.ngTrim? I guess we aren't parsing it, hmm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry for the noise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it catches both when attr.ngTrim === undefined (not false) and when attr.ngTrim === 'false'. attr.ngTrim is either undefined or a string, like all attr values.

So yeah, we're not parsing it (I don't know why), so we have to sort-of parse it here.

Btw, that also means ng-trim="some_falsy_variable" won't work, why is it implemented in this way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cost of watching, probably

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just assumed that this is a "static" setting so we didn't want to complicate the implementation with parsing and full expression support.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should consider moving this to ngModelOptions now that we have it. but that's for a separate PR.

@petebacondarwin what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree @IgorMinar - it is a good candidate for ngModelOptions. @shahata - perhaps another one of your nice PRs?

value = trim(value);
}

Expand Down
2 changes: 1 addition & 1 deletion src/ng/directive/ngIf.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ var ngIfDirective = ['$animate', function($animate) {
var block, childScope, previousElements;
$scope.$watch($attr.ngIf, function ngIfWatchAction(value) {

if (toBoolean(value)) {
if (value) {
if (!childScope) {
$transclude(function (clone, newScope) {
childScope = newScope;
Expand Down
22 changes: 6 additions & 16 deletions src/ng/directive/ngShowHide.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,10 @@
* <div ng-show="myValue" class="ng-hide"></div>
* ```
*
* When the ngShow expression evaluates to false then the ng-hide CSS class is added to the class attribute
* on the element causing it to become hidden. When true, the ng-hide CSS class is removed
* When the ngShow expression evaluates to a falsy value then the ng-hide CSS class is added to the class
* attribute on the element causing it to become hidden. When truthy, the ng-hide CSS class is removed
* from the element causing the element not to appear hidden.
*
* <div class="alert alert-warning">
* **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
* "f" / "0" / "false" / "no" / "n" / "[]"
* </div>
*
* ## Why is !important used?
*
* You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
Expand Down Expand Up @@ -163,7 +158,7 @@
var ngShowDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
$animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
$animate[value ? 'removeClass' : 'addClass'](element, 'ng-hide');
});
};
}];
Expand All @@ -188,15 +183,10 @@ var ngShowDirective = ['$animate', function($animate) {
* <div ng-hide="myValue"></div>
* ```
*
* When the ngHide expression evaluates to true then the .ng-hide CSS class is added to the class attribute
* on the element causing it to become hidden. When false, the ng-hide CSS class is removed
* When the ngHide expression evaluates to a truthy value then the .ng-hide CSS class is added to the class
* attribute on the element causing it to become hidden. When falsy, the ng-hide CSS class is removed
* from the element causing the element not to appear hidden.
*
* <div class="alert alert-warning">
* **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
* "f" / "0" / "false" / "no" / "n" / "[]"
* </div>
*
* ## Why is !important used?
*
* You may be wondering why !important is used for the .ng-hide CSS class. This is because the `.ng-hide` selector
Expand Down Expand Up @@ -319,7 +309,7 @@ var ngShowDirective = ['$animate', function($animate) {
var ngHideDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
$animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
$animate[value ? 'addClass' : 'removeClass'](element, 'ng-hide');
});
};
}];
2 changes: 1 addition & 1 deletion src/ng/filter/orderBy.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function orderByFilter($parse){
return 0;
}
function reverseComparator(comp, descending) {
return toBoolean(descending)
return descending
? function(a,b){return comp(b,a);}
: comp;
}
Expand Down
1 change: 0 additions & 1 deletion test/.jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
"toJsonReplacer": false,
"toJson": false,
"fromJson": false,
"toBoolean": false,
"startingTag": false,
"tryDecodeURIComponent": false,
"parseKeyValue": false,
Expand Down
20 changes: 20 additions & 0 deletions test/BinderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ describe('Binder', function() {
$rootScope.hidden = 'false';
$rootScope.$apply();

assertHidden(element);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be assertVisible()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wait, misread the above line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a nit, but I would personally prefer it if each of these separate test cases within this function were in their own tests --- see what Igor thinks I guess

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the whole file is written in this way, though. I guess we can improve it incrementally while we touch it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...that said, tests here are quite simple, an error will tell you where the problem lies anyway and wrapping it in separate its would add boilerplate for beforeEach & similar stuff.

@IgorMinar, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is one of the oldest test files. we are slowly killing these tests and moving them to other more appropriate files.

so don't make big refactorings here.


$rootScope.hidden = 0;
$rootScope.$apply();

assertVisible(element);

$rootScope.hidden = false;
$rootScope.$apply();

assertVisible(element);

$rootScope.hidden = '';
Expand All @@ -267,6 +277,16 @@ describe('Binder', function() {
$rootScope.show = 'false';
$rootScope.$apply();

assertVisible(element);

$rootScope.show = false;
$rootScope.$apply();

assertHidden(element);

$rootScope.show = false;
$rootScope.$apply();

assertHidden(element);

$rootScope.show = '';
Expand Down
20 changes: 16 additions & 4 deletions test/ng/directive/ngIfSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ describe('ngIf', function () {
dealoc(element);
});

function makeIf(expr) {
element.append($compile('<div class="my-class" ng-if="' + expr + '"><div>Hi</div></div>')($scope));
function makeIf() {
forEach(arguments, function (expr) {
element.append($compile('<div class="my-class" ng-if="' + expr + '"><div>Hi</div></div>')($scope));
});
$scope.$apply();
}

it('should immediately remove element if condition is false', function () {
makeIf('false');
it('should immediately remove the element if condition is falsy', function () {
makeIf('false', 'undefined', 'null', 'NaN', '\'\'', '0');
expect(element.children().length).toBe(0);
});

Expand All @@ -31,6 +33,16 @@ describe('ngIf', function () {
expect(element.children().length).toBe(1);
});

it('should leave the element if the condition is a non-empty string', function () {
makeIf('\'f\'', '\'0\'', '\'false\'', '\'no\'', '\'n\'', '\'[]\'');
expect(element.children().length).toBe(6);
});

it('should leave the element if the condition is an object', function () {
makeIf('[]', '{}');
expect(element.children().length).toBe(2);
});

it('should not add the element twice if the condition goes from true to true', function () {
$scope.hello = 'true1';
makeIf('hello');
Expand Down
100 changes: 76 additions & 24 deletions test/ng/directive/ngShowHideSpec.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,106 @@
'use strict';

describe('ngShow / ngHide', function() {
var element;
var $scope, $compile, element;

function expectVisibility(exprs, ngShowOrNgHide, shownOrHidden) {
element = $compile('<div></div>')($scope);
forEach(exprs, function (expr) {
var childElem = $compile('<div ' + ngShowOrNgHide + '="' + expr + '"></div>')($scope);
element.append(childElem);
$scope.$digest();
expect(childElem)[shownOrHidden === 'shown' ? 'toBeShown' : 'toBeHidden']();
});
}

beforeEach(inject(function ($rootScope, _$compile_) {
$scope = $rootScope.$new();
$compile = _$compile_;
}));

afterEach(function() {
dealoc(element);
});

describe('ngShow', function() {
it('should show and hide an element', inject(function($rootScope, $compile) {
function expectShown() {
expectVisibility(arguments, 'ng-show', 'shown');
}

function expectHidden() {
expectVisibility(arguments, 'ng-show', 'hidden');
}

it('should show and hide an element', function() {
element = jqLite('<div ng-show="exp"></div>');
element = $compile(element)($rootScope);
$rootScope.$digest();
element = $compile(element)($scope);
$scope.$digest();
expect(element).toBeHidden();
$rootScope.exp = true;
$rootScope.$digest();
$scope.exp = true;
$scope.$digest();
expect(element).toBeShown();
}));

});

// https://github.com/angular/angular.js/issues/5414
it('should show if the expression is a function with a no arguments', inject(function($rootScope, $compile) {
it('should show if the expression is a function with a no arguments', function() {
element = jqLite('<div ng-show="exp"></div>');
element = $compile(element)($rootScope);
$rootScope.exp = function(){};
$rootScope.$digest();
element = $compile(element)($scope);
$scope.exp = function(){};
$scope.$digest();
expect(element).toBeShown();
}));

});

it('should make hidden element visible', inject(function($rootScope, $compile) {
it('should make hidden element visible', function() {
element = jqLite('<div class="ng-hide" ng-show="exp"></div>');
element = $compile(element)($rootScope);
element = $compile(element)($scope);
expect(element).toBeHidden();
$rootScope.exp = true;
$rootScope.$digest();
$scope.exp = true;
$scope.$digest();
expect(element).toBeShown();
}));
});

it('should hide the element if condition is falsy', function() {
expectHidden('false', 'undefined', 'null', 'NaN', '\'\'', '0');
});

it('should show the element if condition is a non-empty string', function() {
expectShown('\'f\'', '\'0\'', '\'false\'', '\'no\'', '\'n\'', '\'[]\'');
});

it('should show the element if condition is an object', function() {
expectShown('[]', '{}');
});
});

describe('ngHide', function() {
it('should hide an element', inject(function($rootScope, $compile) {
function expectShown() {
expectVisibility(arguments, 'ng-hide', 'shown');
}

function expectHidden() {
expectVisibility(arguments, 'ng-hide', 'hidden');
}

it('should hide an element', function() {
element = jqLite('<div ng-hide="exp"></div>');
element = $compile(element)($rootScope);
element = $compile(element)($scope);
expect(element).toBeShown();
$rootScope.exp = true;
$rootScope.$digest();
$scope.exp = true;
$scope.$digest();
expect(element).toBeHidden();
}));
});

it('should show the element if condition is falsy', function() {
expectShown('false', 'undefined', 'null', 'NaN', '\'\'', '0');
});

it('should hide the element if condition is a non-empty string', function() {
expectHidden('\'f\'', '\'0\'', '\'false\'', '\'no\'', '\'n\'', '\'[]\'');
});

it('should hide the element if condition is an object', function() {
expectHidden('[]', '{}');
});
});
});

Expand Down