mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 17:12:34 +08:00
refact(input): split input.js into smaller files
The input.js file is unnecessarily large, containing many directives including the vast `ngModel`. This change moves ngModel and a few other directives into their own files, which will make maintenance easier.
This commit is contained in:
6
angularFiles.js
vendored
6
angularFiles.js
vendored
@@ -53,6 +53,7 @@ var angularFiles = {
|
||||
'src/ng/directive/form.js',
|
||||
'src/ng/directive/input.js',
|
||||
'src/ng/directive/ngBind.js',
|
||||
'src/ng/directive/ngChange.js',
|
||||
'src/ng/directive/ngClass.js',
|
||||
'src/ng/directive/ngCloak.js',
|
||||
'src/ng/directive/ngController.js',
|
||||
@@ -61,6 +62,8 @@ var angularFiles = {
|
||||
'src/ng/directive/ngIf.js',
|
||||
'src/ng/directive/ngInclude.js',
|
||||
'src/ng/directive/ngInit.js',
|
||||
'src/ng/directive/ngList.js',
|
||||
'src/ng/directive/ngModel.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
'src/ng/directive/ngPluralize.js',
|
||||
'src/ng/directive/ngRepeat.js',
|
||||
@@ -70,7 +73,8 @@ var angularFiles = {
|
||||
'src/ng/directive/ngTransclude.js',
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js'
|
||||
'src/ng/directive/style.js',
|
||||
'src/ng/directive/validators.js'
|
||||
],
|
||||
|
||||
'angularLoader': [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
78
src/ng/directive/ngChange.js
Normal file
78
src/ng/directive/ngChange.js
Normal file
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngChange
|
||||
*
|
||||
* @description
|
||||
* Evaluate the given expression when the user changes the input.
|
||||
* The expression is evaluated immediately, unlike the JavaScript onchange event
|
||||
* which only triggers at the end of a change (usually, when the user leaves the
|
||||
* form element or presses the return key).
|
||||
*
|
||||
* The `ngChange` expression is only evaluated when a change in the input value causes
|
||||
* a new value to be committed to the model.
|
||||
*
|
||||
* It will not be evaluated:
|
||||
* * if the value returned from the `$parsers` transformation pipeline has not changed
|
||||
* * if the input has continued to be invalid since the model will stay `null`
|
||||
* * if the model is changed programmatically and not by a change to the input value
|
||||
*
|
||||
*
|
||||
* Note, this directive requires `ngModel` to be present.
|
||||
*
|
||||
* @element input
|
||||
* @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
|
||||
* in input value.
|
||||
*
|
||||
* @example
|
||||
* <example name="ngChange-directive" module="changeExample">
|
||||
* <file name="index.html">
|
||||
* <script>
|
||||
* angular.module('changeExample', [])
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.counter = 0;
|
||||
* $scope.change = function() {
|
||||
* $scope.counter++;
|
||||
* };
|
||||
* }]);
|
||||
* </script>
|
||||
* <div ng-controller="ExampleController">
|
||||
* <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
|
||||
* <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
|
||||
* <label for="ng-change-example2">Confirmed</label><br />
|
||||
* <tt>debug = {{confirmed}}</tt><br/>
|
||||
* <tt>counter = {{counter}}</tt><br/>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* var counter = element(by.binding('counter'));
|
||||
* var debug = element(by.binding('confirmed'));
|
||||
*
|
||||
* it('should evaluate the expression if changing from view', function() {
|
||||
* expect(counter.getText()).toContain('0');
|
||||
*
|
||||
* element(by.id('ng-change-example1')).click();
|
||||
*
|
||||
* expect(counter.getText()).toContain('1');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* });
|
||||
*
|
||||
* it('should not evaluate the expression if changing from model', function() {
|
||||
* element(by.id('ng-change-example2')).click();
|
||||
|
||||
* expect(counter.getText()).toContain('0');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
var ngChangeDirective = valueFn({
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
ctrl.$viewChangeListeners.push(function() {
|
||||
scope.$eval(attr.ngChange);
|
||||
});
|
||||
}
|
||||
});
|
||||
128
src/ng/directive/ngList.js
Normal file
128
src/ng/directive/ngList.js
Normal file
@@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngList
|
||||
*
|
||||
* @description
|
||||
* Text input that converts between a delimited string and an array of strings. The default
|
||||
* delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
|
||||
* delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
|
||||
*
|
||||
* The behaviour of the directive is affected by the use of the `ngTrim` attribute.
|
||||
* * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
|
||||
* list item is respected. This implies that the user of the directive is responsible for
|
||||
* dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
|
||||
* tab or newline character.
|
||||
* * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
|
||||
* when joining the list items back together) and whitespace around each list item is stripped
|
||||
* before it is added to the model.
|
||||
*
|
||||
* ### Example with Validation
|
||||
*
|
||||
* <example name="ngList-directive" module="listExample">
|
||||
* <file name="app.js">
|
||||
* angular.module('listExample', [])
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.names = ['morpheus', 'neo', 'trinity'];
|
||||
* }]);
|
||||
* </file>
|
||||
* <file name="index.html">
|
||||
* <form name="myForm" ng-controller="ExampleController">
|
||||
* List: <input name="namesInput" ng-model="names" ng-list required>
|
||||
* <span class="error" ng-show="myForm.namesInput.$error.required">
|
||||
* Required!</span>
|
||||
* <br>
|
||||
* <tt>names = {{names}}</tt><br/>
|
||||
* <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
|
||||
* <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
|
||||
* <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
* <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* var listInput = element(by.model('names'));
|
||||
* var names = element(by.exactBinding('names'));
|
||||
* var valid = element(by.binding('myForm.namesInput.$valid'));
|
||||
* var error = element(by.css('span.error'));
|
||||
*
|
||||
* it('should initialize to model', function() {
|
||||
* expect(names.getText()).toContain('["morpheus","neo","trinity"]');
|
||||
* expect(valid.getText()).toContain('true');
|
||||
* expect(error.getCssValue('display')).toBe('none');
|
||||
* });
|
||||
*
|
||||
* it('should be invalid if empty', function() {
|
||||
* listInput.clear();
|
||||
* listInput.sendKeys('');
|
||||
*
|
||||
* expect(names.getText()).toContain('');
|
||||
* expect(valid.getText()).toContain('false');
|
||||
* expect(error.getCssValue('display')).not.toBe('none');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* ### Example - splitting on whitespace
|
||||
* <example name="ngList-directive-newlines">
|
||||
* <file name="index.html">
|
||||
* <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
|
||||
* <pre>{{ list | json }}</pre>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it("should split the text by newlines", function() {
|
||||
* var listInput = element(by.model('list'));
|
||||
* var output = element(by.binding('list | json'));
|
||||
* listInput.sendKeys('abc\ndef\nghi');
|
||||
* expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* @element input
|
||||
* @param {string=} ngList optional delimiter that should be used to split the value.
|
||||
*/
|
||||
var ngListDirective = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
priority: 100,
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
// We want to control whitespace trimming so we use this convoluted approach
|
||||
// to access the ngList attribute, which doesn't pre-trim the attribute
|
||||
var ngList = element.attr(attr.$attr.ngList) || ', ';
|
||||
var trimValues = attr.ngTrim !== 'false';
|
||||
var separator = trimValues ? trim(ngList) : ngList;
|
||||
|
||||
var parse = function(viewValue) {
|
||||
// If the viewValue is invalid (say required but empty) it will be `undefined`
|
||||
if (isUndefined(viewValue)) return;
|
||||
|
||||
var list = [];
|
||||
|
||||
if (viewValue) {
|
||||
forEach(viewValue.split(separator), function(value) {
|
||||
if (value) list.push(trimValues ? trim(value) : value);
|
||||
});
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(parse);
|
||||
ctrl.$formatters.push(function(value) {
|
||||
if (isArray(value)) {
|
||||
return value.join(ngList);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// Override the standard $isEmpty because an empty array means the input is empty.
|
||||
ctrl.$isEmpty = function(value) {
|
||||
return !value || !value.length;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
1338
src/ng/directive/ngModel.js
Normal file
1338
src/ng/directive/ngModel.js
Normal file
File diff suppressed because it is too large
Load Diff
91
src/ng/directive/validators.js
Normal file
91
src/ng/directive/validators.js
Normal file
@@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
var requiredDirective = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
link: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
attr.required = true; // force truthy in case we are on non input element
|
||||
|
||||
ctrl.$validators.required = function(modelValue, viewValue) {
|
||||
return !attr.required || !ctrl.$isEmpty(viewValue);
|
||||
};
|
||||
|
||||
attr.$observe('required', function() {
|
||||
ctrl.$validate();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var patternDirective = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
link: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
|
||||
var regexp, patternExp = attr.ngPattern || attr.pattern;
|
||||
attr.$observe('pattern', function(regex) {
|
||||
if (isString(regex) && regex.length > 0) {
|
||||
regex = new RegExp('^' + regex + '$');
|
||||
}
|
||||
|
||||
if (regex && !regex.test) {
|
||||
throw minErr('ngPattern')('noregexp',
|
||||
'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
|
||||
regex, startingTag(elm));
|
||||
}
|
||||
|
||||
regexp = regex || undefined;
|
||||
ctrl.$validate();
|
||||
});
|
||||
|
||||
ctrl.$validators.pattern = function(value) {
|
||||
return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var maxlengthDirective = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
link: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
|
||||
var maxlength = -1;
|
||||
attr.$observe('maxlength', function(value) {
|
||||
var intVal = int(value);
|
||||
maxlength = isNaN(intVal) ? -1 : intVal;
|
||||
ctrl.$validate();
|
||||
});
|
||||
ctrl.$validators.maxlength = function(modelValue, viewValue) {
|
||||
return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength);
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var minlengthDirective = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngModel',
|
||||
link: function(scope, elm, attr, ctrl) {
|
||||
if (!ctrl) return;
|
||||
|
||||
var minlength = 0;
|
||||
attr.$observe('minlength', function(value) {
|
||||
minlength = int(value) || 0;
|
||||
ctrl.$validate();
|
||||
});
|
||||
ctrl.$validators.minlength = function(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
1575
test/ng/directive/ngModelSpec.js
Normal file
1575
test/ng/directive/ngModelSpec.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user