docs(bike-shed-migration): convert guide <doc:...> examples to <example>...

This CL also contains style fixes as the converted scripts caused jshint to complain.
This commit is contained in:
Caitlin Potter
2014-02-15 20:19:10 -05:00
committed by Peter Bacon Darwin
parent 896e34689d
commit f7fad29fd9
7 changed files with 556 additions and 552 deletions

View File

@@ -387,120 +387,116 @@ redirect to regular / hashbang url, as this conversion happens only during parsi
= on page reload.
In this examples we use `<base href="/base/index.html" />`
<doc:example>
<doc:source source="false">
<example>
<file name="index.html">
<div id="html5-mode" ng-controller="Html5Cntl">
<h3>Browser with History API</h3>
<div ng-address-bar browser="html5"></div><br><br>
$location.protocol() = {{$location.protocol()}}<br>
$location.host() = {{$location.host()}}<br>
$location.port() = {{$location.port()}}<br>
$location.path() = {{$location.path()}}<br>
$location.search() = {{$location.search()}}<br>
$location.hash() = {{$location.hash()}}<br>
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
<a href="/other-base/another?search">external</a>
</div>
<div ng-non-bindable class="html5-hashbang-example">
<div id="html5-mode" ng-controller="Html5Cntl">
<h3>Browser with History API</h3>
<div ng-address-bar browser="html5"></div><br><br>
$location.protocol() = {{$location.protocol()}}<br>
$location.host() = {{$location.host()}}<br>
$location.port() = {{$location.port()}}<br>
$location.path() = {{$location.path()}}<br>
$location.search() = {{$location.search()}}<br>
$location.hash() = {{$location.hash()}}<br>
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
<a href="/other-base/another?search">external</a>
</div>
<div id="hashbang-mode" ng-controller="HashbangCntl">
<h3>Browser without History API</h3>
<div ng-address-bar browser="hashbang"></div><br><br>
$location.protocol() = {{$location.protocol()}}<br>
$location.host() = {{$location.host()}}<br>
$location.port() = {{$location.port()}}<br>
$location.path() = {{$location.path()}}<br>
$location.search() = {{$location.search()}}<br>
$location.hash() = {{$location.hash()}}<br>
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
<a href="/other-base/another?search">external</a>
</div>
</file>
<div id="hashbang-mode" ng-controller="HashbangCntl">
<h3>Browser without History API</h3>
<div ng-address-bar browser="hashbang"></div><br><br>
$location.protocol() = {{$location.protocol()}}<br>
$location.host() = {{$location.host()}}<br>
$location.port() = {{$location.port()}}<br>
$location.path() = {{$location.path()}}<br>
$location.search() = {{$location.search()}}<br>
$location.hash() = {{$location.hash()}}<br>
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
<a href="/other-base/another?search">external</a>
</div>
</div>
<file name="script.js">
function FakeBrowser(initUrl, baseHref) {
this.onUrlChange = function(fn) {
this.urlChange = fn;
};
<script>
function FakeBrowser(initUrl, baseHref) {
this.onUrlChange = function(fn) {
this.urlChange = fn;
this.url = function() {
return initUrl;
};
this.defer = function(fn, delay) {
setTimeout(function() { fn(); }, delay || 0);
};
this.baseHref = function() {
return baseHref;
};
this.notifyWhenOutstandingRequests = angular.noop;
}
var browsers = {
html5: new FakeBrowser('http://www.example.com/base/path?a=b#h', '/base/index.html'),
hashbang: new FakeBrowser('http://www.example.com/base/index.html#!/path?a=b#h', '/base/index.html')
};
this.url = function() {
return initUrl;
};
function Html5Cntl($scope, $location) {
$scope.$location = $location;
}
this.defer = function(fn, delay) {
setTimeout(function() { fn(); }, delay || 0);
};
function HashbangCntl($scope, $location) {
$scope.$location = $location;
}
this.baseHref = function() {
return baseHref;
};
function initEnv(name) {
var root = angular.element(document.getElementById(name + '-mode'));
// We must kill a link to the injector for this element otherwise angular will
// complain that it has been bootstrapped already.
root.data('$injector', null);
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
$locationProvider.html5Mode(true).hashPrefix('!');
this.notifyWhenOutstandingRequests = angular.noop;
}
$provide.value('$browser', browsers[name]);
$provide.value('$sniffer', {history: name == 'html5'});
var browsers = {
html5: new FakeBrowser('http://www.example.com/base/path?a=b#h', '/base/index.html'),
hashbang: new FakeBrowser('http://www.example.com/base/index.html#!/path?a=b#h', '/base/index.html')
};
$compileProvider.directive('ngAddressBar', function() {
return function(scope, elm, attrs) {
var browser = browsers[attrs.browser],
input = angular.element('<input type="text" style="width: 400px">').val(browser.url()),
delay;
function Html5Cntl($scope, $location) {
$scope.$location = $location;
}
input.on('keypress keyup keydown', function() {
if (!delay) {
delay = setTimeout(fireUrlChange, 250);
}
});
function HashbangCntl($scope, $location) {
$scope.$location = $location;
}
browser.url = function(url) {
return input.val(url);
};
function initEnv(name) {
var root = angular.element(document.getElementById(name + '-mode'));
// We must kill a link to the injector for this element otherwise angular will
// complain that it has been bootstrapped already.
root.data('$injector', null);
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
$locationProvider.html5Mode(true).hashPrefix('!');
elm.append('Address: ').append(input);
$provide.value('$browser', browsers[name]);
$provide.value('$sniffer', {history: name == 'html5'});
$compileProvider.directive('ngAddressBar', function() {
return function(scope, elm, attrs) {
var browser = browsers[attrs.browser],
input = angular.element('<input type="text" style="width: 400px">').val(browser.url()),
delay;
input.on('keypress keyup keydown', function() {
if (!delay) {
delay = setTimeout(fireUrlChange, 250);
function fireUrlChange() {
delay = null;
browser.urlChange(input.val());
}
});
browser.url = function(url) {
return input.val(url);
};
elm.append('Address: ').append(input);
function fireUrlChange() {
delay = null;
browser.urlChange(input.val());
}
};
});
}]);
root.on('click', function(e) {
e.stopPropagation();
});
}]);
root.on('click', function(e) {
e.stopPropagation();
});
}
}
initEnv('html5');
initEnv('hashbang');
</script>
</doc:source>
</doc:example>
initEnv('html5');
initEnv('hashbang');
</file>
</example>
# Caveats

View File

@@ -28,45 +28,46 @@ function myController($loc, $log) {
myController.$inject = ['$location', '$log'];
```
<doc:example module="MyServiceModule">
<doc:source>
<script>
angular.
module('MyServiceModule', []).
factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
<example module="MyServiceModule">
<file name="index.html">
<div id="simple" ng-controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
function myController(scope, notifyService) {
scope.callNotify = function(msg) {
notifyService(msg);
};
}
<file name="script.js">
angular.
module('MyServiceModule', []).
factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
myController.$inject = ['$scope','notify'];
</script>
function myController(scope, notifyService) {
scope.callNotify = function(msg) {
notifyService(msg);
};
}
<div id="simple" ng-controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</doc:source>
<doc:protractor>
it('should test service', function() {
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
.toEqual('test');
});
</doc:protractor>
</doc:example>
myController.$inject = ['$scope','notify'];
</file>
<file name="protractor.js" type="protractor">
it('should test service', function() {
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
.toEqual('test');
});
</file>
</example>
## Implicit Dependency Injection
@@ -74,36 +75,37 @@ A new feature of Angular DI allows it to determine the dependency from the name
Let's rewrite the above example to show the use of this implicit dependency injection of
`$window`, `$scope`, and our `notify` service:
<doc:example module="MyServiceModuleDI">
<doc:source>
<script>
angular.
module('MyServiceModuleDI', []).
factory('notify', function($window) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
$window.alert(msgs.join("\n"));
msgs = [];
}
};
});
<example module="MyServiceModuleDI">
<file name="index.html">
<div id="implicit" ng-controller="myController">
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</file>
function myController($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
}
</script>
<div id="implicit" ng-controller="myController">
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</doc:source>
</doc:example>
<file name="script.js">
angular.
module('MyServiceModuleDI', []).
factory('notify', function($window) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
$window.alert(msgs.join("\n"));
msgs = [];
}
};
});
function myController($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
}
</file>
</example>
However, if you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming)) your
code, your variable names will get renamed in which case you will still need to explicitly specify

View File

@@ -33,55 +33,58 @@ controller method and call the method. If you want to `eval()` an angular expres
JavaScript, use the {@link ng.$rootScope.Scope#methods_$eval `$eval()`} method.
## Example
<doc:example>
<doc:source>
1+2={{1+2}}
</doc:source>
<doc:protractor>
it('should calculate expression in binding', function() {
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
});
</doc:protractor>
</doc:example>
<example>
<file name="index.html">
1+2={{1+2}}
</file>
<file name="protractor.js" type="protractor">
it('should calculate expression in binding', function() {
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
});
</file>
</example>
You can try evaluating different expressions here:
<doc:example>
<doc:source>
<script>
function Cntl2($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
<example>
<file name="index.html">
<div ng-controller="Cntl2" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
</file>
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}
</script>
<div ng-controller="Cntl2" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
</doc:source>
<doc:protractor>
it('should allow user expression testing', function() {
element(by.css('.expressions button')).click();
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
expect(lis.count()).toBe(1);
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
});
</doc:protractor>
</doc:example>
<file name="script.js">
function Cntl2($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}
</file>
<file name="protractor.js" type="protractor">
it('should allow user expression testing', function() {
element(by.css('.expressions button')).click();
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
expect(lis.count()).toBe(1);
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
});
</file>
</example>
# Property Evaluation
@@ -92,38 +95,40 @@ to global window properties, Angular expressions have to use {@link ng.$window
defined on `window`, in an expression you must use `$window.alert()`. This is done intentionally to
prevent accidental access to the global state (a common source of subtle bugs).
<doc:example>
<doc:source>
<script>
function Cntl1($window, $scope){
$scope.name = 'World';
<example>
<file name="index.html">
<div class="example2" ng-controller="Cntl1">
Name: <input ng-model="name" type="text"/>
<button ng-click="greet()">Greet</button>
</div>
</file>
$scope.greet = function() {
$window.alert('Hello ' + $scope.name);
}
}
</script>
<div class="example2" ng-controller="Cntl1">
Name: <input ng-model="name" type="text"/>
<button ng-click="greet()">Greet</button>
</div>
</doc:source>
<doc:protractor>
it('should calculate expression in binding', function() {
if (browser.params.browser = 'safari') {
// Safari can't handle dialogs.
return;
};
element(by.css('[ng-click="greet()"]')).click();
<file name="script.js">
function Cntl1($window, $scope){
$scope.name = 'World';
var alertDialog = browser.switchTo().alert();
$scope.greet = function() {
$window.alert('Hello ' + $scope.name);
};
}
</file>
expect(alertDialog.getText()).toEqual('Hello World');
<file name="protractor.js" type="protractor">
it('should calculate expression in binding', function() {
if (browser.params.browser == 'safari') {
// Safari can't handle dialogs.
return;
}
element(by.css('[ng-click="greet()"]')).click();
alertDialog.accept();
});
</doc:protractor>
</doc:example>
var alertDialog = browser.switchTo().alert();
expect(alertDialog.getText()).toEqual('Hello World');
alertDialog.accept();
});
</file>
</example>
## Forgiving

View File

@@ -47,35 +47,35 @@ The example below therefore calls the filter directly in the controller.
By this, the controller is able to call the filter only when needed (e.g. when the data is loaded from the backend
or the filter expression is changed).
<doc:example module="FilterInControllerModule">
<doc:source>
<script>
angular.module('FilterInControllerModule', []).
controller('FilterController', ['filterFilter', function(filterFilter) {
this.array = [
{name: 'Tobias'},
{name: 'Jeff'},
{name: 'Brian'},
{name: 'Igor'},
{name: 'James'},
{name: 'Brad'}
];
this.filteredArray = filterFilter(this.array, 'a');
}]);
</script>
<example module="FilterInControllerModule">
<file name="index.html">
<div ng-controller="FilterController as ctrl">
<div>
All entries:
<span ng-repeat="entry in ctrl.array">{{entry.name}} </span>
</div>
<div>
Entries that contain an "a":
<span ng-repeat="entry in ctrl.filteredArray">{{entry.name}} </span>
</div>
</div>
</file>
<div ng-controller="FilterController as ctrl">
<div>
All entries:
<span ng-repeat="entry in ctrl.array">{{entry.name}} </span>
</div>
<div>
Entries that contain an "a":
<span ng-repeat="entry in ctrl.filteredArray">{{entry.name}} </span>
</div>
</div>
</doc:source>
</doc:example>
<file name="script.js">
angular.module('FilterInControllerModule', []).
controller('FilterController', ['filterFilter', function(filterFilter) {
this.array = [
{name: 'Tobias'},
{name: 'Jeff'},
{name: 'Brian'},
{name: 'Igor'},
{name: 'James'},
{name: 'Brad'}
];
this.filteredArray = filterFilter(this.array, 'a');
}]);
</file>
</example>
## Creating custom filters
@@ -89,37 +89,37 @@ function.
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
<doc:example module="MyReverseModule">
<doc:source>
<script>
angular.module('MyReverseModule', []).
filter('reverse', function() {
return function(input, uppercase) {
var out = "";
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
// conditional based on optional argument
if (uppercase) {
out = out.toUpperCase();
}
return out;
}
});
<example module="MyReverseModule">
<file name="index.html">
<div ng-controller="Ctrl">
<input ng-model="greeting" type="greeting"><br>
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
Reverse + uppercase: {{greeting|reverse:true}}<br>
</div>
</file>
function Ctrl($scope) {
$scope.greeting = 'hello';
}
</script>
<file name="script.js">
angular.module('MyReverseModule', []).
filter('reverse', function() {
return function(input, uppercase) {
var out = "";
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
// conditional based on optional argument
if (uppercase) {
out = out.toUpperCase();
}
return out;
};
});
<div ng-controller="Ctrl">
<input ng-model="greeting" type="greeting"><br>
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
Reverse + uppercase: {{greeting|reverse:true}}<br>
</div>
</doc:source>
</doc:example>
function Ctrl($scope) {
$scope.greeting = 'hello';
}
</file>
</example>
## Testing custom filters

View File

@@ -16,38 +16,38 @@ The key directive in understanding two-way data-binding is {@link ng.directive:n
The `ngModel` directive provides the two-way data-binding by synchronizing the model to the view, as well as view to the model.
In addition it provides an {@link ngModel.NgModelController API} for other directives to augment its behavior.
<doc:example>
<doc:source>
<div ng-controller="Controller">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<example>
<file name="index.html">
<div ng-controller="Controller">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<script>
function Controller($scope) {
$scope.master = {};
<script>
function Controller($scope) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
}
</script>
</doc:source>
</doc:example>
$scope.reset();
}
</script>
</file>
</example>
Note that `novalidate` is used to disable browser's native form validation.
@@ -67,47 +67,47 @@ The following example uses the CSS to display validity of each form control.
In the example both `user.name` and `user.email` are required, but are rendered with red background only when they are dirty.
This ensures that the user is not distracted with an error until after interacting with the control, and failing to satisfy its validity.
<doc:example>
<doc:source>
<div ng-controller="Controller">
<form novalidate class="css-form">
Name:
<input type="text" ng-model="user.name" required /><br />
E-mail: <input type="email" ng-model="user.email" required /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
</div>
<example>
<file name="index.html">
<div ng-controller="Controller">
<form novalidate class="css-form">
Name:
<input type="text" ng-model="user.name" required /><br />
E-mail: <input type="email" ng-model="user.email" required /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
</div>
<style type="text/css">
.css-form input.ng-invalid.ng-dirty {
background-color: #FA787E;
}
<style type="text/css">
.css-form input.ng-invalid.ng-dirty {
background-color: #FA787E;
}
.css-form input.ng-valid.ng-dirty {
background-color: #78FA89;
}
</style>
.css-form input.ng-valid.ng-dirty {
background-color: #78FA89;
}
</style>
<script>
function Controller($scope) {
$scope.master = {};
<script>
function Controller($scope) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset();
}
</script>
</doc:source>
</doc:example>
$scope.reset();
}
</script>
</file>
</example>
@@ -130,54 +130,54 @@ This allows us to extend the above example with these features:
- SAVE button is enabled only if form has some changes and is valid
- custom error messages for `user.email` and `user.agree`
<doc:example>
<doc:source>
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
Name:
<input type="text" ng-model="user.name" name="uName" required /><br />
E-mail:
<input type="email" ng-model="user.email" name="uEmail" required/><br />
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
<example>
<file name="index.html">
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
Name:
<input type="text" ng-model="user.name" name="uName" required /><br />
E-mail:
<input type="email" ng-model="user.email" name="uEmail" required/><br />
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
</div>
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
required /><br />
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
<button ng-click="update(user)"
ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
</form>
</div>
</file>
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<file name="script.js">
function Controller($scope) {
$scope.master = {};
<input type="checkbox" ng-model="user.agree" name="userAgree" required />
I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
required /><br />
<div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
<button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
<button ng-click="update(user)"
ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
</form>
</div>
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
<script>
function Controller($scope) {
$scope.master = {};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
$scope.reset();
}
</script>
</doc:source>
</doc:example>
$scope.reset();
}
</file>
</example>
@@ -209,72 +209,72 @@ In the following example we create two directives.
Note that we can't use input type `number` here as HTML5 browsers would not allow the user to type what it would consider an invalid number such as `1,2`.
<doc:example module="form-example1">
<doc:source>
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
<example module="form-example1">
<file name="index.html">
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</div>
</file>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</div>
<file name="script.js">
var app = angular.module('form-example1', []);
<script>
var app = angular.module('form-example1', []);
var INTEGER_REGEXP = /^\-?\d+$/;
app.directive('integer', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
ctrl.$setValidity('integer', true);
return viewValue;
} else {
// it is invalid, return undefined (no model update)
ctrl.$setValidity('integer', false);
return undefined;
}
});
}
};
});
var INTEGER_REGEXP = /^\-?\d+$/;
app.directive('integer', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
ctrl.$setValidity('integer', true);
return viewValue;
} else {
// it is invalid, return undefined (no model update)
ctrl.$setValidity('integer', false);
return undefined;
}
});
}
};
});
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
app.directive('smartFloat', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (FLOAT_REGEXP.test(viewValue)) {
ctrl.$setValidity('float', true);
return parseFloat(viewValue.replace(',', '.'));
} else {
ctrl.$setValidity('float', false);
return undefined;
}
});
}
};
});
</script>
</doc:source>
</doc:example>
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
app.directive('smartFloat', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (FLOAT_REGEXP.test(viewValue)) {
ctrl.$setValidity('float', true);
return parseFloat(viewValue.replace(',', '.'));
} else {
ctrl.$setValidity('float', false);
return undefined;
}
});
}
};
});
</file>
</example>
# Implementing custom form controls (using `ngModel`)
@@ -290,40 +290,40 @@ See {@link guide/directive $compileProvider.directive} for more info.
The following example shows how to add two-way data-binding to contentEditable elements.
<doc:example module="form-example2">
<doc:source>
<script>
angular.module('form-example2', []).directive('contenteditable', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.on('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(elm.html());
});
});
<example module="form-example2">
<file name="index.html">
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
<pre>model = {{content}}</pre>
// model -> view
ctrl.$render = function() {
elm.html(ctrl.$viewValue);
};
// load init value from DOM
ctrl.$setViewValue(elm.html());
<style type="text/css">
div[contentEditable] {
cursor: pointer;
background-color: #D0D0D0;
}
};
});
</script>
</style>
</file>
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
<pre>model = {{content}}</pre>
<file name="script.js">
angular.module('form-example2', []).directive('contenteditable', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.on('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(elm.html());
});
});
<style type="text/css">
div[contentEditable] {
cursor: pointer;
background-color: #D0D0D0;
}
</style>
</doc:source>
</doc:example>
// model -> view
ctrl.$render = function() {
elm.html(ctrl.$viewValue);
};
// load init value from DOM
ctrl.$setViewValue(elm.html());
}
};
});
</file>
</example>

View File

@@ -27,7 +27,7 @@ To make your Angular application work on IE please make sure that:
[JSON2](https://github.com/douglascrockford/JSON-js) or
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
```html
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
<head>
@@ -43,7 +43,7 @@ To make your Angular application work on IE please make sure that:
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
```html
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
...
@@ -55,7 +55,7 @@ To make your Angular application work on IE please make sure that:
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
```html
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
<head>

View File

@@ -26,26 +26,26 @@ Important things to notice:
* Notice the reference to the `myApp` module in the `<html ng-app="myApp">`, it is what
bootstraps the app using your module.
<doc:example module='myApp'>
<doc:source>
<script>
// declare a module
var myAppModule = angular.module('myApp', []);
// configure the module.
// in this example we will create a greeting filter
myAppModule.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
};
});
</script>
<example module='myApp'>
<file name="index.html">
<div>
{{ 'World' | greet }}
</div>
</doc:source>
</doc:example>
</file>
<file name="script.js">
// declare a module
var myAppModule = angular.module('myApp', []);
// configure the module.
// in this example we will create a greeting filter
myAppModule.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
};
});
</file>
</example>
@@ -67,49 +67,50 @@ that are relevant to tests.
The above is only a suggestion, so feel free to tailor it to your needs.
<doc:example module='xmpl'>
<doc:source>
<script>
angular.module('xmpl.service', []).
value('greeter', {
salutation: 'Hello',
localize: function(localization) {
this.salutation = localization.salutation;
},
greet: function(name) {
return this.salutation + ' ' + name + '!';
}
}).
value('user', {
load: function(name) {
this.name = name;
}
});
angular.module('xmpl.directive', []);
angular.module('xmpl.filter', []);
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
run(function(greeter, user) {
// This is effectively part of the main method initialization code
greeter.localize({
salutation: 'Bonjour'
});
user.load('World');
})
// A Controller for your app
var XmplController = function($scope, greeter, user) {
$scope.greeting = greeter.greet(user.name);
}
</script>
<example module='xmpl'>
<file name="index.html">
<div ng-controller="XmplController">
{{ greeting }}!
</div>
</doc:source>
</doc:example>
</file>
<file name="script.js">
angular.module('xmpl.service', []).
value('greeter', {
salutation: 'Hello',
localize: function(localization) {
this.salutation = localization.salutation;
},
greet: function(name) {
return this.salutation + ' ' + name + '!';
}
}).
value('user', {
load: function(name) {
this.name = name;
}
});
angular.module('xmpl.directive', []);
angular.module('xmpl.filter', []);
angular.module('xmpl', ['xmpl.service', 'xmpl.directive', 'xmpl.filter']).
run(function(greeter, user) {
// This is effectively part of the main method initialization code
greeter.localize({
salutation: 'Bonjour'
});
user.load('World');
});
// A Controller for your app
var XmplController = function($scope, greeter, user) {
$scope.greeting = greeter.greet(user.name);
};
</file>
</example>