mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-18 11:31:07 +00:00
doc(NgModelController) add example and $render documentation
Closes#930
This commit is contained in:
parent
073e76f835
commit
8024a5742c
3 changed files with 107 additions and 10 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
@ngdoc overview
|
@ngdoc overview
|
||||||
@name Developer Guide: Forms
|
@name Forms
|
||||||
@description
|
@description
|
||||||
|
|
||||||
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
||||||
|
|
@ -628,6 +628,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||||
/**
|
/**
|
||||||
* @ngdoc directive
|
* @ngdoc directive
|
||||||
* @name angular.module.ng.$compileProvider.directive.textarea
|
* @name angular.module.ng.$compileProvider.directive.textarea
|
||||||
|
* @restrict E
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* HTML textarea element control with angular data-binding. The data-binding and validation
|
* HTML textarea element control with angular data-binding. The data-binding and validation
|
||||||
|
|
@ -782,6 +783,79 @@ var VALID_CLASS = 'ng-valid',
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
|
* `NgModelController` provides API for the `ng-model` directive. The controller contains
|
||||||
|
* services for data-binding, validation, CSS update, value formatting and parsing. It
|
||||||
|
* specifically does not contain any logic which deals with DOM rendering or listening to
|
||||||
|
* DOM events. The `NgModelController` is meant to be extended by other directives where, the
|
||||||
|
* directive provides DOM manipulation and the `NgModelController` provides the data-binding.
|
||||||
|
*
|
||||||
|
* This example shows how to use `NgModelController` with a custom control to achieve
|
||||||
|
* data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
|
||||||
|
* collaborate together to achieve the desired result.
|
||||||
|
*
|
||||||
|
* <example module="customControl">
|
||||||
|
<file name="style.css">
|
||||||
|
[contenteditable] {
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: white;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ng-invalid {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
</file>
|
||||||
|
<file name="script.js">
|
||||||
|
angular.module('customControl', []).
|
||||||
|
directive('contenteditable', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A', // only activate on element attribute
|
||||||
|
require: '?ngModel', // get a hold of NgModelController
|
||||||
|
link: function(scope, element, attrs, ngModel) {
|
||||||
|
if(!ngModel) return; // do nothing if no ng-model
|
||||||
|
|
||||||
|
// Specify how UI should be updated
|
||||||
|
ngModel.$render = function() {
|
||||||
|
element.html(ngModel.$viewValue || '');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for change events to enable binding
|
||||||
|
element.bind('blur keyup change', function() {
|
||||||
|
scope.$apply(read);
|
||||||
|
});
|
||||||
|
read(); // initialize
|
||||||
|
|
||||||
|
// Write data to the model
|
||||||
|
function read() {
|
||||||
|
ngModel.$setViewValue(element.html());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</file>
|
||||||
|
<file name="index.html">
|
||||||
|
<form name="myForm">
|
||||||
|
<div contenteditable
|
||||||
|
name="myWidget" ng-model="userContent"
|
||||||
|
required>Change me!</div>
|
||||||
|
<span ng-show="myForm.myWidget.$error.required">Required!</span>
|
||||||
|
<hr>
|
||||||
|
<textarea ng-model="userContent"></textarea>
|
||||||
|
</form>
|
||||||
|
</file>
|
||||||
|
<file name="scenario.js">
|
||||||
|
it('should data-bind and become invalid', function() {
|
||||||
|
var contentEditable = element('[contenteditable]');
|
||||||
|
|
||||||
|
expect(contentEditable.text()).toEqual('Change me!');
|
||||||
|
input('userContent').enter('');
|
||||||
|
expect(contentEditable.text()).toEqual('');
|
||||||
|
expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
|
||||||
|
});
|
||||||
|
</file>
|
||||||
|
* </example>
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$element',
|
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$element',
|
||||||
function($scope, $exceptionHandler, $attr, ngModel, $element) {
|
function($scope, $exceptionHandler, $attr, ngModel, $element) {
|
||||||
|
|
@ -794,9 +868,19 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e
|
||||||
this.$dirty = false;
|
this.$dirty = false;
|
||||||
this.$valid = true;
|
this.$valid = true;
|
||||||
this.$invalid = false;
|
this.$invalid = false;
|
||||||
this.$render = noop;
|
|
||||||
this.$name = $attr.name;
|
this.$name = $attr.name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name angular.module.ng.$compileProvider.directive.ngModel.NgModelController#$render
|
||||||
|
* @methodOf angular.module.ng.$compileProvider.directive.ngModel.NgModelController
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Called when the view needs to be updated. It is expected that the user of the ng-model
|
||||||
|
* directive will implement this method.
|
||||||
|
*/
|
||||||
|
this.$render = noop;
|
||||||
|
|
||||||
var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
|
var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
|
||||||
invalidCount = 0, // used to easily determine if we are valid
|
invalidCount = 0, // used to easily determine if we are valid
|
||||||
$error = this.$error = {}; // keep invalid keys here
|
$error = this.$error = {}; // keep invalid keys here
|
||||||
|
|
@ -958,7 +1042,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', 'ngModel', '$e
|
||||||
* - {@link angular.module.ng.$compileProvider.directive.textarea textarea}
|
* - {@link angular.module.ng.$compileProvider.directive.textarea textarea}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var ngModelDirective = [function() {
|
var ngModelDirective = function() {
|
||||||
return {
|
return {
|
||||||
inject: {
|
inject: {
|
||||||
ngModel: 'accessor'
|
ngModel: 'accessor'
|
||||||
|
|
@ -978,7 +1062,7 @@ var ngModelDirective = [function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}];
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1039,11 +1123,12 @@ var ngChangeDirective = valueFn({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var requiredDirective = [function() {
|
var requiredDirective = function() {
|
||||||
return {
|
return {
|
||||||
require: '?ngModel',
|
require: '?ngModel',
|
||||||
link: function(scope, elm, attr, ctrl) {
|
link: function(scope, elm, attr, ctrl) {
|
||||||
if (!ctrl) return;
|
if (!ctrl) return;
|
||||||
|
attr.required = true; // force truthy in case we are on non input element
|
||||||
|
|
||||||
var validator = function(value) {
|
var validator = function(value) {
|
||||||
if (attr.required && (isEmpty(value) || value === false)) {
|
if (attr.required && (isEmpty(value) || value === false)) {
|
||||||
|
|
@ -1063,7 +1148,7 @@ var requiredDirective = [function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}];
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1144,7 +1229,7 @@ var ngListDirective = function() {
|
||||||
|
|
||||||
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
||||||
|
|
||||||
var ngValueDirective = [function() {
|
var ngValueDirective = function() {
|
||||||
return {
|
return {
|
||||||
priority: 100,
|
priority: 100,
|
||||||
compile: function(tpl, tplAttr) {
|
compile: function(tpl, tplAttr) {
|
||||||
|
|
@ -1162,4 +1247,4 @@ var ngValueDirective = [function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}];
|
};
|
||||||
|
|
|
||||||
|
|
@ -285,8 +285,9 @@ describe('input', function() {
|
||||||
var formElm, inputElm, scope, $compile, changeInputValueTo;
|
var formElm, inputElm, scope, $compile, changeInputValueTo;
|
||||||
|
|
||||||
function compileInput(inputHtml) {
|
function compileInput(inputHtml) {
|
||||||
formElm = jqLite('<form name="form">' + inputHtml + '</form>');
|
inputElm = jqLite(inputHtml);
|
||||||
inputElm = formElm.find('input');
|
formElm = jqLite('<form name="form"></form>');
|
||||||
|
formElm.append(inputElm);
|
||||||
$compile(formElm)(scope);
|
$compile(formElm)(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -633,6 +634,17 @@ describe('input', function() {
|
||||||
expect(inputElm.val()).toBe('0')
|
expect(inputElm.val()).toBe('0')
|
||||||
expect(scope.form.alias.$error.required).toBeFalsy();
|
expect(scope.form.alias.$error.required).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should register required on non boolean elements', function() {
|
||||||
|
compileInput('<div ng-model="value" name="alias" required>');
|
||||||
|
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.value = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(inputElm).toBeInvalid();
|
||||||
|
expect(scope.form.alias.$error.required).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue