Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 923b6aba0d | |||
| 955e20eb61 | |||
| 286a40751c | |||
| eca0535457 | |||
| 7ace77a5d7 | |||
| 7f362af153 | |||
| 35dee2abac | |||
| 29c926201d | |||
| 9b6852a8c9 | |||
| 53efc8d5d0 | |||
| 0b8461c9cb | |||
| abd8e2a9eb | |||
| bc4dadc894 | |||
| ec53089bb1 | |||
| 7bb50e2823 | |||
| 632b2ddd34 | |||
| 7caad2205a | |||
| 9bf5f89659 | |||
| f41ca4a53e | |||
| 0bcd0872d8 | |||
| 6ec5946094 | |||
| 784ea8e160 | |||
| 6ec53bdfd3 | |||
| d1b6480dcf | |||
| ef6fed3ef8 | |||
| 473dee5786 | |||
| 779e3f6b5f | |||
| 71bca00651 | |||
| 9a9fce0abc | |||
| 939ca37cfe | |||
| 4ae8a2a4b6 | |||
| 113d3954b9 | |||
| 7cb5983750 | |||
| 2b149ca6d4 | |||
| e77866c18c | |||
| 0dc6418d20 | |||
| 837a077578 | |||
| adf91fe6ee | |||
| dea1c0d34c | |||
| f533acc9aa | |||
| d01cae2a0d | |||
| d015c8a80b | |||
| 8d2717146b | |||
| 3d598dae64 | |||
| 0a58986f52 | |||
| 6e69b85f9a | |||
| 7a9e336028 | |||
| 9b8df52aa9 | |||
| 7fab29fbe1 | |||
| 1a47fcbb8b | |||
| ffd4dab611 | |||
| 13edaa95c7 | |||
| 47b1f54bba | |||
| cdc7280dd3 | |||
| 0bb282bc6d | |||
| fdb09ef858 | |||
| 5e69cb2f9f | |||
| 5c2da38e3f | |||
| 3a799df0f1 | |||
| 511c765a44 | |||
| bf55d76d27 | |||
| c9efc80cd0 | |||
| fa15f2a6df | |||
| c023a0bfbb | |||
| b470e005e8 | |||
| c959191882 | |||
| e4adebd07a | |||
| ac94f6125f | |||
| c5686c5271 | |||
| 7fdb54d12b | |||
| 869008140a | |||
| 26ee32ec79 | |||
| a06193f97b | |||
| ba9dee170c | |||
| d4b60ada1e | |||
| b839f73ad0 | |||
| aee32931fd | |||
| 7ee5f46bbc | |||
| 2b97854bf4 | |||
| 316ee8f7ca | |||
| 2e18f44fcd | |||
| c85d064ecf | |||
| 7b9b82281a | |||
| aab632b330 | |||
| 4c8d8ad508 | |||
| 3a8f3dc9ea | |||
| c139e68d99 |
@@ -1,5 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# JS files must always use LF for tools to work
|
||||
*.js eol=lf
|
||||
@@ -1,27 +0,0 @@
|
||||
***Note*: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.**
|
||||
|
||||
**Do you want to request a *feature* or report a *bug*?**
|
||||
|
||||
|
||||
|
||||
**What is the current behavior?**
|
||||
|
||||
|
||||
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).**
|
||||
|
||||
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
|
||||
|
||||
|
||||
**Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.**
|
||||
|
||||
|
||||
|
||||
**Other information (e.g. stacktraces, related issues, suggestions how to fix)**
|
||||
@@ -1,23 +0,0 @@
|
||||
**What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)**
|
||||
|
||||
|
||||
|
||||
**What is the current behavior? (You can also link to an open issue here)**
|
||||
|
||||
|
||||
|
||||
**What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?**
|
||||
|
||||
|
||||
|
||||
**Please check if the PR fulfills these requirements**
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
**Other information**:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// This is an incomplete TODO list of checks we want to start enforcing
|
||||
//
|
||||
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
|
||||
// that correct the existing code base issues and make the new check pass.
|
||||
|
||||
{
|
||||
"validateParameterSeparator": ", ", // Re-assert this rule when JSCS allows multiple spaces
|
||||
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"validateJSDoc": {
|
||||
"checkParamNames": true,
|
||||
"requireParamTypes": true
|
||||
}
|
||||
}
|
||||
+19
-28
@@ -1,13 +1,6 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.4'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- bower_components
|
||||
- docs/bower_components
|
||||
- '0.10'
|
||||
|
||||
branches:
|
||||
except:
|
||||
@@ -15,54 +8,52 @@ branches:
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- JOB=ci-checks
|
||||
- JOB=unit BROWSER_PROVIDER=saucelabs
|
||||
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
|
||||
- JOB=unit BROWSER_PROVIDER=browserstack
|
||||
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
|
||||
global:
|
||||
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- BROWSER_STACK_USERNAME=VojtaJina
|
||||
- BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
|
||||
|
||||
# node 4 likes the G++ v4.8 compiler
|
||||
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
|
||||
|
||||
install:
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true
|
||||
# - npm config set registry http://23.251.144.68
|
||||
# Disable the spinner, it looks bad on Travis
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
#- npm install -g npm@2.5
|
||||
# Install npm dependencies and ensure that npm cache is not stale
|
||||
- npm install
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
|
||||
before_script:
|
||||
- ./scripts/travis/before_build.sh
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/travis/start_browser_provider.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt package
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
|
||||
script:
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/tear_down_browser_provider.sh
|
||||
- ./scripts/travis/print_logs.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2
|
||||
- http://104.197.9.155:8484/hubot/travis/activity #hubot-server
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: always # default: false
|
||||
on_start: false # default: false
|
||||
|
||||
+50
-3004
File diff suppressed because it is too large
Load Diff
+10
-25
@@ -1,4 +1,4 @@
|
||||
# Contributing to AngularJS
|
||||
#Contributing to AngularJS
|
||||
|
||||
We'd love for you to contribute to our source code and to make AngularJS even better than it is
|
||||
today! Here are the guidelines we'd like you to follow:
|
||||
@@ -19,7 +19,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
## <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
|
||||
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc] and [Gitter][gitter].
|
||||
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc].
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
@@ -54,7 +54,7 @@ For large fixes, please build and test the documentation before submitting the P
|
||||
accidentally introduced any layout or formatting issues. You should also make sure that your commit message
|
||||
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly. When naming the commit, it is advised to still label it according to the commit guidelines below, by starting the commit message with **docs** and referencing the filename. Since this is not obvious and some changes are made on the fly, this is not strictly necessary and we will understand if this isn't done the first few times.
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly. When naming the commit, it is advised to still label it according to the commit guidelines below, by starting the commit message with **docs** and referencing the filename. Since this is not obvious and some changes are made on the fly, this is not strictly necessary and we will understand if this isn't done the first few times.
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
@@ -71,7 +71,7 @@ chances of your issue being dealt with quickly:
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or an unambiguous set of steps.
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
@@ -123,19 +123,13 @@ Before you submit your pull request consider the following guidelines:
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suite to ensure tests are still passing.
|
||||
* Commit your changes to your branch (e.g. `my-fix-branch`).
|
||||
* Push the changes to your GitHub repository (this will update your Pull Request).
|
||||
|
||||
If the PR gets too outdated we may ask you to rebase and force push to update the PR:
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push origin my-fix-branch -f
|
||||
```
|
||||
|
||||
*WARNING. Squashing or reverting commits and forced push thereafter may remove GitHub comments
|
||||
on code that were previously made by you and others in your commits.*
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
#### After your pull request is merged
|
||||
@@ -193,8 +187,6 @@ We have very precise rules over how our git commit messages can be formatted. T
|
||||
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||
we use the git commit messages to **generate the AngularJS change log**.
|
||||
|
||||
The commit message formatting can be added using a typical git workflow or through the use of a CLI wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `npm run commit` in your terminal after staging your changes in git.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
@@ -207,13 +199,8 @@ format that includes a **type**, a **scope** and a **subject**:
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
### Revert
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
to read on github as well as in various git tools.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
@@ -223,7 +210,7 @@ Must be one of the following:
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **refactor**: A code change that neither fixes a bug or adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests
|
||||
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||
@@ -240,15 +227,14 @@ The subject contains succinct description of the change:
|
||||
* don't capitalize first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
### Body
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
||||
###Body
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Footer
|
||||
###Footer
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
@@ -276,7 +262,6 @@ You can find out more detailed information about contributing in the
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[dev-doc]: https://docs.angularjs.org/guide
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[gitter]: https://gitter.im/angular/angular.js
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
|
||||
+18
-52
@@ -17,6 +17,10 @@ module.exports = function(grunt) {
|
||||
NG_VERSION.cdn = versionInfo.cdnVersion;
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
//global beforeEach
|
||||
util.init();
|
||||
|
||||
|
||||
//config
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
@@ -115,7 +119,7 @@ module.exports = function(grunt) {
|
||||
files: { src: 'test/**/*.js' },
|
||||
},
|
||||
ng: {
|
||||
files: { src: files['angularSrc'].concat('!src/angular.bind.js') },
|
||||
files: { src: files['angularSrc'] },
|
||||
},
|
||||
ngAnimate: {
|
||||
files: { src: 'src/ngAnimate/**/*.js' },
|
||||
@@ -126,9 +130,6 @@ module.exports = function(grunt) {
|
||||
ngLocale: {
|
||||
files: { src: 'src/ngLocale/**/*.js' },
|
||||
},
|
||||
ngMessageFormat: {
|
||||
files: { src: 'src/ngMessageFormat/**/*.js' },
|
||||
},
|
||||
ngMessages: {
|
||||
files: { src: 'src/ngMessages/**/*.js' },
|
||||
},
|
||||
@@ -156,13 +157,9 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!src/angular.bind.js' // we ignore this file since contains an early return statement
|
||||
],
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: '.jscsrc'
|
||||
config: ".jscs.json"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -207,10 +204,6 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(files['angularModules']['ngResource'], 'module')
|
||||
},
|
||||
messageformat: {
|
||||
dest: 'build/angular-message-format.js',
|
||||
src: util.wrap(files['angularModules']['ngMessageFormat'], 'module')
|
||||
},
|
||||
messages: {
|
||||
dest: 'build/angular-messages.js',
|
||||
src: util.wrap(files['angularModules']['ngMessages'], 'module')
|
||||
@@ -231,9 +224,9 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-aria.js',
|
||||
src: util.wrap(files['angularModules']['ngAria'], 'module')
|
||||
},
|
||||
'promises-aplus-adapter': {
|
||||
"promises-aplus-adapter": {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
}
|
||||
},
|
||||
|
||||
@@ -243,7 +236,6 @@ module.exports = function(grunt) {
|
||||
animate: 'build/angular-animate.js',
|
||||
cookies: 'build/angular-cookies.js',
|
||||
loader: 'build/angular-loader.js',
|
||||
messageformat: 'build/angular-message-format.js',
|
||||
messages: 'build/angular-messages.js',
|
||||
touch: 'build/angular-touch.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
@@ -253,33 +245,17 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
|
||||
'ddescribe-iit': {
|
||||
"ddescribe-iit": {
|
||||
files: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/ngScenario/DescribeSpec.js',
|
||||
'!src/ng/directive/attrs.js', // legitimate xit here
|
||||
'!src/ngScenario/**/*.js',
|
||||
'!test/helpers/privateMocks*.js'
|
||||
],
|
||||
options: {
|
||||
disallowed: [
|
||||
'fit',
|
||||
'iit',
|
||||
'xit',
|
||||
'fthey',
|
||||
'tthey',
|
||||
'xthey',
|
||||
'fdescribe',
|
||||
'ddescribe',
|
||||
'xdescribe',
|
||||
'it.only',
|
||||
'describe.only'
|
||||
]
|
||||
}
|
||||
'!src/ngScenario/**/*.js'
|
||||
]
|
||||
},
|
||||
|
||||
'merge-conflict': {
|
||||
"merge-conflict": {
|
||||
files: [
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
@@ -309,11 +285,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
shell: {
|
||||
'npm-install': {
|
||||
command: 'node scripts/npm/check-node-modules.js'
|
||||
},
|
||||
|
||||
'promises-aplus-tests': {
|
||||
"promises-aplus-tests": {
|
||||
options: {
|
||||
stdout: false,
|
||||
stderr: true,
|
||||
@@ -339,15 +311,9 @@ module.exports = function(grunt) {
|
||||
}
|
||||
});
|
||||
|
||||
// global beforeEach task
|
||||
if (!process.env.TRAVIS) {
|
||||
grunt.task.run('shell:npm-install');
|
||||
}
|
||||
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
|
||||
@@ -357,11 +323,11 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
grunt.registerTask('minify', ['bower', 'clean', 'build', 'minall']);
|
||||
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
The Closure Compiler project contains definitions for the AngularJS JavaScript
|
||||
in its `contrib/externs` directory.
|
||||
The Closure Compiler project contains externs definitions for AngularJS
|
||||
JavaScript in its `contrib/externs` directory.
|
||||
|
||||
The definitions contain externs for use with the Closure compiler (aka
|
||||
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||
|
||||
@@ -10,7 +10,7 @@ the browser how to do dependency injection and inversion of control.
|
||||
|
||||
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
|
||||
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. Best of all?? It makes development fun!
|
||||
piece of cake. The best of all: it makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
@@ -18,15 +18,15 @@ piece of cake. Best of all?? It makes development fun!
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
|
||||
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
|
||||
|
||||
grunt package
|
||||
|
||||
|
||||
Running tests
|
||||
Running Tests
|
||||
-------------
|
||||
To execute all unit tests, use:
|
||||
|
||||
@@ -43,26 +43,3 @@ To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
What to use AngularJS for and when to use it
|
||||
---------
|
||||
AngularJS is the next generation framework where each component is designed to work with every other component in an interconnected way like a well-oiled machine. AngularJS is JavaScript MVC made easy and done right. (Well it is not really MVC, read on, to understand what this means.)
|
||||
|
||||
#### MVC, no, MV* done the right way!
|
||||
MVC, short for Model-View-Controller, is a design pattern, i.e. how the code should be organized and how the different parts of an application separated for proper readability and debugging. Model is the data and the database. View is the user interface and what the user sees. Controller is the main link between Model and View. These are the three pillars of major programming frameworks present on the market today. On the other hand AngularJS works on MV*, short for Model-View-_Whatever_. The _Whatever_ is AngularJS's way of telling that you may create any kind of linking between the Model and the View here.
|
||||
|
||||
Unlike other frameworks in any programming language, where MVC, the three separate components, each one has to be written and then connected by the programmer, AngularJS helps the programmer by asking him/her to just create these and everything else will be taken care of by AngularJS.
|
||||
|
||||
#### Interconnection with HTML at the root level
|
||||
AngularJS uses HTML to define the user's interface. AngularJS also enables the programmer to write new HTML tags (AngularJS Directives) and increase the readability and understandability of the HTML code. Directives are AngularJS’s way of bringing additional functionality to HTML. Directives achieve this by enabling us to invent our own HTML elements. This also helps in making the code DRY (Don't Repeat Yourself), which means once created, a new directive can be used anywhere within the application.
|
||||
|
||||
#### Data Handling made simple
|
||||
Data and Data Models in AngularJS are plain JavaScript objects and one can add and change properties directly on it and loop over objects and arrays at will.
|
||||
|
||||
#### Two-way Data Binding
|
||||
One of AngularJS's strongest features. Two-way Data Binding means that if something changes in the Model, the change gets reflected in the View instantaneously, and the same happens the other way around. This is also referred to as Reactive Programming, i.e. suppose `a = b + c` is being programmed and after this, if the value of `b` and/or `c` is changed then the value of `a` will be automatically updated to reflect the change. AngularJS uses its "scopes" as a glue between the Model and View and makes these updates in one available for the other.
|
||||
|
||||
#### Less Written Code and Easily Maintainable Code
|
||||
Everything in AngularJS is created to enable the programmer to end up writing less code that is easily maintainable and readable by any other new person on the team. Believe it or not, one can write a complete working two-way data binded application in less than 10 lines of code. Try and see for yourself!
|
||||
|
||||
#### Testing Ready
|
||||
AngularJS has Dependency Injection, i.e. it takes care of providing all the necessary dependencies to its controllers whenever required. This helps in making the AngularJS code ready for unit testing by making use of mock dependencies created and injected. This makes AngularJS more modular and easily testable thus in turn helping a team create more robust applications.
|
||||
|
||||
+2
-6
@@ -20,7 +20,7 @@ The following is done automatically so you don't have to worry about it:
|
||||
This process based on the idea of minimizing user pain
|
||||
[from this blog post](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html).
|
||||
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?q=is%3Aopen+sort%3Acreated-desc+no%3Amilestone)
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
* Sort by submit date, with the newest issues first
|
||||
* You don't have to do issues in order; feel free to pick and choose issues as you please.
|
||||
* You can triage older issues as well
|
||||
@@ -55,11 +55,7 @@ This process based on the idea of minimizing user pain
|
||||
* inconvenience - causes ugly/boilerplate code in apps
|
||||
1. Label `component: *`
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label `PRs plz!` - These issues are good targets for PRs from the open source community. In addition to applying this label, you must:
|
||||
* Leave a comment explaining the problem and solution so someone can easily finish it.
|
||||
* Assign the issue to yourself.
|
||||
* Give feedback on PRs addressing this issue.
|
||||
* You are responsible for mentoring contributors helping with this issue.
|
||||
1. Label `PRs plz!` - These issues are good targets for PRs from the open source community. Apply to issues where the problem and solution are well defined in the comments, and it's not too complex.
|
||||
1. Label `origin: google` for issues from Google
|
||||
1. Assign a milestone:
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
|
||||
Vendored
+11
-41
@@ -14,15 +14,13 @@ var angularFiles = {
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/animate.js',
|
||||
'src/ng/animateRunner.js',
|
||||
'src/ng/animateCss.js',
|
||||
'src/ng/asyncCallback.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
'src/ng/compile.js',
|
||||
'src/ng/controller.js',
|
||||
'src/ng/document.js',
|
||||
'src/ng/exceptionHandler.js',
|
||||
'src/ng/forceReflow.js',
|
||||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/interpolate.js',
|
||||
@@ -34,7 +32,6 @@ var angularFiles = {
|
||||
'src/ng/q.js',
|
||||
'src/ng/raf.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/rootElement.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
@@ -43,7 +40,6 @@ var angularFiles = {
|
||||
'src/ng/timeout.js',
|
||||
'src/ng/urlUtils.js',
|
||||
'src/ng/window.js',
|
||||
'src/ng/cookieReader.js',
|
||||
|
||||
'src/ng/filter.js',
|
||||
'src/ng/filter/filter.js',
|
||||
@@ -69,7 +65,6 @@ var angularFiles = {
|
||||
'src/ng/directive/ngList.js',
|
||||
'src/ng/directive/ngModel.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
'src/ng/directive/ngOptions.js',
|
||||
'src/ng/directive/ngPluralize.js',
|
||||
'src/ng/directive/ngRepeat.js',
|
||||
'src/ng/directive/ngShowHide.js',
|
||||
@@ -79,43 +74,21 @@ var angularFiles = {
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js',
|
||||
'src/ng/directive/validators.js',
|
||||
'src/angular.bind.js',
|
||||
'src/publishExternalApis.js',
|
||||
'src/ngLocale/angular-locale_en-us.js'
|
||||
'src/ng/directive/validators.js'
|
||||
],
|
||||
|
||||
'angularLoader': [
|
||||
'src/stringify.js',
|
||||
'stringify.js',
|
||||
'src/minErr.js',
|
||||
'src/loader.js'
|
||||
],
|
||||
|
||||
'angularModules': {
|
||||
'ngAnimate': [
|
||||
'src/ngAnimate/shared.js',
|
||||
'src/ngAnimate/rafScheduler.js',
|
||||
'src/ngAnimate/animateChildrenDirective.js',
|
||||
'src/ngAnimate/animateCss.js',
|
||||
'src/ngAnimate/animateCssDriver.js',
|
||||
'src/ngAnimate/animateJs.js',
|
||||
'src/ngAnimate/animateJsDriver.js',
|
||||
'src/ngAnimate/animateQueue.js',
|
||||
'src/ngAnimate/animation.js',
|
||||
'src/ngAnimate/ngAnimateSwap.js',
|
||||
'src/ngAnimate/module.js'
|
||||
'src/ngAnimate/animate.js'
|
||||
],
|
||||
'ngCookies': [
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngCookies/cookieStore.js',
|
||||
'src/ngCookies/cookieWriter.js'
|
||||
],
|
||||
'ngMessageFormat': [
|
||||
'src/ngMessageFormat/messageFormatCommon.js',
|
||||
'src/ngMessageFormat/messageFormatSelector.js',
|
||||
'src/ngMessageFormat/messageFormatInterpolationParts.js',
|
||||
'src/ngMessageFormat/messageFormatParser.js',
|
||||
'src/ngMessageFormat/messageFormatService.js'
|
||||
'src/ngCookies/cookies.js'
|
||||
],
|
||||
'ngMessages': [
|
||||
'src/ngMessages/messages.js'
|
||||
@@ -185,16 +158,16 @@ var angularFiles = {
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'@angularTest'
|
||||
'@angularTest',
|
||||
],
|
||||
|
||||
'karmaExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'src/angular.bind.js'
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'karmaScenario': [
|
||||
@@ -205,10 +178,8 @@ var angularFiles = {
|
||||
"karmaModules": [
|
||||
'build/angular.js',
|
||||
'@angularSrcModules',
|
||||
'test/modules/no_bootstrap.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
@@ -222,22 +193,21 @@ var angularFiles = {
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'@angularTest'
|
||||
'@angularTest',
|
||||
],
|
||||
|
||||
'karmaJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js',
|
||||
'src/angular.bind.js'
|
||||
'test/jquery_remove.js'
|
||||
]
|
||||
};
|
||||
|
||||
angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngAnimate'],
|
||||
angularFiles['angularModules']['ngMessageFormat'],
|
||||
angularFiles['angularModules']['ngMessages'],
|
||||
angularFiles['angularModules']['ngCookies'],
|
||||
angularFiles['angularModules']['ngResource'],
|
||||
|
||||
@@ -8,20 +8,17 @@
|
||||
Large table rendered with AngularJS
|
||||
</p>
|
||||
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="none">none: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineBinding">baseline binding: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineInterpolation">baseline interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBind">ngBind: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindOnce">ngBindOnce: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolation">interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation">interpolation + bind-once: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationAttr">attribute interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFn">ngBind + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFn">interpolation + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFilter">ngBind + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFilter">interpolation + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelConstName">ngModel (const name): </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelInterpName">ngModel (interp name): </label></div>
|
||||
<div>none: <input type="radio" ng-model="benchmarkType" value="none"></div>
|
||||
<div>baseline binding: <input type="radio" ng-model="benchmarkType" value="baselineBinding"></div>
|
||||
<div>baseline interpolation: <input type="radio" ng-model="benchmarkType" value="baselineInterpolation"></div>
|
||||
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
|
||||
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>attribute interpolation: <input type="radio" ng-model="benchmarkType" value="interpolationAttr"></div>
|
||||
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
<div>interpolation + filter: <input type="radio" ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
|
||||
<ng-switch on="benchmarkType">
|
||||
<baseline-binding-table ng-switch-when="baselineBinding">
|
||||
@@ -38,7 +35,7 @@
|
||||
</div>
|
||||
<div ng-switch-when="ngBindOnce">
|
||||
<h2>baseline binding once</h2>
|
||||
<div ng-repeat="row in ::data">
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in ::row">
|
||||
<span ng-bind="::column.i"></span>:<span ng-bind="::column.j"></span>|
|
||||
</span>
|
||||
@@ -50,12 +47,6 @@
|
||||
<span ng-repeat="column in row">{{column.i}}:{{column.j}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="bindOnceInterpolation">
|
||||
<h2>baseline one-time interpolation</h2>
|
||||
<div ng-repeat="row in ::data">
|
||||
<span ng-repeat="column in ::row">{{::column.i}}:{{::column.j}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolationAttr">
|
||||
<h2>attribute interpolation</h2>
|
||||
<div ng-repeat="row in data">
|
||||
@@ -86,20 +77,6 @@
|
||||
<span ng-repeat="column in row">{{column.i | noop}}:{{column.j | noop}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngModelConstName">
|
||||
<h2>ngModel (const name)</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<input type="text" ng-model="row.i" name="constName" />
|
||||
<input type="text" ng-model="row.j" />
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngModelInterpName">
|
||||
<h2>ngModel (interp name)</h2>
|
||||
<div ng-repeat="(rowIdx, row) in data">
|
||||
<input type="text" ng-model="row.i" name="input-{{rowIdx}}" />
|
||||
<input type="text" ng-model="row.j" name="input2-{{rowIdx}}" />
|
||||
</div>
|
||||
</div>
|
||||
</ng-switch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/* globals angular, benchmarkSteps */
|
||||
|
||||
var app = angular.module('ngOptionsBenchmark', []);
|
||||
|
||||
app.config(function($compileProvider) {
|
||||
if ($compileProvider.debugInfoEnabled) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.controller('DataController', function($scope, $element) {
|
||||
$scope.items = [];
|
||||
$scope.count = 10000;
|
||||
|
||||
function changeOptions() {
|
||||
$scope.items = [];
|
||||
for (var i = 0; i < $scope.count; ++i) {
|
||||
$scope.items.push({
|
||||
id: i,
|
||||
label: 'item-' + i,
|
||||
group: 'group-' + i % 100
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var selectElement = $element.find('select');
|
||||
console.log(selectElement);
|
||||
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'add-options',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.count = 10000;
|
||||
changeOptions();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'set-model-1',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.x = $scope.items[1000];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'set-model-2',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.x = $scope.items[10];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'remove-options',
|
||||
fn: function() {
|
||||
$scope.count = 100;
|
||||
changeOptions();
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'add-options',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.count = 10000;
|
||||
changeOptions();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'set-view-1',
|
||||
fn: function() {
|
||||
selectElement.val('2000');
|
||||
selectElement.triggerHandler('change');
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'set-view-2',
|
||||
fn: function() {
|
||||
selectElement.val('1000');
|
||||
selectElement.triggerHandler('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [ {
|
||||
id: 'angular',
|
||||
src: '/build/angular.js'
|
||||
},
|
||||
{
|
||||
src: 'app.js',
|
||||
}]
|
||||
});
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
<div ng-app="ngOptionsBenchmark" ng-cloak>
|
||||
<div ng-controller="DataController">
|
||||
<div class="container-fluid">
|
||||
<p>
|
||||
Tests the execution of ng-options for rendering during model and option updates.
|
||||
</p>
|
||||
<select ng-model="x" ng-options="a as a.label group by a.group for a in items track by a.id"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
|
||||
@@ -44,7 +44,7 @@ var noArgs = function (fn) {
|
||||
|
||||
var identity = function (i) { return i; };
|
||||
|
||||
// like Q.all, but runs the commands in series
|
||||
// like Q.all, but runs the comands in series
|
||||
// useful for ensuring env state (like which branch is checked out)
|
||||
var allInSeries = function (fn) {
|
||||
return function (args) {
|
||||
@@ -103,10 +103,10 @@ then(function (tags) {
|
||||
sort(semver.rcompare);
|
||||
}).
|
||||
then(function (tags) {
|
||||
var major = tags[0].split('.')[0];
|
||||
var major = tags[0].split('.')[0] + '.x';
|
||||
return tags.
|
||||
filter(function (ver) {
|
||||
return semver(ver).major == major;
|
||||
return semver.satisfies(ver, major);
|
||||
});
|
||||
}).
|
||||
then(function (tags) {
|
||||
|
||||
@@ -9,11 +9,3 @@
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ng-animate-shim {
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.ng-anchor {
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ li.doc-example-live {
|
||||
}
|
||||
|
||||
div.syntaxhighlighter {
|
||||
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars */
|
||||
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars http://is.gd/gSMgC */
|
||||
}
|
||||
|
||||
/* TABS - tutorial environment navigation */
|
||||
|
||||
@@ -124,7 +124,7 @@ h1,h2,h3,h4,h5,h6 {
|
||||
font-size:1.2em;
|
||||
padding:0;
|
||||
margin:0;
|
||||
border-bottom:1px solid #aaa;
|
||||
border-bottom:1px soild #aaa;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
@@ -315,13 +315,8 @@ iframe.example {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.search-results-group .search-results {
|
||||
padding: 0 5px 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right:1px solid #222;
|
||||
border-right:1px solid #050505;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
@@ -330,57 +325,10 @@ iframe.example {
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
/* Prevent bullets in the second column from being hidden in Chrome and IE */
|
||||
-webkit-column-gap: 2em;
|
||||
-ms-column-gap: 2em;
|
||||
column-gap: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
word-wrap: break-word;
|
||||
-webkit-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
hyphens: auto;
|
||||
-ms-column-break-inside: avoid;
|
||||
-webkit-column-break-inside: avoid;
|
||||
-moz-column-break-inside: avoid; /* Unsupported */
|
||||
column-break-inside: avoid;
|
||||
text-indent: -0.65em; /* Make sure line wrapped words are aligned vertically */
|
||||
}
|
||||
|
||||
@supports (-moz-column-count: 2) {
|
||||
.search-results-group .search-result {
|
||||
/* Prevents column breaks inside words in FF, but has adverse effects in IE11 and Chrome */
|
||||
overflow: hidden;
|
||||
padding-left: 1em; /* In FF the list item bullet is otherwise hidden */
|
||||
margin-left: -1em; /* offset the padding left */
|
||||
}
|
||||
}
|
||||
|
||||
.search-result:before {
|
||||
content: "\002D\00A0"; /* Dash and non-breaking space as List item type */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:48%;
|
||||
display:inline-block;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:auto;
|
||||
display: list-item;
|
||||
}
|
||||
}
|
||||
|
||||
.search-close {
|
||||
@@ -467,7 +415,7 @@ iframe.example {
|
||||
|
||||
.main-body-grid .side-navigation {
|
||||
position:relative;
|
||||
padding-bottom:120px;
|
||||
padding-bottom:50px;
|
||||
}
|
||||
|
||||
.main-body-grid .side-navigation.ng-hide {
|
||||
@@ -635,18 +583,6 @@ ul.events > li {
|
||||
margin-bottom:40px;
|
||||
}
|
||||
|
||||
.definition-table td {
|
||||
padding: 8px;
|
||||
border: 1px solid #eee;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.table > tbody > tr.head > td,
|
||||
.table > tbody > tr.head > th {
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) and (max-width: 991px) {
|
||||
.main-body-grid {
|
||||
margin-top: 160px;
|
||||
@@ -695,7 +631,6 @@ ul.events > li {
|
||||
}
|
||||
.main-body-grid .side-navigation {
|
||||
display:block!important;
|
||||
padding-bottom:50px;
|
||||
}
|
||||
.main-body-grid .side-navigation.ng-hide {
|
||||
display:none!important;
|
||||
@@ -740,11 +675,6 @@ ul.events > li {
|
||||
padding-bottom:60px;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.search-results-group {
|
||||
float:none!important;
|
||||
display:block!important;
|
||||
@@ -752,47 +682,15 @@ ul.events > li {
|
||||
border:0!important;
|
||||
padding:0!important;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
display:inline-block!important;
|
||||
padding:0 5px;
|
||||
width:auto!important;
|
||||
text-indent: initial;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content:", ";
|
||||
}
|
||||
|
||||
.search-results-group .search-result:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-result {
|
||||
display: list-item !important;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-bottom:0px;
|
||||
}
|
||||
}
|
||||
|
||||
iframe[name="example-anchoringExample"] {
|
||||
height:400px;
|
||||
}
|
||||
|
||||
+442
@@ -0,0 +1,442 @@
|
||||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
|
||||
directive.runnableExample = ['$templateCache', '$document', function($templateCache, $document) {
|
||||
var exampleClassNameSelector = '.runnable-example-file';
|
||||
var doc = $document[0];
|
||||
var tpl =
|
||||
'<nav class="runnable-example-tabs" ng-if="tabs">' +
|
||||
' <a ng-class="{active:$index==activeTabIndex}"' +
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
|
||||
return {
|
||||
restrict: 'C',
|
||||
scope : true,
|
||||
controller : ['$scope', function($scope) {
|
||||
$scope.setTab = function(index) {
|
||||
var tab = $scope.tabs[index];
|
||||
$scope.activeTabIndex = index;
|
||||
$scope.$broadcast('tabChange', index, tab);
|
||||
};
|
||||
}],
|
||||
compile : function(element) {
|
||||
element.html(tpl + element.html());
|
||||
return function(scope, element) {
|
||||
var node = element[0];
|
||||
var examples = node.querySelectorAll(exampleClassNameSelector);
|
||||
var tabs = [], now = Date.now();
|
||||
angular.forEach(examples, function(child, index) {
|
||||
tabs.push(child.getAttribute('name'));
|
||||
});
|
||||
|
||||
if(tabs.length > 0) {
|
||||
scope.tabs = tabs;
|
||||
scope.$on('tabChange', function(e, index, title) {
|
||||
angular.forEach(examples, function(child) {
|
||||
child.style.display = 'none';
|
||||
});
|
||||
var selected = examples[index];
|
||||
selected.style.display = 'block';
|
||||
});
|
||||
scope.setTab(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
directive.dropdownToggle =
|
||||
['$document', '$location', '$window',
|
||||
function ($document, $location, $window) {
|
||||
var openElement = null, close;
|
||||
return {
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs) {
|
||||
scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.parent().on('click', function(event) {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var iWasOpen = false;
|
||||
|
||||
if (openElement) {
|
||||
iWasOpen = openElement === element;
|
||||
close();
|
||||
}
|
||||
|
||||
if (!iWasOpen){
|
||||
element.parent().addClass('open');
|
||||
openElement = element;
|
||||
|
||||
close = function (event) {
|
||||
event && event.preventDefault();
|
||||
event && event.stopPropagation();
|
||||
$document.off('click', close);
|
||||
element.parent().removeClass('open');
|
||||
close = null;
|
||||
openElement = null;
|
||||
}
|
||||
|
||||
$document.on('click', close);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
directive.syntax = function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
function makeLink(type, text, link, icon) {
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
'<span class="' + icon + '"></span> ' + text +
|
||||
'</a>';
|
||||
};
|
||||
|
||||
var html = '';
|
||||
var types = {
|
||||
'github' : {
|
||||
text : 'View on Github',
|
||||
key : 'syntaxGithub',
|
||||
icon : 'icon-github'
|
||||
},
|
||||
'plunkr' : {
|
||||
text : 'View on Plunkr',
|
||||
key : 'syntaxPlunkr',
|
||||
icon : 'icon-arrow-down'
|
||||
},
|
||||
'jsfiddle' : {
|
||||
text : 'View on JSFiddle',
|
||||
key : 'syntaxFiddle',
|
||||
icon : 'icon-cloud'
|
||||
}
|
||||
};
|
||||
for(var type in types) {
|
||||
var data = types[type];
|
||||
var link = attrs[data.key];
|
||||
if(link) {
|
||||
html += makeLink(type, data.text, link, data.icon);
|
||||
}
|
||||
};
|
||||
|
||||
var nav = document.createElement('nav');
|
||||
nav.className = 'syntax-links';
|
||||
nav.innerHTML = html;
|
||||
|
||||
var node = element[0];
|
||||
var par = node.parentNode;
|
||||
par.insertBefore(nav, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directive.tabbable = function() {
|
||||
return {
|
||||
restrict: 'C',
|
||||
compile: function(element) {
|
||||
var navTabs = angular.element('<ul class="nav nav-tabs"></ul>'),
|
||||
tabContent = angular.element('<div class="tab-content"></div>');
|
||||
|
||||
tabContent.append(element.contents());
|
||||
element.append(navTabs).append(tabContent);
|
||||
},
|
||||
controller: ['$scope', '$element', function($scope, $element) {
|
||||
var navTabs = $element.contents().eq(0),
|
||||
ngModel = $element.controller('ngModel') || {},
|
||||
tabs = [],
|
||||
selectedTab;
|
||||
|
||||
ngModel.$render = function() {
|
||||
var $viewValue = this.$viewValue;
|
||||
|
||||
if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) {
|
||||
if(selectedTab) {
|
||||
selectedTab.paneElement.removeClass('active');
|
||||
selectedTab.tabElement.removeClass('active');
|
||||
selectedTab = null;
|
||||
}
|
||||
if($viewValue) {
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++) {
|
||||
if ($viewValue == tabs[i].value) {
|
||||
selectedTab = tabs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedTab) {
|
||||
selectedTab.paneElement.addClass('active');
|
||||
selectedTab.tabElement.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.addPane = function(element, attr) {
|
||||
var li = angular.element('<li><a href></a></li>'),
|
||||
a = li.find('a'),
|
||||
tab = {
|
||||
paneElement: element,
|
||||
paneAttrs: attr,
|
||||
tabElement: li
|
||||
};
|
||||
|
||||
tabs.push(tab);
|
||||
|
||||
attr.$observe('value', update)();
|
||||
attr.$observe('title', function(){ update(); a.text(tab.title); })();
|
||||
|
||||
function update() {
|
||||
tab.title = attr.title;
|
||||
tab.value = attr.value || attr.title;
|
||||
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
}
|
||||
ngModel.$render();
|
||||
}
|
||||
|
||||
navTabs.append(li);
|
||||
li.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (ngModel.$setViewValue) {
|
||||
$scope.$apply(function() {
|
||||
ngModel.$setViewValue(tab.value);
|
||||
ngModel.$render();
|
||||
});
|
||||
} else {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
ngModel.$render();
|
||||
}
|
||||
});
|
||||
|
||||
return function() {
|
||||
tab.tabElement.remove();
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++ ) {
|
||||
if (tab == tabs[i]) {
|
||||
tabs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
directive.table = function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
if (!attrs['class']) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var popoverElement = function() {
|
||||
var object = {
|
||||
init : function() {
|
||||
this.element = angular.element(
|
||||
'<div class="popover popover-incode top">' +
|
||||
'<div class="arrow"></div>' +
|
||||
'<div class="popover-inner">' +
|
||||
'<div class="popover-title"><code></code></div>' +
|
||||
'<div class="popover-content"></div>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
this.node = this.element[0];
|
||||
this.element.css({
|
||||
'display':'block',
|
||||
'position':'absolute'
|
||||
});
|
||||
angular.element(document.body).append(this.element);
|
||||
|
||||
var inner = this.element.children()[1];
|
||||
this.titleElement = angular.element(inner.childNodes[0].firstChild);
|
||||
this.contentElement = angular.element(inner.childNodes[1]);
|
||||
|
||||
//stop the click on the tooltip
|
||||
this.element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
var self = this;
|
||||
angular.element(document.body).on('click',function(event) {
|
||||
if(self.visible()) self.hide();
|
||||
});
|
||||
},
|
||||
|
||||
show : function(x,y) {
|
||||
this.element.addClass('visible');
|
||||
this.position(x || 0, y || 0);
|
||||
},
|
||||
|
||||
hide : function() {
|
||||
this.element.removeClass('visible');
|
||||
this.position(-9999,-9999);
|
||||
},
|
||||
|
||||
visible : function() {
|
||||
return this.position().y >= 0;
|
||||
},
|
||||
|
||||
isSituatedAt : function(element) {
|
||||
return this.besideElement ? element[0] == this.besideElement[0] : false;
|
||||
},
|
||||
|
||||
title : function(value) {
|
||||
return this.titleElement.html(value);
|
||||
},
|
||||
|
||||
content : function(value) {
|
||||
if(value && value.length > 0) {
|
||||
value = marked(value);
|
||||
}
|
||||
return this.contentElement.html(value);
|
||||
},
|
||||
|
||||
positionArrow : function(position) {
|
||||
this.node.className = 'popover ' + position;
|
||||
},
|
||||
|
||||
positionAway : function() {
|
||||
this.besideElement = null;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
positionBeside : function(element) {
|
||||
this.besideElement = element;
|
||||
|
||||
var elm = element[0];
|
||||
var x = elm.offsetLeft;
|
||||
var y = elm.offsetTop;
|
||||
x -= 30;
|
||||
y -= this.node.offsetHeight + 10;
|
||||
this.show(x,y);
|
||||
},
|
||||
|
||||
position : function(x,y) {
|
||||
if(x != null && y != null) {
|
||||
this.element.css('left',x + 'px');
|
||||
this.element.css('top', y + 'px');
|
||||
}
|
||||
else {
|
||||
return {
|
||||
x : this.node.offsetLeft,
|
||||
y : this.node.offsetTop
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
object.init();
|
||||
object.hide();
|
||||
|
||||
return object;
|
||||
};
|
||||
|
||||
directive.popover = ['popoverElement', function(popover) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
priority : 500,
|
||||
link: function(scope, element, attrs) {
|
||||
element.on('click',function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if(popover.isSituatedAt(element) && popover.visible()) {
|
||||
popover.title('');
|
||||
popover.content('');
|
||||
popover.positionAway();
|
||||
}
|
||||
else {
|
||||
popover.title(attrs.title);
|
||||
popover.content(attrs.content);
|
||||
popover.positionBeside(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
directive.tabPane = function() {
|
||||
return {
|
||||
require: '^tabbable',
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs, tabsCtrl) {
|
||||
element.on('$remove', tabsCtrl.addPane(element, attrs));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
directive.foldout = ['$http', '$animate','$window', function($http, $animate, $window) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
priority : 500,
|
||||
link: function(scope, element, attrs) {
|
||||
var container, loading, url = attrs.url;
|
||||
if(/\/build\//.test($window.location.href)) {
|
||||
url = '/build/docs' + url;
|
||||
}
|
||||
element.on('click',function() {
|
||||
scope.$apply(function() {
|
||||
if(!container) {
|
||||
if(loading) return;
|
||||
|
||||
loading = true;
|
||||
var par = element.parent();
|
||||
container = angular.element('<div class="foldout">loading...</div>');
|
||||
$animate.enter(container, null, par);
|
||||
|
||||
$http.get(url, { cache : true }).success(function(html) {
|
||||
loading = false;
|
||||
|
||||
html = '<div class="foldout-inner">' +
|
||||
'<div calss="foldout-arrow"></div>' +
|
||||
html +
|
||||
'</div>';
|
||||
container.html(html);
|
||||
|
||||
//avoid showing the element if the user has already closed it
|
||||
if(container.css('display') == 'block') {
|
||||
container.css('display','none');
|
||||
$animate.addClass(container, 'ng-hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
container.hasClass('ng-hide') ? $animate.removeClass(container, 'ng-hide') : $animate.addClass(container, 'ng-hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
angular.module('bootstrap', [])
|
||||
.directive(directive)
|
||||
.factory('popoverElement', popoverElement)
|
||||
.run(function() {
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
tables: true
|
||||
});
|
||||
});
|
||||
+1
-3
@@ -54,9 +54,7 @@ angular.module('ui.bootstrap.dropdown', [])
|
||||
}
|
||||
};
|
||||
|
||||
var closeDropdown = function(evt) {
|
||||
if (evt && evt.which === 3) return;
|
||||
|
||||
var closeDropdown = function() {
|
||||
openScope.$apply(function() {
|
||||
openScope.isOpen = false;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* global importScripts, onmessage: true, postMessage, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
|
||||
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
|
||||
@@ -13,6 +13,7 @@ angular.module('docsApp', [
|
||||
'search',
|
||||
'tutorials',
|
||||
'versions',
|
||||
'bootstrap',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
@@ -34,15 +34,4 @@ angular.module('directives', [])
|
||||
return function(scope, element) {
|
||||
$anchorScroll.yOffset = element;
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('table', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
if (!attrs['class']) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
angular.module('DocsController', [])
|
||||
|
||||
.controller('DocsController', [
|
||||
'$scope', '$rootScope', '$location', '$window', '$cookies',
|
||||
'$scope', '$rootScope', '$location', '$window', '$cookies', 'openPlunkr',
|
||||
'NG_PAGES', 'NG_NAVIGATION', 'NG_VERSION',
|
||||
function($scope, $rootScope, $location, $window, $cookies,
|
||||
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
|
||||
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
|
||||
|
||||
$scope.openPlunkr = openPlunkr;
|
||||
|
||||
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
|
||||
|
||||
$scope.navClass = function(navItem) {
|
||||
|
||||
+3
-10
@@ -13,10 +13,10 @@ angular.module('errors', ['ngSanitize'])
|
||||
};
|
||||
|
||||
return function (text, target) {
|
||||
if (!text) return text;
|
||||
|
||||
var targetHtml = target ? ' target="' + target + '"' : '';
|
||||
|
||||
if (!text) return text;
|
||||
|
||||
return $sanitize(text.replace(LINKY_URL_REGEXP, function (url) {
|
||||
if (STACK_TRACE_REGEXP.test(url)) {
|
||||
return url;
|
||||
@@ -34,10 +34,6 @@ angular.module('errors', ['ngSanitize'])
|
||||
|
||||
|
||||
.directive('errorDisplay', ['$location', 'errorLinkFilter', function ($location, errorLinkFilter) {
|
||||
var encodeAngleBrackets = function (text) {
|
||||
return text.replace(/</g, '<').replace(/>/g, '>');
|
||||
};
|
||||
|
||||
var interpolate = function (formatString) {
|
||||
var formatArgs = arguments;
|
||||
return formatString.replace(/\{\d+\}/g, function (match) {
|
||||
@@ -55,15 +51,12 @@ angular.module('errors', ['ngSanitize'])
|
||||
link: function (scope, element, attrs) {
|
||||
var search = $location.search(),
|
||||
formatArgs = [attrs.errorDisplay],
|
||||
formattedText,
|
||||
i;
|
||||
|
||||
for (i = 0; angular.isDefined(search['p'+i]); i++) {
|
||||
formatArgs.push(search['p'+i]);
|
||||
}
|
||||
|
||||
formattedText = encodeAngleBrackets(interpolate.apply(null, formatArgs));
|
||||
element.html(errorLinkFilter(formattedText, '_blank'));
|
||||
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
+28
-149
@@ -1,55 +1,5 @@
|
||||
angular.module('examples', [])
|
||||
|
||||
.directive('runnableExample', ['$templateCache', '$document', function($templateCache, $document) {
|
||||
var exampleClassNameSelector = '.runnable-example-file';
|
||||
var doc = $document[0];
|
||||
var tpl =
|
||||
'<nav class="runnable-example-tabs" ng-if="tabs">' +
|
||||
' <a ng-class="{active:$index==activeTabIndex}"' +
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
|
||||
return {
|
||||
restrict: 'C',
|
||||
scope : true,
|
||||
controller : ['$scope', function($scope) {
|
||||
$scope.setTab = function(index) {
|
||||
var tab = $scope.tabs[index];
|
||||
$scope.activeTabIndex = index;
|
||||
$scope.$broadcast('tabChange', index, tab);
|
||||
};
|
||||
}],
|
||||
compile : function(element) {
|
||||
element.html(tpl + element.html());
|
||||
return function(scope, element) {
|
||||
var node = element[0];
|
||||
var examples = node.querySelectorAll(exampleClassNameSelector);
|
||||
var tabs = [], now = Date.now();
|
||||
angular.forEach(examples, function(child, index) {
|
||||
tabs.push(child.getAttribute('name'));
|
||||
});
|
||||
|
||||
if(tabs.length > 0) {
|
||||
scope.tabs = tabs;
|
||||
scope.$on('tabChange', function(e, index, title) {
|
||||
angular.forEach(examples, function(child) {
|
||||
child.style.display = 'none';
|
||||
});
|
||||
var selected = examples[index];
|
||||
selected.style.display = 'block';
|
||||
});
|
||||
scope.setTab(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('formPostData', ['$document', function($document) {
|
||||
return function(url, newWindow, fields) {
|
||||
/**
|
||||
@@ -72,110 +22,29 @@ angular.module('examples', [])
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('createCopyrightNotice', function() {
|
||||
var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n'
|
||||
+ 'Use of this source code is governed by an MIT-style license that\n'
|
||||
+ 'can be found in the LICENSE file at http://angular.io/license';
|
||||
var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/';
|
||||
var COPYRIGHT_HTML = '\n\n<!-- \n' + COPYRIGHT + '\n-->';
|
||||
|
||||
return function getCopyright(filename) {
|
||||
switch (filename.substr(filename.lastIndexOf('.'))) {
|
||||
case '.html':
|
||||
return COPYRIGHT_HTML;
|
||||
case '.js':
|
||||
case '.css':
|
||||
return COPYRIGHT_JS_CSS;
|
||||
case '.md':
|
||||
return COPYRIGHT;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
})
|
||||
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
|
||||
return function(exampleFolder, clickEvent) {
|
||||
|
||||
.directive('plnkrOpener', ['$q', 'getExampleData', 'formPostData', 'createCopyrightNotice', function($q, getExampleData, formPostData, createCopyrightNotice) {
|
||||
return {
|
||||
scope: {},
|
||||
bindToController: {
|
||||
'examplePath': '@'
|
||||
},
|
||||
controllerAs: 'plnkr',
|
||||
template: '<button ng-click="plnkr.open($event)" class="btn pull-right"> <i class="glyphicon glyphicon-edit"> </i> Edit in Plunker</button> ',
|
||||
controller: [function() {
|
||||
var ctrl = this;
|
||||
var exampleName = 'AngularJS Example';
|
||||
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
|
||||
|
||||
ctrl.example = {
|
||||
path: ctrl.examplePath,
|
||||
manifest: undefined,
|
||||
files: undefined,
|
||||
name: 'AngularJS Example'
|
||||
};
|
||||
|
||||
ctrl.prepareExampleData = function() {
|
||||
if (ctrl.example.manifest) {
|
||||
return $q.when(ctrl.example);
|
||||
}
|
||||
|
||||
return getExampleData(ctrl.examplePath).then(function(data) {
|
||||
ctrl.example.files = data.files;
|
||||
ctrl.example.manifest = data.manifest;
|
||||
|
||||
// Build a pretty title for the Plunkr
|
||||
var exampleNameParts = data.manifest.name.split('-');
|
||||
exampleNameParts.unshift('AngularJS');
|
||||
angular.forEach(exampleNameParts, function(part, index) {
|
||||
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
|
||||
});
|
||||
ctrl.example.name = exampleNameParts.join(' - ');
|
||||
|
||||
return ctrl.example;
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.open = function(clickEvent) {
|
||||
|
||||
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
|
||||
|
||||
var postData = {
|
||||
'tags[0]': "angularjs",
|
||||
'tags[1]': "example",
|
||||
'private': true
|
||||
};
|
||||
|
||||
// Make sure the example data is available.
|
||||
// If an XHR must be made, this might break some pop-up blockers when
|
||||
// new window is requested
|
||||
ctrl.prepareExampleData()
|
||||
.then(function() {
|
||||
angular.forEach(ctrl.example.files, function(file) {
|
||||
postData['files[' + file.name + ']'] = file.content + createCopyrightNotice(file.name);
|
||||
});
|
||||
|
||||
postData.description = ctrl.example.name;
|
||||
|
||||
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Initialize the example data, so it's ready when clicking the open button.
|
||||
// Otherwise pop-up blockers will prevent a new window from opening
|
||||
ctrl.prepareExampleData(ctrl.example.path);
|
||||
|
||||
}]
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('getExampleData', ['$http', '$q', function($http, $q) {
|
||||
return function(exampleFolder){
|
||||
// Load the manifest for the example
|
||||
return $http.get(exampleFolder + '/manifest.json')
|
||||
$http.get(exampleFolder + '/manifest.json')
|
||||
.then(function(response) {
|
||||
return response.data;
|
||||
})
|
||||
.then(function(manifest) {
|
||||
var filePromises = [];
|
||||
|
||||
// Build a pretty title for the Plunkr
|
||||
var exampleNameParts = manifest.name.split('-');
|
||||
exampleNameParts.unshift('AngularJS');
|
||||
angular.forEach(exampleNameParts, function(part, index) {
|
||||
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
|
||||
});
|
||||
exampleName = exampleNameParts.join(' - ');
|
||||
|
||||
angular.forEach(manifest.files, function(filename) {
|
||||
filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
|
||||
.then(function(response) {
|
||||
@@ -183,7 +52,7 @@ angular.module('examples', [])
|
||||
// The manifests provide the production index file but Plunkr wants
|
||||
// a straight index.html
|
||||
if (filename === "index-production.html") {
|
||||
filename = "index.html";
|
||||
filename = "index.html"
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -192,11 +61,21 @@ angular.module('examples', [])
|
||||
};
|
||||
}));
|
||||
});
|
||||
return $q.all(filePromises);
|
||||
})
|
||||
.then(function(files) {
|
||||
var postData = {};
|
||||
|
||||
return $q.all({
|
||||
manifest: manifest,
|
||||
files: $q.all(filePromises)
|
||||
angular.forEach(files, function(file) {
|
||||
postData['files[' + file.name + ']'] = file.content;
|
||||
});
|
||||
|
||||
postData['tags[0]'] = "angularjs";
|
||||
postData['tags[1]'] = "example";
|
||||
postData.private = true;
|
||||
postData.description = exampleName;
|
||||
|
||||
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
|
||||
});
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -11,15 +11,7 @@ angular.module('search', [])
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
docsSearch(q).then(function(hits) {
|
||||
// Make sure the areas are always in the same order
|
||||
var results = {
|
||||
api: [],
|
||||
guide: [],
|
||||
tutorial: [],
|
||||
error: [],
|
||||
misc: []
|
||||
};
|
||||
|
||||
var results = {};
|
||||
angular.forEach(hits, function(hit) {
|
||||
var area = hit.area;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ angular.module('tutorials', [])
|
||||
'step': '@docTutorialReset'
|
||||
},
|
||||
template:
|
||||
'<p><button class="btn" ng-click="show=!show">Workspace Reset Instructions ➤</button></p>\n' +
|
||||
'<p><a href="" ng-click="show=!show;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>\n' +
|
||||
'<div class="alert alert-info" ng-show="show">\n' +
|
||||
' <p>Reset the workspace to step {{step}}.</p>' +
|
||||
' <p><pre>git checkout -f step-{{step}}</pre></p>\n' +
|
||||
@@ -43,7 +43,7 @@ angular.module('tutorials', [])
|
||||
'<a href="http://angular.github.io/angular-phonecat/step-{{step}}/app">Step {{step}} Live Demo</a>.</p>\n' +
|
||||
'</div>\n' +
|
||||
'<p>The most important changes are listed below. You can see the full diff on ' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}" title="See diff on Github">GitHub</a>\n' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
|
||||
'</p>'
|
||||
};
|
||||
});
|
||||
@@ -21,13 +21,11 @@ angular.module('versions', [])
|
||||
};
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
var currentPagePath = $location.path().replace(/\/$/, ''),
|
||||
url = '';
|
||||
if (version.isOldDocsUrl) {
|
||||
url = version.docsUrl;
|
||||
}else{
|
||||
url = version.docsUrl + currentPagePath;
|
||||
}
|
||||
$window.location = url;
|
||||
var currentPagePath = $location.path().replace(/\/$/, '');
|
||||
|
||||
// TODO: We need to do some munging of the path for different versions of the API...
|
||||
|
||||
|
||||
$window.location = version.docsUrl + currentPagePath;
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"extends": "../../../.jshintrc-base",
|
||||
"browser": true,
|
||||
"globals": {
|
||||
// AngularJS
|
||||
"angular": false,
|
||||
|
||||
// ngMocks
|
||||
"module": false,
|
||||
"inject": true,
|
||||
|
||||
// Jasmine
|
||||
"jasmine": false,
|
||||
"describe": false,
|
||||
"ddescribe": false,
|
||||
"xdescribe": false,
|
||||
"it": false,
|
||||
"iit": false,
|
||||
"xit": false,
|
||||
"beforeEach": false,
|
||||
"afterEach": false,
|
||||
"spyOn": false,
|
||||
"expect": false
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ describe("DocsController", function() {
|
||||
|
||||
angular.module('fake', [])
|
||||
.value('$cookies', {})
|
||||
.value('openPlunkr', function() {})
|
||||
.value('NG_PAGES', {})
|
||||
.value('NG_NAVIGATION', {})
|
||||
.value('NG_VERSION', {});
|
||||
@@ -25,7 +26,7 @@ describe("DocsController", function() {
|
||||
|
||||
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
|
||||
$window._gaq = [];
|
||||
spyOn($location, 'path').and.returnValue('x/y/z');
|
||||
spyOn($location, 'path').andReturn('x/y/z');
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('errors', function() {
|
||||
// Mock `ngSanitize` module
|
||||
angular.
|
||||
module('ngSanitize', []).
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
|
||||
|
||||
beforeEach(module('errors'));
|
||||
|
||||
|
||||
describe('errorDisplay', function() {
|
||||
var $sanitize;
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(inject(function(_$sanitize_, _errorLinkFilter_) {
|
||||
$sanitize = _$sanitize_;
|
||||
errorLinkFilter = _errorLinkFilter_;
|
||||
}));
|
||||
|
||||
|
||||
it('should return empty input unchanged', function() {
|
||||
var inputs = [undefined, null, false, 0, ''];
|
||||
var remaining = inputs.length;
|
||||
|
||||
inputs.forEach(function(falsyValue) {
|
||||
expect(errorLinkFilter(falsyValue)).toBe(falsyValue);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should recognize URLs and convert them to `<a>`', function() {
|
||||
var urls = [
|
||||
['ftp://foo/bar?baz#qux'],
|
||||
['http://foo/bar?baz#qux'],
|
||||
['https://foo/bar?baz#qux'],
|
||||
['mailto:foo_bar@baz.qux', null, 'foo_bar@baz.qux'],
|
||||
['foo_bar@baz.qux', 'mailto:foo_bar@baz.qux', 'foo_bar@baz.qux']
|
||||
];
|
||||
var remaining = urls.length;
|
||||
|
||||
urls.forEach(function(values) {
|
||||
var actualUrl = values[0];
|
||||
var expectedUrl = values[1] || actualUrl;
|
||||
var expectedText = values[2] || expectedUrl;
|
||||
var anchor = '<a href="' + expectedUrl + '">' + expectedText + '</a>';
|
||||
|
||||
var input = 'start ' + actualUrl + ' end';
|
||||
var output = 'start ' + anchor + ' end';
|
||||
|
||||
expect(errorLinkFilter(input)).toBe(output);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should not recognize stack-traces as URLs', function() {
|
||||
var urls = [
|
||||
'ftp://foo/bar?baz#qux:4:2',
|
||||
'http://foo/bar?baz#qux:4:2',
|
||||
'https://foo/bar?baz#qux:4:2',
|
||||
'mailto:foo_bar@baz.qux:4:2',
|
||||
'foo_bar@baz.qux:4:2'
|
||||
];
|
||||
var remaining = urls.length;
|
||||
|
||||
urls.forEach(function(url) {
|
||||
var input = 'start ' + url + ' end';
|
||||
|
||||
expect(errorLinkFilter(input)).toBe(input);
|
||||
remaining--;
|
||||
});
|
||||
|
||||
expect(remaining).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should should set `[target]` if specified', function() {
|
||||
var url = 'https://foo/bar?baz#qux';
|
||||
var target = '_blank';
|
||||
var outputWithoutTarget = '<a href="' + url + '">' + url + '</a>';
|
||||
var outputWithTarget = '<a target="' + target + '" href="' + url + '">' + url + '</a>';
|
||||
|
||||
expect(errorLinkFilter(url)).toBe(outputWithoutTarget);
|
||||
expect(errorLinkFilter(url, target)).toBe(outputWithTarget);
|
||||
});
|
||||
|
||||
|
||||
it('should truncate the contents of the generated `<a>` to 60 characters', function() {
|
||||
var looongUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo';
|
||||
var truncatedUrl = 'https://foooooooooooooooooooooooooooooooooooooooooooooooo...';
|
||||
var output = '<a href="' + looongUrl + '">' + truncatedUrl + '</a>';
|
||||
|
||||
expect(looongUrl.length).toBeGreaterThan(60);
|
||||
expect(truncatedUrl.length).toBe(60);
|
||||
expect(errorLinkFilter(looongUrl)).toBe(output);
|
||||
});
|
||||
|
||||
|
||||
it('should pass the final string through `$sanitize`', function() {
|
||||
$sanitize.calls.reset();
|
||||
|
||||
var input = 'start https://foo/bar?baz#qux end';
|
||||
var output = errorLinkFilter(input);
|
||||
|
||||
expect($sanitize).toHaveBeenCalledTimes(1);
|
||||
expect($sanitize).toHaveBeenCalledWith(output);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('errorDisplay', function() {
|
||||
var $compile;
|
||||
var $location;
|
||||
var $rootScope;
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.decorator('errorLinkFilter', function() {
|
||||
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
|
||||
errorLinkFilter.and.callFake(angular.identity);
|
||||
|
||||
return errorLinkFilter;
|
||||
});
|
||||
}));
|
||||
beforeEach(inject(function(_$compile_, _$location_, _$rootScope_) {
|
||||
$compile = _$compile_;
|
||||
$location = _$location_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
|
||||
it('should set the element\s HTML', function() {
|
||||
var elem = $compile('<span error-display="bar">foo</span>')($rootScope);
|
||||
expect(elem.html()).toBe('bar');
|
||||
});
|
||||
|
||||
|
||||
it('should interpolate the contents against `$location.search()`', function() {
|
||||
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
|
||||
expect(elem.html()).toBe('foo = foo, bar = bar');
|
||||
});
|
||||
|
||||
|
||||
it('should pass the interpolated text through `errorLinkFilter`', function() {
|
||||
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
|
||||
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
|
||||
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
|
||||
});
|
||||
|
||||
|
||||
it('should encode `<` and `>`', function() {
|
||||
var elem = $compile('<span error-display="<xyz>"></span>')($rootScope);
|
||||
expect(elem.text()).toBe('<xyz>');
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.5.12",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
|
||||
+7
-16
@@ -6,18 +6,18 @@ var packagePath = __dirname;
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
// Create and export a new Dgeni package called angularjs. This package depends upon
|
||||
// the ngdoc, nunjucks, and examples packages defined in the dgeni-packages npm module.
|
||||
// the ngdoc,nunjucks and examples packages defined in the dgeni-packages npm module.
|
||||
module.exports = new Package('angularjs', [
|
||||
require('dgeni-packages/ngdoc'),
|
||||
require('dgeni-packages/nunjucks'),
|
||||
require('dgeni-packages/examples'),
|
||||
require('dgeni-packages/git')
|
||||
require('dgeni-packages/examples')
|
||||
])
|
||||
|
||||
|
||||
.factory(require('./services/errorNamespaceMap'))
|
||||
.factory(require('./services/getMinerrInfo'))
|
||||
.factory(require('./services/getVersion'))
|
||||
.factory(require('./services/gitData'))
|
||||
|
||||
.factory(require('./services/deployments/debug'))
|
||||
.factory(require('./services/deployments/default'))
|
||||
@@ -26,6 +26,7 @@ module.exports = new Package('angularjs', [
|
||||
|
||||
.factory(require('./inline-tag-defs/type'))
|
||||
|
||||
|
||||
.processor(require('./processors/error-docs'))
|
||||
.processor(require('./processors/index-page'))
|
||||
.processor(require('./processors/keywords'))
|
||||
@@ -42,7 +43,7 @@ module.exports = new Package('angularjs', [
|
||||
|
||||
readFilesProcessor.basePath = path.resolve(__dirname,'../..');
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{ include: 'src/**/*.js', exclude: 'src/angular.bind.js', basePath: 'src' },
|
||||
{ include: 'src/**/*.js', basePath: 'src' },
|
||||
{ include: 'docs/content/**/*.ngdoc', basePath: 'docs/content' }
|
||||
];
|
||||
|
||||
@@ -124,16 +125,10 @@ module.exports = new Package('angularjs', [
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['error'],
|
||||
getId: function(doc) { return 'error:' + doc.namespace + ':' + doc.name; },
|
||||
getAliases: function(doc) { return [doc.name, doc.namespace + ':' + doc.name, doc.id]; }
|
||||
},
|
||||
{
|
||||
docTypes: ['errorNamespace'],
|
||||
docTypes: ['error', 'errorNamespace'],
|
||||
getId: function(doc) { return 'error:' + doc.name; },
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
.config(function(checkAnchorLinksProcessor) {
|
||||
@@ -170,8 +165,4 @@ module.exports = new Package('angularjs', [
|
||||
jqueryDeployment,
|
||||
productionDeployment
|
||||
];
|
||||
})
|
||||
|
||||
.config(function(generateKeywordsProcessor) {
|
||||
generateKeywordsProcessor.docTypesToIgnore = ['componentGroup'];
|
||||
});
|
||||
|
||||
@@ -16,11 +16,9 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
ignoreWordsFile: undefined,
|
||||
areasToSearch: ['api', 'guide', 'misc', 'error', 'tutorial'],
|
||||
propertiesToIgnore: [],
|
||||
docTypesToIgnore: [],
|
||||
$validate: {
|
||||
ignoreWordsFile: { },
|
||||
areasToSearch: { presence: true },
|
||||
docTypesToIgnore: { },
|
||||
propertiesToIgnore: { }
|
||||
},
|
||||
$runAfter: ['memberDocsProcessor'],
|
||||
@@ -30,7 +28,6 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
// Keywords to ignore
|
||||
var wordsToIgnore = [];
|
||||
var propertiesToIgnore;
|
||||
var docTypesToIgnore;
|
||||
var areasToSearch;
|
||||
|
||||
// Keywords start with "ng:" or one of $, _ or a letter
|
||||
@@ -50,8 +47,6 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
areasToSearch = _.indexBy(this.areasToSearch);
|
||||
propertiesToIgnore = _.indexBy(this.propertiesToIgnore);
|
||||
log.debug('Properties to ignore', propertiesToIgnore);
|
||||
docTypesToIgnore = _.indexBy(this.docTypesToIgnore);
|
||||
log.debug('Doc types to ignore', docTypesToIgnore);
|
||||
|
||||
var ignoreWordsMap = _.indexBy(wordsToIgnore);
|
||||
|
||||
@@ -83,36 +78,34 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
|
||||
// We are only interested in docs that live in the right area
|
||||
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
|
||||
docs = _.filter(docs, function(doc) { return !docTypesToIgnore[doc.docType]; });
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'../angular-touch.js',
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -18,6 +18,7 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -21,6 +21,7 @@ module.exports = function productionDeployment(getVersion) {
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
/**
|
||||
* @dgService gitData
|
||||
* @description
|
||||
* Information from the local git repository
|
||||
*/
|
||||
module.exports = function gitData() {
|
||||
return {
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "base.template.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Error: {$ doc.namespace $}:{$ doc.name $}
|
||||
<h1>Error: {$ doc.id $}
|
||||
<div><span class='hint'>{$ doc.fullName $}</span></div>
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="Description"
|
||||
content="AngularJS is what HTML would have been, had it been designed for building web-apps.
|
||||
Declarative templates with data-binding, MVC, dependency injection and great
|
||||
@@ -76,7 +76,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
|
||||
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -90,7 +90,7 @@
|
||||
<li class="disabled"><a href="http://angularjs.org/">Why AngularJS?</a></li>
|
||||
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
|
||||
<li><a href="http://builtwith.angularjs.org/">Case Studies</a></li>
|
||||
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
</ul>
|
||||
@@ -147,13 +147,13 @@
|
||||
<div class="search-results-container" ng-show="hasResults">
|
||||
<div class="container">
|
||||
<div class="search-results-frame">
|
||||
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
|
||||
<div ng-repeat="(key, value) in results" class="search-results-group" ng-class="colClassName + ' col-group-' + key">
|
||||
<h4 class="search-results-group-heading">{{ key }}</h4>
|
||||
<ul class="search-results">
|
||||
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
|
||||
TODO: use a html minifier instead -->
|
||||
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
|
||||
</ul>
|
||||
<div class="search-results">
|
||||
<div ng-repeat="item in value" class="search-result">
|
||||
- <a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="" ng-click="hideResults()" class="search-close">
|
||||
@@ -220,10 +220,10 @@
|
||||
<p class="pull-right"><a back-to-top>Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2016
|
||||
Super-powered by Google ©2010-2014
|
||||
( <a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
|
||||
ng-bind-template="v{{version}}">
|
||||
</a>
|
||||
)
|
||||
</p>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
{% macro typeList(types) -%}
|
||||
{% for typeName in types %}<a href="" class="{$ typeName | typeClass $}">{$ typeName | escape $}</a>{% endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro paramTable(params) %}
|
||||
<table class="variables-matrix input-arguments">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param</th>
|
||||
<th>Type</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for param in params %}
|
||||
<tr>
|
||||
<td>
|
||||
{$ param.name $}
|
||||
{% if param.alias %}| {$ param.alias $}{% endif %}
|
||||
{% if param.optional %}<div><em>(optional)</em></div>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{$ typeList(param.typeList) $}
|
||||
</td>
|
||||
<td>
|
||||
{$ param.description | marked $}
|
||||
{% if param.defaultValue %}<p><em>(default: {$ param.defaultValue $})</em></p>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endmacro -%}
|
||||
|
||||
|
||||
{%- macro directiveParam(name, type, join, sep) %}
|
||||
{%- if type.optional %}[{% endif -%}
|
||||
{$ name | dashCase $}{$ join $}{$ type.name $}{$ sep $}
|
||||
{%- if type.optional %}]{% endif -%}
|
||||
{% endmacro -%}
|
||||
|
||||
{%- macro functionSyntax(fn) %}
|
||||
{%- set sep = joiner(', ') -%}
|
||||
{% marked -%}
|
||||
`{$ fn.name $}({%- for param in fn.params %}{$ sep() $}
|
||||
{%- if param.type.optional %}[{% endif -%}
|
||||
{$ param.name $}
|
||||
{%- if param.type.optional %}]{% endif -%}
|
||||
{% endfor %});`
|
||||
{%- endmarked %}
|
||||
{% endmacro -%}
|
||||
|
||||
{%- macro typeInfo(fn) -%}
|
||||
<table class="variables-matrix return-arguments">
|
||||
<tr>
|
||||
<td>{$ typeList(fn.typeList) $}</td>
|
||||
<td>{$ fn.description | marked $}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{%- endmacro -%}
|
||||
@@ -1,8 +1,10 @@
|
||||
{# Be aware that we need these extra new lines here or marked will not realize that the <div>
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
<plnkr-opener example-path="{$ doc.path $}"></plnkr-opener>
|
||||
<a ng-click="openPlunkr('{$ doc.path $}', $event)" class="btn pull-right">
|
||||
<i class="glyphicon glyphicon-edit"> </i>
|
||||
Edit in Plunker</a>
|
||||
|
||||
<div class="runnable-example"
|
||||
path="{$ doc.example.deployments.default.path $}"
|
||||
@@ -22,5 +24,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Be aware that we need these extra new lines here or marked will not realize that the <div>
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
@@ -140,7 +140,7 @@ or JavaScript callbacks.
|
||||
{@link ngAnimate#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link ng.$animate $animate} to trigger animation operations within your directive code.
|
||||
Use {@link ngAnimate.$animate $animate} to trigger animation operations within your directive code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $animate:nocb
|
||||
@fullName Do not pass a callback to animate methods
|
||||
@description
|
||||
|
||||
Since Angular 1.3, the methods of {@link ng.$animate} do not accept a callback as the last parameter.
|
||||
Instead, they return a promise to which you can attach `then` handlers to be run when the animation completes.
|
||||
|
||||
If you are getting this error then you need to update your code to use the promise-based API.
|
||||
|
||||
See https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9 for information about
|
||||
the change to the animation API and the changes you need to make.
|
||||
@@ -1,8 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $compile:baddir
|
||||
@fullName Invalid Directive/Component Name
|
||||
@description
|
||||
|
||||
This error occurs when the name of a directive or component is not valid.
|
||||
|
||||
Directives and Components must start with a lowercase character and must not contain leading or trailing whitespaces.
|
||||
@@ -8,7 +8,7 @@ but the required directive controller is not present on the current DOM element
|
||||
|
||||
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
|
||||
|
||||
If the required controller is expected to be on an ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
|
||||
If the required controller is expected to be on a ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
|
||||
|
||||
If the required controller is optionally requested, use `?` or `^?` to specify that.
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $compile:infchng
|
||||
@fullName Unstable `$onChanges` hooks
|
||||
@description
|
||||
|
||||
This error occurs when the application's model becomes unstable because some `$onChanges` hooks are causing updates which then trigger
|
||||
further calls to `$onChanges` that can never complete.
|
||||
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
|
||||
|
||||
For example, the situation can occur by setting up a `$onChanges()` hook which triggers an event on the component, which subsequently
|
||||
triggers the component's bound inputs to be updated:
|
||||
|
||||
```html
|
||||
<c1 prop="a" on-change="a = -a"></c1>
|
||||
```
|
||||
|
||||
```js
|
||||
function Controller1() {}
|
||||
Controller1.$onChanges = function() {
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
mod.component('c1', {
|
||||
controller: Controller1,
|
||||
bindings: {'prop': '<', onChange: '&'}
|
||||
}
|
||||
```
|
||||
|
||||
The maximum number of allowed iterations of the `$onChanges` hooks is controlled via TTL setting which can be configured via
|
||||
{@link ng.$compileProvider#onChangesTtl `$compileProvider.onChangesTtl`}.
|
||||
@@ -3,7 +3,7 @@
|
||||
@fullName Invalid Isolate Scope Definition
|
||||
@description
|
||||
|
||||
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=<`), after which comes an optional `?`, and it ends with an optional local name.
|
||||
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=`) with an optional local name.
|
||||
|
||||
```
|
||||
myModule.directive('directiveName', function factory() {
|
||||
@@ -12,11 +12,9 @@ myModule.directive('directiveName', function factory() {
|
||||
scope: {
|
||||
'attrName': '@', // OK
|
||||
'attrName2': '=localName', // OK
|
||||
'attrName3': '<?localName', // OK
|
||||
'attrName4': ' = name', // OK
|
||||
'attrName5': 'name', // ERROR: missing mode @&=
|
||||
'attrName6': 'name=', // ERROR: must be prefixed with @&=
|
||||
'attrName7': '=name?', // ERROR: ? must come directly after the mode
|
||||
'attrName3': 'name', // ERROR: missing mode @&=
|
||||
'attrName4': ' = name', // ERROR: extra spaces
|
||||
'attrName5': 'name=', // ERROR: must be prefixed with @&=
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $compile:noctrl
|
||||
@fullName Controller is required.
|
||||
@description
|
||||
|
||||
When using the `bindToController` feature of AngularJS, a directive is required
|
||||
to have a Controller. A controller may be specified by adding a "controller"
|
||||
property to the directive definition object. Its value should be either a
|
||||
string, or an invokable object (a function, or an array whose last element is a
|
||||
function).
|
||||
|
||||
For more information, see the {@link guide/directive directives guide}.
|
||||
@@ -1,71 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $compile:noident
|
||||
@fullName Controller identifier is required.
|
||||
@description
|
||||
|
||||
When using the `bindToController` feature of AngularJS, a directive is required
|
||||
to have a Controller identifier, which is initialized in scope with the value of
|
||||
the controller instance. This can be supplied using the "controllerAs" property
|
||||
of the directive object, or alternatively by adding " as IDENTIFIER" to the controller
|
||||
name.
|
||||
|
||||
For example, the following directives are valid:
|
||||
|
||||
```js
|
||||
// OKAY, because controller is a string with an identifier component.
|
||||
directive("okay", function() {
|
||||
return {
|
||||
bindToController: true,
|
||||
controller: "myCtrl as $ctrl"
|
||||
scope: {
|
||||
text: "@text"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// OKAY, because the directive uses the controllerAs property to override
|
||||
// the controller identifier.
|
||||
directive("okay2", function() {
|
||||
return {
|
||||
bindToController: true,
|
||||
controllerAs: "$ctrl",
|
||||
controller: function() {
|
||||
|
||||
},
|
||||
scope: {
|
||||
text: "@text"
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
While the following are invalid:
|
||||
|
||||
```js
|
||||
// BAD, because the controller property is a string with no identifier.
|
||||
directive("bad", function() {
|
||||
return {
|
||||
bindToController: true,
|
||||
controller: "noIdentCtrl",
|
||||
scope: {
|
||||
text: "@text"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// BAD because the controller is not a string (therefore has no identifier),
|
||||
// and there is no controllerAs property.
|
||||
directive("bad2", function() {
|
||||
return {
|
||||
bindToController: true,
|
||||
controller: function noControllerAs() {
|
||||
|
||||
},
|
||||
scope: {
|
||||
text: "@text"
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
@@ -36,25 +36,9 @@ Following are invalid uses of this directive:
|
||||
```
|
||||
|
||||
|
||||
To resolve this error, do one of the following options:
|
||||
|
||||
- use path expressions with scope properties that are two-way data-bound like so:
|
||||
|
||||
To resolve this error, always use path expressions with scope properties that are two-way data-bound:
|
||||
```
|
||||
<my-directive bind="some.property">
|
||||
<my-directive bind="some[3]['property']">
|
||||
```
|
||||
|
||||
- Make the binding optional
|
||||
|
||||
```
|
||||
myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
...
|
||||
scope: {
|
||||
localValue: '=?bind' // <-- the '?' makes it optional
|
||||
}
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -31,7 +31,7 @@ single root element, like the `div` element in this template:
|
||||
<div><b>Hello</b> World!</div>
|
||||
```
|
||||
|
||||
An invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
An an invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
elements. For example:
|
||||
|
||||
```
|
||||
@@ -43,7 +43,7 @@ well. Consider the following template:
|
||||
|
||||
```
|
||||
<div class='container'>
|
||||
<div class='wrapper'>
|
||||
<div class='wrapper>
|
||||
...
|
||||
</div> <!-- wrapper -->
|
||||
</div> <!-- container -->
|
||||
|
||||
@@ -11,7 +11,7 @@ Supported formats:
|
||||
1. `__name__`
|
||||
2. `__name__ as __identifier__`
|
||||
|
||||
Neither `__name__` or `__identifier__` may contain spaces.
|
||||
N'either `__name__` or `__identifier__` may contain spaces.
|
||||
|
||||
Example of incorrect usage that leads to this error:
|
||||
```html
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $http:legacy
|
||||
@fullName The `success` and `error` methods on the promise returned from `$http` have been disabled.
|
||||
@description
|
||||
|
||||
This error occurs when the legacy promise extensions (`success` and `error`)
|
||||
{@link $httpProvider#useLegacyPromiseExtensions legacy `$http` promise extensions} have been disabled.
|
||||
|
||||
To resolve this error, either turn on the legacy extensions by adding
|
||||
`$httpProvider.useLegacyPromiseExtensions(true);` to your application's configuration; or refactor you
|
||||
use of `$http` to use `.then()` rather than `.success()` and `.error()`.
|
||||
|
||||
For example if you code looked like this:
|
||||
|
||||
```js
|
||||
// Simple GET request example :
|
||||
$http.get('/someUrl').
|
||||
success(function(data, status, headers, config) {
|
||||
// This callback will be called asynchronously
|
||||
// when the response is available
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
// called asynchronously if an error occurs
|
||||
// or server returns response with an error status.
|
||||
});
|
||||
```
|
||||
|
||||
then you would change it to look like:
|
||||
|
||||
```js
|
||||
// Simple GET request example :
|
||||
$http.get('/someUrl').
|
||||
then(function(response) {
|
||||
// (The response object contains the data, status, headers and config properties)
|
||||
// This callback will be called asynchronously
|
||||
// when the response is available.
|
||||
}, function(response) {
|
||||
// called asynchronously if an error occurs
|
||||
// or server returns response with an error status.
|
||||
});
|
||||
```
|
||||
|
||||
For more information, see the
|
||||
{@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`}
|
||||
documentation.
|
||||
@@ -6,9 +6,6 @@
|
||||
This error occurs when a module fails to load due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
||||
A common reason why the module fails to load is that you've forgotten to
|
||||
include the file with the defined module or that the file couldn't be loaded.
|
||||
|
||||
### Using `ngRoute`
|
||||
|
||||
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
|
||||
@@ -27,4 +24,4 @@ angular.module('ng').filter('tel', function (){});
|
||||
|
||||
Instead create your own module and add it as a dependency to your application's top-level module.
|
||||
See [#9692](https://github.com/angular/angular.js/issues/9692) and
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
@@ -69,18 +69,3 @@ angular.module('myModule', [])
|
||||
```
|
||||
|
||||
Use the `$controller` service if you want to instantiate controllers yourself.
|
||||
|
||||
Attempting to inject a scope object into anything that's not a controller or a directive,
|
||||
for example a service, will also throw an `Unknown provider: $scopeProvider <- $scope` error.
|
||||
This might happen if one mistakenly registers a controller as a service, ex.:
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('MyController', ['$scope', function($scope) {
|
||||
// This controller throws an unknown provider error because
|
||||
// a scope object cannot be injected into a service.
|
||||
}]);
|
||||
```
|
||||
|
||||
If you encounter this error only with minified code, consider using `ngStrictDi` (see
|
||||
{@link ng.directive:ngApp ngApp}) to provoke the error with the non-minified source.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:badexpr
|
||||
@fullName Expecting end operator
|
||||
@description
|
||||
|
||||
The Angular expression is missing the corresponding closing operator.
|
||||
@@ -1,11 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:dupvalue
|
||||
@fullName Duplicate choice in plural/select
|
||||
@description
|
||||
|
||||
You have repeated a match selection for your plural or select MessageFormat
|
||||
extension in your interpolation expression. The different choices have to be unique.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:logicbug
|
||||
@fullName Bug in ngMessageFormat module
|
||||
@description
|
||||
|
||||
You've just hit a bug in the ngMessageFormat module provided by angular-message-format.min.js.
|
||||
Please file a github issue for this and provide the interpolation text that caused you to hit this
|
||||
bug mentioning the exact version of AngularJS used and we will fix it!
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,17 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:nochgmustache
|
||||
@fullName Redefinition of start/endSymbol incompatible with MessageFormat extensions
|
||||
@description
|
||||
|
||||
You have redefined `$interpolate.startSymbol`/`$interpolate.endSymbol` and also
|
||||
loaded the `ngMessageFormat` module (provided by angular-message-format.min.js)
|
||||
while creating your injector.
|
||||
|
||||
`ngMessageFormat` currently does not support redefinition of the
|
||||
startSymbol/endSymbol used by `$interpolate`. If this is affecting you, please
|
||||
file an issue and mention @chirayuk on it. This is intended to be fixed in a
|
||||
future commit and the github issue will help gauge urgency.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqarg
|
||||
@fullName Missing required argument for MessageFormat
|
||||
@description
|
||||
|
||||
You must specify the MessageFormat function that you're using right after the
|
||||
comma following the Angular expression. Currently, the supported functions are
|
||||
"plural" and "select" (for gender selections.)
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,11 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqcomma
|
||||
@fullName Missing comma following MessageFormat plural/select keyword
|
||||
@description
|
||||
|
||||
The MessageFormat syntax requires a comma following the "plural" or "select"
|
||||
extension keyword in the extended interpolation syntax.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,11 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqendbrace
|
||||
@fullName Unterminated message for plural/select value
|
||||
@description
|
||||
|
||||
The plural or select message for a value or keyword choice has no matching end
|
||||
brace to mark the end of the message.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,6 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqendinterp
|
||||
@fullName Unterminated interpolation
|
||||
@description
|
||||
|
||||
The interpolation text does not have an ending `endSymbol` ("}}" by default) and is unterminated.
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqopenbrace
|
||||
@fullName An opening brace was expected but not found
|
||||
@description
|
||||
|
||||
The plural or select extension keyword or values (such as "other", "male",
|
||||
"female", "=0", "one", "many", etc.) MUST be followed by a message enclosed in
|
||||
braces.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,13 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:reqother
|
||||
@fullName Required choice "other" for select/plural in MessageFormat
|
||||
@description
|
||||
|
||||
Your interpolation expression with a MessageFormat extension for either
|
||||
"plural" or "select" (typically used for gender selection) does not contain a
|
||||
message for the choice "other". Using either select or plural MessageFormat
|
||||
extensions require that you provide a message for the selection "other".
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:unknarg
|
||||
@fullName Unrecognized MessageFormat extension
|
||||
@description
|
||||
|
||||
The MessageFormat extensions provided by `ngMessageFormat` are currently
|
||||
limited to "plural" and "select". The extension that you have used is either
|
||||
unsupported or invalid.
|
||||
|
||||
For more information about the MessageFormat syntax in interpolation
|
||||
expressions, please refer to MessageFormat extensions section at
|
||||
{@link guide/i18n#MessageFormat Angular i18n MessageFormat}
|
||||
@@ -1,10 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:unsafe
|
||||
@fullName MessageFormat extensions not allowed in secure context
|
||||
@description
|
||||
|
||||
You have attempted to use a MessageFormat extension in your interpolation expression that is marked as a secure context. For security purposes, this is not supported.
|
||||
|
||||
Read more about secure contexts at {@link ng.$sce Strict Contextual Escaping
|
||||
(SCE)} and about the MessageFormat extensions at {@link
|
||||
guide/i18n#MessageFormat Angular i18n MessageFormat}.
|
||||
@@ -1,6 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:untermstr
|
||||
@fullName Unterminated string literal
|
||||
@description
|
||||
|
||||
The string literal was not terminated in your Angular expression.
|
||||
@@ -1,8 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $interpolate:wantstring
|
||||
@fullName Expected the beginning of a string
|
||||
@description
|
||||
|
||||
We expected to see the beginning of a string (either a single quote or a double
|
||||
quote character) in the expression but it was not found. The expression is
|
||||
invalid. If this is incorrect, please file an issue on github.
|
||||
@@ -1,6 +1,6 @@
|
||||
@ngdoc error
|
||||
@name $location:nobase
|
||||
@fullName $location in HTML5 mode requires a <base> tag to be present!
|
||||
@fullName $location in HTML5 mode requires a <base> tag to be present!
|
||||
@description
|
||||
|
||||
If you configure {@link ng.$location `$location`} to use
|
||||
@@ -15,7 +15,7 @@ $locationProvider.html5Mode({
|
||||
});
|
||||
```
|
||||
|
||||
Note that removing the requirement for a `<base>` tag will have adverse side effects when resolving
|
||||
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
|
||||
relative paths with `$location` in IE9.
|
||||
|
||||
The base URL is then used to resolve all relative URLs throughout the application regardless of the
|
||||
@@ -35,7 +35,7 @@ URL of the subcontext:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<base href="/subapp/">
|
||||
<base href="/subapp">
|
||||
...
|
||||
</head>
|
||||
```
|
||||
|
||||
@@ -14,32 +14,3 @@ perform this check - it's up to the developer to not expose such sensitive and p
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid Window access.
|
||||
|
||||
### Common CoffeeScript Issue
|
||||
|
||||
Be aware that if you are using CoffeeScript, it automatically returns the value of the last statement in a
|
||||
function. So for instance
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
```
|
||||
|
||||
compiles to something like
|
||||
|
||||
```js
|
||||
scope.foo = function() {
|
||||
return window.open('https://example.com');
|
||||
};
|
||||
```
|
||||
|
||||
You can see that this function will return the result of calling `window.open`, which is a `Window`
|
||||
object.
|
||||
|
||||
You can avoid this by explicitly returning something else from the function:
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
return true;
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ To resolve this type of issue, either fix the api to be always synchronous or as
|
||||
your callback handler to always run asynchronously by using the `$timeout` service.
|
||||
|
||||
```
|
||||
function MyController($scope, $timeout, thirdPartyComponent) {
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
$timeout(function() {
|
||||
$scope.someData = someData;
|
||||
@@ -161,7 +161,7 @@ In this second scenario, we are already inside a `$digest` when the ngFocus dire
|
||||
call to `$apply()`, causing this error to be thrown.
|
||||
|
||||
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in an
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
|
||||
`$apply` block:
|
||||
|
||||
```
|
||||
@@ -200,7 +200,7 @@ the top of the call stack.
|
||||
Once you have identified this call you work your way up the stack to see what the problem is.
|
||||
|
||||
* If the second call was made in your application code then you should look at why this code has been
|
||||
called from within an `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
sync/async scenario described earlier.
|
||||
|
||||
* If the second call was made inside an Angular directive then it is likely that it matches the second
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:badparse
|
||||
@fullName Parsing Error while Sanitizing
|
||||
@description
|
||||
|
||||
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
|
||||
The error contains part of the html string that can't be parsed.
|
||||
|
||||
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
|
||||
|
||||
If a valid html code results in this error, please file a bug.
|
||||
@@ -1,10 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:noinert
|
||||
@fullName Can't create an inert html document
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser.
|
||||
|
||||
This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input.
|
||||
|
||||
The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that Angular's `$sanitize` is being used on an unsupported platform.
|
||||
@@ -1,13 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:uinput
|
||||
@fullName Failed to sanitize html because the input is unstable
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification
|
||||
errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS
|
||||
vulnerability in the browser.
|
||||
|
||||
mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized
|
||||
doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks
|
||||
harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after
|
||||
sanitization.
|
||||
@@ -1,51 +0,0 @@
|
||||
@ngdoc error
|
||||
@name filter:notarray
|
||||
@fullName Not an array
|
||||
@description
|
||||
|
||||
This error occurs when {@link ng.filter filter} is not used with an array:
|
||||
```html
|
||||
<input ng-model="search">
|
||||
<div ng-repeat="(key, value) in myObj | filter:search">
|
||||
{{ key }} : {{ value }}
|
||||
</div>
|
||||
```
|
||||
|
||||
Filter must be used with an array so a subset of items can be returned.
|
||||
The array can be initialized asynchronously and therefore null or undefined won't throw this error.
|
||||
|
||||
To filter an object by the value of its properties you can create your own custom filter:
|
||||
```js
|
||||
angular.module('customFilter',[])
|
||||
.filter('custom', function() {
|
||||
return function(input, search) {
|
||||
if (!input) return input;
|
||||
if (!search) return input;
|
||||
var expected = ('' + search).toLowerCase();
|
||||
var result = {};
|
||||
angular.forEach(input, function(value, key) {
|
||||
var actual = ('' + value).toLowerCase();
|
||||
if (actual.indexOf(expected) !== -1) {
|
||||
result[key] = value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
});
|
||||
```
|
||||
That can be used as:
|
||||
```html
|
||||
<input ng-model="search">
|
||||
<div ng-repeat="(key, value) in myObj | custom:search">
|
||||
{{ key }} : {{ value }}
|
||||
</div>
|
||||
```
|
||||
|
||||
You could as well convert the object to an array using a filter such as
|
||||
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
|
||||
```html
|
||||
<input ng-model="search">
|
||||
<div ng-repeat="item in myObj | toArray:false | filter:search">
|
||||
{{ item }}
|
||||
</div>
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
@ngdoc error
|
||||
@name linky:notstring
|
||||
@fullName Not a string
|
||||
@description
|
||||
|
||||
This error occurs when {@link ngSanitize.linky linky} is used with a non-empty, non-string value:
|
||||
```html
|
||||
<div ng-bind-html="42 | linky"></div>
|
||||
```
|
||||
|
||||
`linky` is supposed to be used with string values only, and therefore assumes that several methods
|
||||
(such as `.match()`) are available on the passed in value.
|
||||
The value can be initialized asynchronously and therefore null or undefined won't throw this error.
|
||||
|
||||
If you want to pass non-string values to `linky` (e.g. Objects whose `.toString()` should be
|
||||
utilized), you need to manually convert them to strings.
|
||||
@@ -1,7 +0,0 @@
|
||||
@ngdoc error
|
||||
@name ng:cpta
|
||||
@fullName Copying TypedArray
|
||||
@description
|
||||
|
||||
Copying TypedArray's with a destination is not supported because TypedArray
|
||||
objects can not be mutated, they are fixed length.
|
||||
@@ -1,28 +0,0 @@
|
||||
@ngdoc error
|
||||
@name ngModel:nopromise
|
||||
@fullName No promise
|
||||
@description
|
||||
|
||||
The return value of an async validator, must always be a promise. If you want to return a
|
||||
non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or
|
||||
{@link ng.$q#reject `$q.reject()`}.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
.directive('asyncValidator', function($q) {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elem, attrs, ngModel) {
|
||||
ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) {
|
||||
if (/* I don't need to hit the backend API */) {
|
||||
return $q.resolve(); // to mark as valid or
|
||||
// return $q.reject(); // to mark as invalid
|
||||
} else {
|
||||
// ...send a request to the backend and return a promise
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
```
|
||||
@@ -1,56 +0,0 @@
|
||||
@ngdoc error
|
||||
@name ngModel:numfmt
|
||||
@fullName Model is not of type `number`
|
||||
@description
|
||||
|
||||
The number input directive `<input type="number">` requires the model to be a `number`.
|
||||
|
||||
If the model is something else, this error will be thrown.
|
||||
|
||||
Angular does not set validation errors on the `<input>` in this case
|
||||
as this error is caused by incorrect application logic and not by bad input from the user.
|
||||
|
||||
If your model does not contain actual numbers then it is up to the application developer
|
||||
to use a directive that will do the conversion in the `ngModel` `$formatters` and `$parsers`
|
||||
pipeline.
|
||||
|
||||
## Example
|
||||
|
||||
In this example, our model stores the number as a string, so we provide the `stringToNumber`
|
||||
directive to convert it into the format the `input[number]` directive expects.
|
||||
|
||||
|
||||
<example module="numfmt-error-module">
|
||||
<file name="index.html">
|
||||
<table>
|
||||
<tr ng-repeat="x in ['0', '1']">
|
||||
<td>
|
||||
<input type="number" string-to-number ng-model="x" /> {{ x }} : {{ typeOf(x) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('numfmt-error-module', [])
|
||||
|
||||
.run(function($rootScope) {
|
||||
$rootScope.typeOf = function(value) {
|
||||
return typeof value;
|
||||
};
|
||||
})
|
||||
|
||||
.directive('stringToNumber', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
ngModel.$parsers.push(function(value) {
|
||||
return '' + value;
|
||||
});
|
||||
ngModel.$formatters.push(function(value) {
|
||||
return parseFloat(value, 10);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -0,0 +1,33 @@
|
||||
@ngdoc error
|
||||
@name ngOptions:trkslct
|
||||
@fullName Comprehension expression cannot contain both `select as` and `track by` expressions.
|
||||
@description
|
||||
|
||||
NOTE: This error was introduced in 1.3.0-rc.5, and was removed for 1.3.0-rc.6 in order to
|
||||
not break existing apps.
|
||||
|
||||
This error occurs when 'ngOptions' is passed a comprehension expression that contains both a
|
||||
`select as` expression and a `track by` expression. These two expressions are fundamentally
|
||||
incompatible.
|
||||
|
||||
* Example of bad expression: `<select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">`
|
||||
`values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}]`,
|
||||
`$scope.selected = {name: 'aSubItem'};`
|
||||
* track by is always applied to `value`, with purpose to preserve the selection,
|
||||
(to `item` in this case)
|
||||
* To calculate whether an item is selected, `ngOptions` does the following:
|
||||
1. apply `track by` to the values in the array:
|
||||
In the example: [1,2]
|
||||
2. apply `track by` to the already selected value in `ngModel`:
|
||||
In the example: this is not possible, as `track by` refers to `item.id`, but the selected
|
||||
value from `ngModel` is `{name: aSubItem}`.
|
||||
|
||||
Here's an example of how to make this example work by using `track by` without `select as`:
|
||||
|
||||
```
|
||||
<select ng-model="selected" ng-options="item.label for item in values track by item.id">
|
||||
```
|
||||
|
||||
Note: This would store the whole `item` as the model to `scope.selected` instead of `item.subItem`.
|
||||
|
||||
For more information on valid expression syntax, see 'ngOptions' in {@link ng.directive:select select} directive docs.
|
||||
@@ -1,52 +0,0 @@
|
||||
@ngdoc error
|
||||
@name orderBy:notarray
|
||||
@fullName Value is not array-like
|
||||
@description
|
||||
|
||||
This error occurs when {@link ng.orderBy orderBy} is not passed an array-like value:
|
||||
```html
|
||||
<div ng-repeat="(key, value) in myObj | orderBy:someProp">
|
||||
{{ key }} : {{ value }}
|
||||
</div>
|
||||
```
|
||||
|
||||
`orderBy` must be used with an array-like value so a subset of items can be returned.
|
||||
The array can be initialized asynchronously and therefore `null` or `undefined` won't throw this error.
|
||||
|
||||
To use `orderBy` to order the properties of an object, you can create your own array based on that object:
|
||||
```js
|
||||
angular.module('aModule', [])
|
||||
.controller('aController', function($scope) {
|
||||
var myObj = {
|
||||
one: {id: 1, name: 'Some thing'},
|
||||
two: {id: 2, name: 'Another thing'},
|
||||
three: {id: 3, name: 'A third thing'}
|
||||
};
|
||||
|
||||
$scope.arrFromMyObj = Object.keys(myObj).map(function(key) {
|
||||
return myObj[key];
|
||||
});
|
||||
});
|
||||
```
|
||||
That can be used as:
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in arrFromMyObj | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
|
||||
You could as well convert the object to an array using a filter such as
|
||||
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in myObj | toArray:false | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
@@ -165,7 +165,7 @@ encoded.
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
[HTML5 History API](https://html.spec.whatwg.org/multipage/browsers.html#the-history-interface). Applications use the same API in
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/introduction.html#history-0). Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
@@ -301,7 +301,7 @@ it('should show example', inject(
|
||||
### Fallback for legacy browsers
|
||||
|
||||
For browsers that support the HTML5 history API, `$location` uses the HTML5 history API to write
|
||||
path and search. If the history API is not supported by a browser, `$location` supplies a Hashbang
|
||||
path and search. If the history API is not supported by a browser, `$location` supplies a Hasbang
|
||||
URL. This frees you from having to worry about whether the browser viewing your app supports the
|
||||
history API or not; the `$location` service makes this transparent to you.
|
||||
|
||||
@@ -330,8 +330,8 @@ reload to the original link.
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url
|
||||
base in the head of your main html file (`<base href="/my-base/index.html">`) unless `html5Mode.requireBase`
|
||||
is set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
|
||||
base in the head of your main html file (`<base href="/my-base">`) unless `html5Mode.requireBase` is
|
||||
set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
|
||||
that, relative urls will always be resolved to this base url, even if the initial url of the
|
||||
document was different.
|
||||
|
||||
@@ -339,7 +339,6 @@ There is one exception: Links that only contain a hash fragment (e.g. `<a href="
|
||||
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
|
||||
to anchors on the same page without needing to know on which page the user currently is.
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
@@ -347,20 +346,6 @@ to entry point of your application (e.g. index.html). Requiring a `<base>` tag i
|
||||
this case, as it allows Angular to differentiate between the part of the url that is the application
|
||||
base and the path that should be handled by the application.
|
||||
|
||||
### Base href constraints
|
||||
|
||||
The `$location` service is not able to function properly if the current URL is outside the URL given
|
||||
as the base href. This can have subtle confusing consequencies...
|
||||
|
||||
Consider a base href set as follows: `<base href="/base/">` (i.e. the application exists in the "folder"
|
||||
called `/base`). The URL `/base` is actually outside the application (it refers to the `base` file found
|
||||
in the root `/` folder).
|
||||
|
||||
If you wish to be able to navigate to the application via a URL such as `/base` then you should ensure that
|
||||
you server is setup to redirect such requests to `/base/`.
|
||||
|
||||
See https://github.com/angular/angular.js/issues/14018 for more information.
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
|
||||
@@ -371,15 +356,15 @@ legacy browsers and hashbang links in modern browser:
|
||||
|
||||
### Example
|
||||
|
||||
Here you can see two `$location` instances that show the difference between **Html5 mode** and **Html5 Fallback mode**.
|
||||
Note that to simulate different levels of browser support, the `$location` instances are connected to
|
||||
a fakeBrowser service, which you don't have to set up in actual projects.
|
||||
Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
|
||||
that you can see the differences. These `$location` services are connected to a fake browsers. Each
|
||||
input represents the address bar of the browser.
|
||||
|
||||
Note that when you type hashbang url into the first browser (or vice versa) it doesn't rewrite /
|
||||
Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.
|
||||
|
||||
In these examples we use `<base href="/base/index.html" />`. The inputs represent the address bar of the browser.
|
||||
In these examples we use `<base href="/base/index.html" />`
|
||||
|
||||
#### Browser in HTML5 mode
|
||||
<example module="html5-mode" name="location-html5-mode">
|
||||
@@ -404,7 +389,6 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
|
||||
<file name="app.js">
|
||||
angular.module('html5-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
// Configure the fakeBrowser. Do not set these values in actual projects.
|
||||
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: true })
|
||||
@@ -554,7 +538,6 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
|
||||
<file name="app.js">
|
||||
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
// Configure the fakeBrowser. Do not set these values in actual projects.
|
||||
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: false })
|
||||
@@ -710,7 +693,7 @@ A path should always begin with forward slash (`/`); the `$location.path()` sett
|
||||
forward slash if it is missing.
|
||||
|
||||
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
|
||||
`hashPrefix`.
|
||||
hashPrefix.
|
||||
|
||||
## Crawling your app
|
||||
|
||||
@@ -786,8 +769,8 @@ then uses the information it obtains to compose hashbang URLs (such as
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<th>Navigation outside the app</td>
|
||||
<th>Use lower level API</td>
|
||||
<td>Navigation outside the app</td>
|
||||
<td>Use lower level API</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -801,8 +784,8 @@ then uses the information it obtains to compose hashbang URLs (such as
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<th>Read access</td>
|
||||
<th>Change to</td>
|
||||
<td>Read access</td>
|
||||
<td>Change to</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
@@ -23,9 +23,9 @@ angular.module('myApp', ['ngAria'])...
|
||||
###Using ngAria
|
||||
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
|
||||
added it as a dependency, you can test a few things:
|
||||
* Using your favorite element inspector, look for attributes added by ngAria in your own code.
|
||||
* Using your favorite element inspector, look for ngAria attributes in your own code.
|
||||
* Test using your keyboard to ensure `tabindex` is used correctly.
|
||||
* Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
|
||||
* Fire up a screen reader such as VoiceOver to listen for ARIA support.
|
||||
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
|
||||
|
||||
##Supported directives
|
||||
@@ -33,20 +33,16 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
* {@link guide/accessibility#ngmodel ngModel}
|
||||
* {@link guide/accessibility#ngdisabled ngDisabled}
|
||||
* {@link guide/accessibility#ngrequired ngRequired}
|
||||
* {@link guide/accessibility#ngvaluechecked ngChecked}
|
||||
* {@link guide/accessibility#ngvaluechecked ngValue}
|
||||
* {@link guide/accessibility#ngshow ngShow}
|
||||
* {@link guide/accessibility#nghide ngHide}
|
||||
* {@link guide/accessibility#ngclick ngClick}
|
||||
* {@link guide/accessibility#ngdblclick ngDblClick}
|
||||
* {@link guide/accessibility#ngmessages ngMessages}
|
||||
|
||||
<h2 id="ngmodel">ngModel</h2>
|
||||
|
||||
Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel}
|
||||
directive. For elements using ngModel, special attention is paid by ngAria if that element also
|
||||
has a role or type of `checkbox`, `radio`, `range` or `textbox`.
|
||||
Most of ngAria's heavy lifting happens in the {@link ngModel ngModel}
|
||||
directive. For elements using ngModel, special attention is paid by ngAria if that element also
|
||||
has a a role or type of `checkbox`, `radio`, `range` or `textbox`.
|
||||
|
||||
For those elements using ngModel, ngAria will dynamically bind and update the following ARIA
|
||||
attributes (if they have not been explicitly specified by the developer):
|
||||
@@ -137,75 +133,37 @@ attributes (if they have not been explicitly specified by the developer):
|
||||
|
||||
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
|
||||
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
it with your keyboard and at least one mobile and desktop screen reader.
|
||||
|
||||
<h2 id="ngvaluechecked">ngValue and ngChecked</h2>
|
||||
|
||||
To ease the transition between native inputs and custom controls, ngAria now supports
|
||||
{@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}.
|
||||
The original directives were created for native inputs only, so ngAria extends
|
||||
support to custom elements by managing `aria-checked` for accessibility.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val"></custom-radio-button>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val" aria-checked="true"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>
|
||||
```
|
||||
operable** from the keybard. Think of `ng-click` on a `<div>` or `<md-checkbox>`: you still need
|
||||
to bind `ng-keypress` to make it fully operable from the keyboard. As a rule, any time you create
|
||||
a widget involving user interaction, be sure to test it with your keyboard and at least one mobile
|
||||
and desktop screen reader (preferably more).
|
||||
|
||||
<h2 id="ngdisabled">ngDisabled</h2>
|
||||
|
||||
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
|
||||
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
|
||||
using ngAria with {@link ng.ngDisabled ngDisabled} will also
|
||||
using ngAria with [ngDisabled](https://docs.angularjs.org/api/ng/directive/ngDisabled) will also
|
||||
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
|
||||
custom controls to be more accessible.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-disabled="disabled"></md-checkbox>
|
||||
<md-checkbox ng-disabled="disabled">
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox disabled aria-disabled="true"></md-checkbox>
|
||||
<md-checkbox disabled aria-disabled="true">
|
||||
```
|
||||
|
||||
>You can check whether a control is legitimately disabled for a screen reader by visiting
|
||||
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
|
||||
|
||||
<h2 id="ngrequired">ngRequired</h2>
|
||||
|
||||
The boolean `required` attribute is only valid for native form controls such as `input` and
|
||||
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
|
||||
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
|
||||
`aria-required`. This tells accessibility APIs when a custom control is required.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val"></md-checkbox>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
|
||||
```
|
||||
[chrome://accessibility](chrome://accessibility).
|
||||
|
||||
<h2 id="ngshow">ngShow</h2>
|
||||
|
||||
>The {@link ng.ngShow ngShow} directive shows or hides the
|
||||
>The [ngShow](https://docs.angularjs.org/api/ng/directive/ngShow) directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngShow` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -242,7 +200,7 @@ Becomes:
|
||||
|
||||
<h2 id="nghide">ngHide</h2>
|
||||
|
||||
>The {@link ng.ngHide ngHide} directive shows or hides the
|
||||
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngHide` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -251,25 +209,10 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
|
||||
`display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.
|
||||
|
||||
<h2><span id="ngclick">ngClick</span> and <span id="ngdblclick">ngDblclick</span></h2>
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
|
||||
a node blacklist:
|
||||
|
||||
* Button
|
||||
* Anchor
|
||||
* Input
|
||||
* Textarea
|
||||
* Select
|
||||
* Details/Summary
|
||||
|
||||
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
|
||||
dynamically bind a keypress event by default as long as the element isn't in the node blacklist.
|
||||
You can turn this functionality on or off with the `bindKeypress` configuration option.
|
||||
|
||||
ngAria will also add the `button` role to communicate to users of assistive technologies. This can
|
||||
be disabled with the `bindRoleForClick` configuration option.
|
||||
|
||||
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
|
||||
such as `div` or `taco-button` to enable keyboard access.
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex` if it isn't there already.
|
||||
Even with this, you must currently still add `ng-keypress` to non-interactive elements such as `div`
|
||||
or `taco-button` to enable keyboard access. Conversation is currently ongoing about whether ngAria
|
||||
should also bind `ng-keypress`.
|
||||
|
||||
<h3>Example</h3>
|
||||
```html
|
||||
@@ -280,12 +223,13 @@ Becomes:
|
||||
```html
|
||||
<div ng-click="toggleMenu()" tabindex="0"></div>
|
||||
```
|
||||
*Note: ngAria still requires `ng-keypress` to be added manually to non-native controls like divs.*
|
||||
|
||||
<h2 id="ngmessages">ngMessages</h2>
|
||||
|
||||
The new ngMessages module makes it easy to display form validation or other messages with priority
|
||||
sequencing and animation. To expose these visual messages to screen readers,
|
||||
ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown,
|
||||
ngAria injects `aria-live="polite"`, causing them to be read aloud any time a message is shown,
|
||||
regardless of the user's focus location.
|
||||
###Example
|
||||
|
||||
@@ -299,7 +243,7 @@ regardless of the user's focus location.
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<div ng-messages="myForm.myName.$error" aria-live="assertive">
|
||||
<div ng-messages="myForm.myName.$error" aria-live="polite">
|
||||
<div ng-message="required">You did not enter a field</div>
|
||||
<div ng-message="maxlength">Your field is too long</div>
|
||||
</div>
|
||||
@@ -310,18 +254,62 @@ The attribute magic of ngAria may not work for every scenario. To disable indivi
|
||||
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
|
||||
tell ngAria to ignore the attribute globally.
|
||||
|
||||
<example module="ngAria_ngClickExample" deps="angular-aria.js">
|
||||
<example module="ngAria_ngDisabledExample" deps="angular-aria.js">
|
||||
<file name="index.html">
|
||||
<div ng-click="someFunction" show-attrs>
|
||||
<div> with ng-click and bindRoleForClick, tabindex set to false
|
||||
<style>
|
||||
[role=checkbox] {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
[role=checkbox] .icon:before {
|
||||
content: '\2610';
|
||||
display: inline-block;
|
||||
font-size: 2em;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
speak: none;
|
||||
}
|
||||
[role=checkbox].active .icon:before {
|
||||
content: '\2611';
|
||||
}
|
||||
</style>
|
||||
<form ng-controller="formsController">
|
||||
<div ng-model="someModel" show-attrs>
|
||||
Div with ngModel and aria-invalid disabled
|
||||
</div>
|
||||
<div role="checkbox" ng-model="checked" ng-class="{active: checked}"
|
||||
aria-label="Custom Checkbox" ng-click="toggleCheckbox()" some-checkbox show-attrs>
|
||||
<span class="icon" aria-hidden="true"></span>
|
||||
Custom Checkbox for comparison
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
angular.module('ngAria_ngClickExample', ['ngAria'], function config($ariaProvider) {
|
||||
angular.module('ngAria_ngDisabledExample', ['ngAria'], function config($ariaProvider) {
|
||||
$ariaProvider.config({
|
||||
bindRoleForClick: false,
|
||||
tabindex: false
|
||||
ariaInvalid: false,
|
||||
tabindex: true
|
||||
});
|
||||
})
|
||||
.controller('formsController', function($scope){
|
||||
$scope.checked = false;
|
||||
$scope.toggleCheckbox = function(){
|
||||
$scope.checked = !$scope.checked;
|
||||
}
|
||||
})
|
||||
.directive('someCheckbox', function(){
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, $el, $attrs) {
|
||||
$el.on('keypress', function(event){
|
||||
event.preventDefault();
|
||||
if(event.keyCode === 32 || event.keyCode === 13){
|
||||
$scope.toggleCheckbox();
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.directive('showAttrs', function() {
|
||||
return function(scope, el, attrs) {
|
||||
var pre = document.createElement('pre');
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# Animations
|
||||
|
||||
AngularJS provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
AngularJS 1.3 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
|
||||
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
|
||||
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
|
||||
@@ -236,7 +236,7 @@ The example below shows how to perform animations during class changes:
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Although the CSS is a little different than what we saw before, the idea is the same.
|
||||
Although the CSS is a little different then what we saw before, the idea is the same.
|
||||
|
||||
## Which directives support animations?
|
||||
|
||||
@@ -253,7 +253,7 @@ The table below explains in detail which animation events are triggered
|
||||
| {@link ng.directive:ngClass#animations ngClass or {{class}}} | add and remove |
|
||||
| {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link ng.$animate API docs}.
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link ngAnimate.$animate API docs}.
|
||||
|
||||
## How do I use animations in my own directives?
|
||||
|
||||
@@ -274,176 +274,8 @@ myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
}]);
|
||||
```
|
||||
|
||||
## Animations on app bootstrap / page load
|
||||
|
||||
By default, animations are disabled when the Angular app {@link guide/bootstrap bootstraps}. If you are using the {@link ngApp} directive,
|
||||
this happens in the `DOMContentLoaded` event, so immediately after the page has been loaded.
|
||||
Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on
|
||||
the page, the loading process may become too visually overwhelming, and the performance may suffer.
|
||||
|
||||
Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap have finished.
|
||||
Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest} and the one after that to finish.
|
||||
This ensures that the whole app has been compiled fully before animations are attempted.
|
||||
|
||||
If you do want your animations to play when the app bootstraps, you can enable animations globally in
|
||||
your main module's {@link angular.Module#run run} function:
|
||||
|
||||
```js
|
||||
myModule.run(function($animate) {
|
||||
$animate.enabled(true);
|
||||
});
|
||||
```
|
||||
|
||||
## How to (selectively) enable, disable and skip animations
|
||||
|
||||
There are three different ways to disable animations, both globally and for specific animations.
|
||||
Disabling specific animations can help to speed up the render performance, for example for large `ngRepeat`
|
||||
lists that don't actually have animations. Because ngAnimate checks at runtime if animations are present,
|
||||
performance will take a hit even if an element has no animation.
|
||||
|
||||
### In the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
|
||||
|
||||
This function can be called in the {@link angular.Module#config config} phase of an app. It takes a regex as the only argument,
|
||||
which will then be matched against the classes of any element that is about to be animated. The regex
|
||||
allows a lot of flexibility - you can either allow animations only for specific classes (useful when
|
||||
you are working with 3rd party animations), or exclude specific classes from getting animated.
|
||||
|
||||
```js
|
||||
app.config(function($animateProvider) {
|
||||
$animateProvider.classNameFilter(/animate-/);
|
||||
});
|
||||
```
|
||||
|
||||
```css
|
||||
/* prefixed with animate- */
|
||||
.animate-fade-add.animate-fade-add-active {
|
||||
transition:1s linear all;
|
||||
opacity:0;
|
||||
}
|
||||
```
|
||||
|
||||
The classNameFilter approach generally applies the biggest speed boost, because the matching is
|
||||
done before any other animation disabling strategies are checked. However, that also means it is not
|
||||
possible to override class name matching with the two following strategies. It's of course still possible
|
||||
to enable / disable animations by changing an element's class name at runtime.
|
||||
|
||||
### At runtime: {@link ng.$animate#enabled $animate.enabled()}
|
||||
|
||||
This function can be used to enable / disable animations in two different ways:
|
||||
|
||||
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
|
||||
disables all animations in your app.
|
||||
|
||||
When the second argument is a native DOM or jQuery element, the function enables / disables
|
||||
animations on this element *and all its children*: `$animate.enabled(false, myElement)`. This is the
|
||||
most flexible way to change the animation state. For example, even if you have used it to disable
|
||||
animations on a parent element, you can still re-enable it for a child element. And compared to the
|
||||
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
|
||||
|
||||
Note however that the `$animate.enabled()` state for individual elements does not overwrite disabling
|
||||
rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}.
|
||||
|
||||
### Via CSS styles: overwriting styles in the `ng-animate` CSS class
|
||||
Whenever an animation is started, ngAnimate applies the `ng-animate` class to the element for the
|
||||
whole duration of the animation. By applying CSS transition / animation styling to the class,
|
||||
you can skip an animation:
|
||||
|
||||
```css
|
||||
|
||||
.my-class{
|
||||
transition: transform 2s;
|
||||
}
|
||||
|
||||
.my-class:hover {
|
||||
transform: translateX(50px);
|
||||
}
|
||||
|
||||
my-class.ng-animate {
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
By setting `transition: 0s`, ngAnimate will ignore the existing transition styles, and not try to animate them (Javascript
|
||||
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
|
||||
issues with existing animations interfering with ngAnimate}.
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
|
||||
where the animated element is briefly visible.
|
||||
|
||||
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
|
||||
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
|
||||
animations (`enter`, `move`, and `leave`).
|
||||
|
||||
Here's an example where you might see flickering:
|
||||
|
||||
```html
|
||||
<div ng-class="{red: myProp}">
|
||||
<div ng-class="{blue: myProp}">
|
||||
<div class="message" ng-if="myProp"></div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
|
||||
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
|
||||
|
||||
```css
|
||||
.message.ng-enter-prepare {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Other animation styles ... */
|
||||
```
|
||||
|
||||
## Preventing Collisions with Existing Animations and Third Party Libraries
|
||||
By default, any `ngAnimate` enabled directives will assume any transition / animation styles on the
|
||||
element are part of an `ngAnimate` animation. This can lead to problems when the styles are actually
|
||||
for animations that are independent of `ngAnimate`.
|
||||
|
||||
For example, an element acts as a loading spinner. It has an inifinite css animation on it, and also an
|
||||
{@link ngIf `ngIf`} directive, for which no animations are defined:
|
||||
|
||||
```css
|
||||
@keyframes rotating {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
Now, when the `ngIf` changes, `ngAnimate` will see the spinner animation and use
|
||||
it to animate the `enter`/`leave` event, which doesn't work because
|
||||
the animation is infinite. The element will still be added / removed after a timeout, but there will be a
|
||||
noticable delay.
|
||||
|
||||
This might also happen because some third-party frameworks place animation duration defaults
|
||||
across many element or className selectors in order to make their code small and reuseable.
|
||||
|
||||
You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class that is added
|
||||
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
|
||||
case of the spinner, this would be:
|
||||
|
||||
```css
|
||||
.spinner.ng-animate {
|
||||
transition: 0s none;
|
||||
animation: 0s none;
|
||||
}
|
||||
```
|
||||
|
||||
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
|
||||
than any styles that are independent from ngAnimate.
|
||||
|
||||
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ngAnimate.$animate API documentation}.
|
||||
|
||||
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
|
||||
|
||||
@@ -53,13 +53,13 @@ initialization.
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
|
||||
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
|
||||
for the {@link ng.directive:ngApp `ngApp`} directive which designates your application root.
|
||||
If the {@link ng.directive:ngApp `ngApp`} directive is found then Angular will:
|
||||
for the {@link ng.directive:ngApp `ng-app`} directive which designates your application root.
|
||||
If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
|
||||
|
||||
* load the {@link guide/module module} associated with the directive.
|
||||
* create the application {@link auto.$injector injector}
|
||||
* compile the DOM treating the {@link ng.directive:ngApp
|
||||
`ngApp`} directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
`ng-app`} directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
portion of the DOM as an Angular application.
|
||||
|
||||
|
||||
@@ -142,17 +142,6 @@ This is the sequence that your code should follow:
|
||||
2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
## Things to keep in mind
|
||||
|
||||
There a few things to keep in mind regardless of automatic or manual bootstrapping:
|
||||
|
||||
- While it's possible to bootstrap more than one AngularJS application per page, we don't actively
|
||||
test against this scenario. It's possible that you'll run into problems, especially with complex apps, so
|
||||
caution is advised.
|
||||
- Do not bootstrap your app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, such as
|
||||
{@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
|
||||
Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
|
||||
causing animations to stop working and making the injector inaccessible from outside the app.
|
||||
|
||||
## Deferred Bootstrap
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,495 +0,0 @@
|
||||
@ngdoc overview
|
||||
@name Components
|
||||
@sortOrder 305
|
||||
@description
|
||||
|
||||
# Understanding Components
|
||||
|
||||
In Angular, a Component is a special kind of {@link guide/directive directive} that uses a simpler
|
||||
configuration which is suitable for a component-based application structure.
|
||||
|
||||
This makes it easier to write an app in a way that's similar to using Web Components or using Angular
|
||||
2's style of application architecture.
|
||||
|
||||
Advantages of Components:
|
||||
- simpler configuration than plain directives
|
||||
- promote sane defaults and best practices
|
||||
- optimized for component-based architecture
|
||||
- writing component directives will make it easier to upgrade to Angular 2
|
||||
|
||||
When not to use Components:
|
||||
|
||||
- for directives that rely on DOM manipulation, adding event listeners etc, because the compile
|
||||
and link functions are unavailable
|
||||
- when you need advanced directive definition options like priority, terminal, multi-element
|
||||
- when you want a directive that is triggered by an attribute or CSS class, rather than an element
|
||||
|
||||
## Creating and configuring a Component
|
||||
|
||||
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
|
||||
|
||||
* The name of the Component (as string).
|
||||
* The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.)
|
||||
|
||||
<example name="heroComponentSimple" module="heroApp">
|
||||
<file name="index.js">
|
||||
angular.module('heroApp', []).controller('mainCtrl', function() {
|
||||
this.hero = {
|
||||
name: 'Spawn'
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="heroDetail.js">
|
||||
|
||||
function HeroDetailController() {
|
||||
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '='
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<!-- components match only elements -->
|
||||
<div ng-controller="mainCtrl as ctrl">
|
||||
<b>Hero</b><br>
|
||||
<hero-detail hero="ctrl.hero"></hero-detail>
|
||||
</div>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<span>Name: {{$ctrl.hero.name}}</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
It's also possible to add components via {@link $compileProvider#component} in a module's config phase.
|
||||
|
||||
### Comparison between Directive definition and Component definition
|
||||
|
||||
| | Directive | Component |
|
||||
|-------------------|----------------------|-----------------|
|
||||
| bindings | No | Yes (binds to controller) |
|
||||
| bindToController | Yes (default: false) | No (use bindings instead) |
|
||||
| compile function | Yes | No |
|
||||
| controller | Yes | Yes (default `function() {}`) |
|
||||
| controllerAs | Yes (default: false) | Yes (default: `$ctrl`) |
|
||||
| link functions | Yes | No |
|
||||
| multiElement | Yes | No |
|
||||
| priority | Yes | No |
|
||||
| require | Yes | Yes |
|
||||
| restrict | Yes | No (restricted to elements only) |
|
||||
| scope | Yes (default: false) | No (scope is always isolate) |
|
||||
| template | Yes | Yes, injectable |
|
||||
| templateNamespace | Yes | No |
|
||||
| templateUrl | Yes | Yes, injectable |
|
||||
| terminal | Yes | No |
|
||||
| transclude | Yes (default: false) | Yes (default: false) |
|
||||
|
||||
|
||||
## Component-based application architecture
|
||||
|
||||
As already mentioned, the component helper makes it easier to structure your application with
|
||||
a component-based architecture. But what makes a component beyond the options that
|
||||
the component helper has?
|
||||
|
||||
- **Components only control their own View and Data:**
|
||||
Components should never modify any data or DOM that is out of their own scope. Normally, in Angular
|
||||
it is possible to modify data anywhere in the application through scope inheritance and watches. This
|
||||
is practical, but can also lead to problems when it is not clear which part of the application is
|
||||
responsible for modifying the data. That is why component directives use an isolate scope, so a whole
|
||||
class of scope manipulation is not possible.
|
||||
|
||||
- **Components have a well-defined public API - Inputs and Outputs:**
|
||||
However, scope isolation only goes so far, because Angular uses two-way binding. So if you pass
|
||||
an object to a component like this - `bindings: {item: '='}`, and modify one of its properties, the
|
||||
change will be reflected in the parent component. For components however, only the component that owns
|
||||
the data should modify it, to make it easy to reason about what data is changed, and when. For that reason,
|
||||
components should follow a few simple conventions:
|
||||
|
||||
- Inputs should be using `<` and `@` bindings. The `<` symbol denotes {@link $compile#-scope- one-way bindings} which are
|
||||
available since 1.5. The difference to `=` is that the bound properties in the component scope are not watched, which means
|
||||
if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent
|
||||
and component scope reference the same object, so if you are changing object properties or array elements in the
|
||||
component, the parent will still reflect that change.
|
||||
The general rule should therefore be to never change an object or array property in the component scope.
|
||||
`@` bindings can be used when the input is a string, especially when the value of the binding doesn't change.
|
||||
```js
|
||||
bindings: {
|
||||
hero: '<',
|
||||
comment: '@'
|
||||
}
|
||||
```
|
||||
- Outputs are realized with `&` bindings, which function as callbacks to component events.
|
||||
```js
|
||||
bindings: {
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
```
|
||||
- Instead of manipulating Input Data, the component calls the correct Output Event with the changed data.
|
||||
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
|
||||
the owner component via the correct event.
|
||||
```html
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
```
|
||||
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
|
||||
```js
|
||||
ctrl.deleteHero(hero) {
|
||||
$http.delete(...).then(function() {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- **Components have a well-defined lifecycle**
|
||||
Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life
|
||||
of the component. The following hook methods can be implemented:
|
||||
|
||||
* `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
|
||||
had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
this element). This is a good place to put initialization code for your controller.
|
||||
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
|
||||
are the names of the bound properties that have changed, and the values are an object of the form
|
||||
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
|
||||
cloning the bound value to prevent accidental mutation of the outer value.
|
||||
* `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
|
||||
external resources, watches and event handlers.
|
||||
* `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
|
||||
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
|
||||
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
|
||||
they are waiting for their template to load asynchronously and their own compilation and linking has been
|
||||
suspended until that occurs.
|
||||
This hook can be considered analogous to the `ngAfterViewInit` and `ngAfterContentInit` hooks in Angular 2.
|
||||
Since the compilation process is rather different in Angular 1 there is no direct mapping and care should
|
||||
be taken when upgrading.
|
||||
|
||||
By implementing these methods, you component can take part in its lifecycle.
|
||||
|
||||
- **An application is a tree of components:**
|
||||
Ideally, the whole application should be a tree of components that implement clearly defined inputs
|
||||
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
|
||||
of a component is.
|
||||
|
||||
## Example of a component tree
|
||||
|
||||
The following example expands on the simple component example and incorporates the concepts we introduced
|
||||
above:
|
||||
|
||||
Instead of an ngController, we now have a heroList component that holds the data of
|
||||
different heroes, and creates a heroDetail for each of them.
|
||||
|
||||
The heroDetail component now contains new functionality:
|
||||
- a delete button that calls the bound `onDelete` function of the heroList component
|
||||
- an input to change the hero location, in the form of a reusable editableField component. Instead
|
||||
of manipulating the hero object itself, it sends a changeset upwards to the heroDetail, which sends
|
||||
it upwards to the heroList component, which updates the original data.
|
||||
|
||||
<example name="heroComponentTree" module="heroApp">
|
||||
<file name="index.js">
|
||||
var mode = angular.module('heroApp', []);
|
||||
</file>
|
||||
|
||||
<file name="heroList.js">
|
||||
function HeroListController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
// This would be loaded by $http etc.
|
||||
ctrl.list = [
|
||||
{
|
||||
name: 'Superman',
|
||||
location: ''
|
||||
},
|
||||
{
|
||||
name: 'Batman',
|
||||
location: 'Wayne Manor'
|
||||
}
|
||||
];
|
||||
|
||||
ctrl.updateHero = function(hero, prop, value) {
|
||||
hero[prop] = value;
|
||||
};
|
||||
|
||||
ctrl.deleteHero = function(hero) {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroList', {
|
||||
templateUrl: 'heroList.html',
|
||||
controller: HeroListController
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
<file name="heroDetail.js">
|
||||
function HeroDetailController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.update = function(prop, value) {
|
||||
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '<',
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="editableField.js">
|
||||
|
||||
function EditableFieldController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
ctrl.editMode = false;
|
||||
|
||||
ctrl.handleModeChange = function() {
|
||||
if (ctrl.editMode) {
|
||||
ctrl.onUpdate({value: ctrl.fieldValue});
|
||||
ctrl.fieldValueCopy = ctrl.fieldValue;
|
||||
}
|
||||
ctrl.editMode = !ctrl.editMode;
|
||||
};
|
||||
|
||||
ctrl.reset = function() {
|
||||
ctrl.fieldValue = ctrl.fieldValueCopy;
|
||||
};
|
||||
|
||||
ctrl.$onInit = function() {
|
||||
// Make a copy of the initial value to be able to reset it later
|
||||
ctrl.fieldValueCopy = ctrl.fieldValue;
|
||||
|
||||
// Set a default fieldType
|
||||
if (!ctrl.fieldType) {
|
||||
ctrl.fieldType = 'text';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('editableField', {
|
||||
templateUrl: 'editableField.html',
|
||||
controller: EditableFieldController,
|
||||
bindings: {
|
||||
fieldValue: '<',
|
||||
fieldType: '@?',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<hero-list></hero-list>
|
||||
</file>
|
||||
<file name="heroList.html">
|
||||
<b>Heroes</b><br>
|
||||
<hero-detail ng-repeat="hero in $ctrl.list" hero="hero" on-delete="$ctrl.deleteHero(hero)" on-update="$ctrl.updateHero(hero, prop, value)"></hero-detail>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<hr>
|
||||
<div>
|
||||
Name: {{$ctrl.hero.name}}<br>
|
||||
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="editableField.html">
|
||||
<span ng-switch="$ctrl.editMode">
|
||||
<input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
|
||||
<span ng-switch-default>{{$ctrl.fieldValue}}</span>
|
||||
</span>
|
||||
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button>
|
||||
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
## Components as route templates
|
||||
Components are also useful as route templates (e.g. when using {@link ngRoute ngRoute}). In a component-based
|
||||
application, every view is a component:
|
||||
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
controller: function() {
|
||||
this.user = {name: 'world'};
|
||||
}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home></home>'
|
||||
});
|
||||
});
|
||||
```
|
||||
<br />
|
||||
When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
|
||||
boilerplate, by passing the resolved route dependencies directly to the component. Since 1.5,
|
||||
ngRoute automatically assigns the resolves to the route scope property `$resolve` (you can also
|
||||
configure the property name via `resolveAs`). When using components, you can take advantage of this and pass resolves
|
||||
directly into your component without creating an extra route controller:
|
||||
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
bindings: {
|
||||
user: '<'
|
||||
}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home user="$resolve.user"></home>',
|
||||
resolve: {
|
||||
user: function($http) { return $http.get('...'); }
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Intercomponent Communication
|
||||
|
||||
Directives can require the controllers of other directives to enable communication
|
||||
between each other. This can be achieved in a component by providing an
|
||||
object mapping for the `require` property. The object keys specify the property names under which
|
||||
the required controllers (object values) will be bound to the requiring component's controller.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Note that the required controllers will not be available during the instantiation of the controller,
|
||||
but they are guaranteed to be available just before the `$onInit` method is executed!
|
||||
</div>
|
||||
|
||||
Here is a tab pane example built from components:
|
||||
|
||||
<example module="docsTabsExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsTabsExample', [])
|
||||
.component('myTabs', {
|
||||
transclude: true,
|
||||
controller: function() {
|
||||
var panes = this.panes = [];
|
||||
this.select = function(pane) {
|
||||
angular.forEach(panes, function(pane) {
|
||||
pane.selected = false;
|
||||
});
|
||||
pane.selected = true;
|
||||
};
|
||||
this.addPane = function(pane) {
|
||||
if (panes.length === 0) {
|
||||
this.select(pane);
|
||||
}
|
||||
panes.push(pane);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-tabs.html'
|
||||
})
|
||||
.component('myPane', {
|
||||
transclude: true,
|
||||
require: {
|
||||
tabsCtrl: '^myTabs'
|
||||
},
|
||||
bindings: {
|
||||
title: '@'
|
||||
},
|
||||
controller: function() {
|
||||
this.$onInit = function() {
|
||||
this.tabsCtrl.addPane(this);
|
||||
console.log(this);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<my-tabs>
|
||||
<my-pane title="Hello">
|
||||
<h4>Hello</h4>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
</my-pane>
|
||||
<my-pane title="World">
|
||||
<h4>World</h4>
|
||||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||||
</my-pane>
|
||||
</my-tabs>
|
||||
</file>
|
||||
<file name="my-tabs.html">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
<li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">
|
||||
<a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" ng-transclude></div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-pane.html">
|
||||
<div class="tab-pane" ng-show="$ctrl.selected" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
# Unit-testing Component Controllers
|
||||
|
||||
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
|
||||
that is included in {@link ngMock}. The advantage of this method is that you do not have
|
||||
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
|
||||
from above.
|
||||
|
||||
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
|
||||
|
||||
**Controller Test:**
|
||||
```js
|
||||
describe('component: heroDetail', function() {
|
||||
var component, scope, hero, $componentController;
|
||||
|
||||
beforeEach(module('simpleComponent'));
|
||||
|
||||
beforeEach(inject(function($rootScope, _$componentController_) {
|
||||
scope = $rootScope.$new();
|
||||
$componentController = _$componentController_;
|
||||
hero = {name: 'Wolverine'};
|
||||
}));
|
||||
|
||||
it('should set the default values of the hero', function() {
|
||||
// It's necessary to always pass the scope in the locals, so that the controller instance can be bound to it
|
||||
component = $componentController('heroDetail', {$scope: scope});
|
||||
|
||||
expect(component.hero).toEqual({
|
||||
name: undefined,
|
||||
location: 'unknown'
|
||||
});
|
||||
});
|
||||
|
||||
it('should assign the name bindings to the hero object', function() {
|
||||
// Here we are passing actual bindings to the component
|
||||
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero}
|
||||
);
|
||||
expect(component.hero.name).toBe('Wolverine');
|
||||
});
|
||||
|
||||
it('should call the onDelete binding when a hero is deleted', function() {
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero, onDelete: jasmine.createSpy('deleteSpy')}
|
||||
);
|
||||
|
||||
component.onDelete({hero: component.hero});
|
||||
expect(spy('deleteSpy')).toHaveBeenCalledWith(component.hero);
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
@@ -8,22 +8,22 @@
|
||||
This section briefly touches on all of the important parts of AngularJS using a simple example.
|
||||
For a more in-depth explanation, see the {@link tutorial/ tutorial}.
|
||||
|
||||
| Concept | Description |
|
||||
|--------------------------------------------|--------------------------------------------------------------------------|
|
||||
|{@link concepts#template Template} | HTML with additional markup |
|
||||
|{@link concepts#directive Directives} | extend HTML with custom attributes and elements |
|
||||
|{@link concepts#model Model} | the data shown to the user in the view and with which the user interacts |
|
||||
|{@link concepts#scope Scope} | context where the model is stored so that controllers, directives and expressions can access it |
|
||||
|{@link concepts#expression Expressions} | access variables and functions from the scope |
|
||||
|{@link concepts#compiler Compiler} | parses the template and instantiates directives and expressions |
|
||||
|{@link concepts#filter Filter} | formats the value of an expression for display to the user |
|
||||
|{@link concepts#view View} | what the user sees (the DOM) |
|
||||
|{@link concepts#databinding Data Binding} | sync data between the model and the view |
|
||||
|{@link concepts#controller Controller} | the business logic behind views |
|
||||
|{@link concepts#di Dependency Injection} | Creates and wires objects and functions |
|
||||
|{@link concepts#injector Injector} | dependency injection container |
|
||||
|{@link concepts#module Module} | a container for the different parts of an app including controllers, services, filters, directives which configures the Injector |
|
||||
|{@link concepts#service Service} | reusable business logic independent of views |
|
||||
| Concept | Description |
|
||||
|------------------|------------------------------------------|
|
||||
|{@link concepts#template Template} | HTML with additional markup |
|
||||
|{@link concepts#directive Directives} | extend HTML with custom attributes and elements |
|
||||
|{@link concepts#model Model} | the data shown to the user in the view and with which the user interacts |
|
||||
|{@link concepts#scope Scope} | context where the model is stored so that controllers, directives and expressions can access it |
|
||||
|{@link concepts#expression Expressions} | access variables and functions from the scope |
|
||||
|{@link concepts#compiler Compiler} | parses the template and instantiates directives and expressions |
|
||||
|{@link concepts#filter Filter} | formats the value of an expression for display to the user |
|
||||
|{@link concepts#view View} | what the user sees (the DOM) |
|
||||
|{@link concepts#databinding Data Binding} | sync data between the model and the view |
|
||||
|{@link concepts#controller Controller} | the business logic behind views |
|
||||
|{@link concepts#di Dependency Injection} | Creates and wires objects and functions |
|
||||
|{@link concepts#injector Injector} | dependency injection container |
|
||||
|{@link concepts#module Module} | a container for the different parts of an app including controllers, services, filters, directives which configures the Injector |
|
||||
|{@link concepts#service Service} | reusable business logic independent of views |
|
||||
|
||||
|
||||
## A first example: Data binding
|
||||
@@ -55,11 +55,11 @@ Try out the Live Preview above, and then let's walk through the example and desc
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding1.png">
|
||||
|
||||
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
|
||||
<a name="template">{@link templates template}</a>. When Angular starts your application, it parses and
|
||||
processes this new markup from the template using the <a name="compiler">{@link compiler compiler}</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view"></a>*view*.
|
||||
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
|
||||
processes this new markup from the template using the so-called <a name="compiler">"{@link compiler compiler}"</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
|
||||
|
||||
The first kind of new markup are the <a name="directive">{@link directive directives}</a>.
|
||||
The first kind of new markup are the so-called <a name="directive">"{@link directive directives}"</a>.
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
{@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
|
||||
initializes our application. Angular also defines a directive for the {@link ng.directive:input `input`}
|
||||
@@ -75,16 +75,16 @@ stores/updates the value of the input field into/from a variable.
|
||||
|
||||
The second kind of new markup are the double curly braces `{{ expression | filter }}`:
|
||||
When the compiler encounters this markup, it will replace it with the evaluated value of the markup.
|
||||
An <a name="expression">{@link expression expression}</a> in a template is a JavaScript-like code snippet that allows
|
||||
Angular to read and write variables. Note that those variables are not global variables.
|
||||
An <a name="expression">"{@link expression expression}"</a> in a template is a JavaScript-like code snippet that allows
|
||||
to read and write variables. Note that those variables are not global variables.
|
||||
Just like variables in a JavaScript function live in a scope,
|
||||
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
|
||||
Angular provides a <a name="scope">"{@link scope scope}"</a> for the variables accessible to expressions.
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model">"model"</a>
|
||||
in the rest of the documentation.
|
||||
Applied to the example above, the markup directs Angular to "take the data we got from the input widgets
|
||||
and multiply them together".
|
||||
|
||||
The example above also contains a <a name="filter">{@link guide/filter filter}</a>.
|
||||
The example above also contains a <a name="filter">"{@link guide/filter filter}"</a>.
|
||||
A filter formats the value of an expression for display to the user.
|
||||
In the example above, the filter {@link ng.filter:currency `currency`} formats a number
|
||||
into an output that looks like money.
|
||||
@@ -92,7 +92,7 @@ into an output that looks like money.
|
||||
The important thing in the example is that Angular provides _live_ bindings:
|
||||
Whenever the input values change, the value of the expressions are automatically
|
||||
recalculated and the DOM is updated with their values.
|
||||
The concept behind this is <a name="databinding">{@link databinding two-way data binding}</a>.
|
||||
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
|
||||
|
||||
|
||||
## Adding UI logic: Controllers
|
||||
@@ -150,7 +150,7 @@ different currencies and also pay the invoice.
|
||||
|
||||
What changed?
|
||||
|
||||
First, there is a new JavaScript file that contains a <a name="controller">{@link controller controller}</a>.
|
||||
First, there is a new JavaScript file that contains a so-called <a name="controller">"{@link controller controller}"</a>.
|
||||
More exactly, the file contains a constructor function that creates the actual controller instance.
|
||||
The purpose of controllers is to expose variables and functionality to expressions and directives.
|
||||
|
||||
@@ -179,11 +179,11 @@ The following graphic shows how everything works together after we introduced th
|
||||
|
||||
<img style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding2.png">
|
||||
|
||||
## View-independent business logic: Services
|
||||
## View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practice to move view-independent logic from the controller into a
|
||||
<a name="service">{@link services service}</a>, so it can be reused by other parts
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
|
||||
@@ -255,15 +255,15 @@ We moved the `convertCurrency` function and the definition of the existing curre
|
||||
into the new file `finance2.js`. But how does the controller
|
||||
get a hold of the now separated function?
|
||||
|
||||
This is where <a name="di">{@link di Dependency Injection}</a> comes into play.
|
||||
This is where <a name="di">"{@link di Dependency Injection}"</a> comes into play.
|
||||
Dependency Injection (DI) is a software design pattern that
|
||||
deals with how objects and functions get created and how they get a hold of their dependencies.
|
||||
Everything within Angular (directives, filters, controllers,
|
||||
services, ...) is created and wired using dependency injection. Within Angular,
|
||||
the DI container is called the <a name="injector">{@link di injector}</a>.
|
||||
the DI container is called the <a name="injector">"{@link di injector}"</a>.
|
||||
|
||||
To use DI, there needs to be a place where all the things that should work together are registered.
|
||||
In Angular, this is the purpose of the <a name="module">{@link module modules}</a>.
|
||||
In Angular, this is the purpose of the so-called <a name="module">"{@link module modules}"</a>.
|
||||
When Angular starts, it will use the configuration of the module with the name defined by the `ng-app` directive,
|
||||
including the configuration of all modules that this module depends on.
|
||||
|
||||
@@ -334,9 +334,9 @@ The following example shows how this is done with Angular:
|
||||
var refresh = function() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
return $http.jsonp(url).then(function(response) {
|
||||
return $http.jsonp(url).success(function(data) {
|
||||
var newUsdToForeignRates = {};
|
||||
angular.forEach(response.data.query.results.rate, function(rate) {
|
||||
angular.forEach(data.query.results.rate, function(rate) {
|
||||
var currency = rate.id.substring(3,6);
|
||||
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
|
||||
});
|
||||
@@ -348,7 +348,8 @@ The following example shows how this is done with Angular:
|
||||
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert
|
||||
convert: convert,
|
||||
refresh: refresh
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
|
||||
@@ -5,16 +5,13 @@
|
||||
|
||||
# Understanding Controllers
|
||||
|
||||
In Angular, a Controller is defined by a JavaScript **constructor function** that is used to augment the
|
||||
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
|
||||
{@link scope Angular Scope}.
|
||||
|
||||
When a Controller is attached to the DOM via the {@link ng.directive:ngController ng-controller}
|
||||
directive, Angular will instantiate a new Controller object, using the specified Controller's
|
||||
**constructor function**. A new **child scope** will be created and made available as an injectable
|
||||
parameter to the Controller's constructor function as `$scope`.
|
||||
|
||||
If the controller has been attached using the `controller as` syntax then the controller instance will
|
||||
be assigned to a property on the new scope.
|
||||
**constructor function**. A new **child scope** will be available as an injectable parameter to the
|
||||
Controller's constructor function as `$scope`.
|
||||
|
||||
Use controllers to:
|
||||
|
||||
@@ -33,12 +30,12 @@ Do not use controllers to:
|
||||
services} instead.
|
||||
- Manage the life-cycle of other components (for example, to create service instances).
|
||||
|
||||
## Setting up the initial state of a `$scope` object
|
||||
# Setting up the initial state of a `$scope` object
|
||||
|
||||
Typically, when you create an application you need to set up the initial state for the Angular
|
||||
`$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object.
|
||||
The properties contain the **view model** (the model that will be presented by the view). All the
|
||||
`$scope` properties will be available to the {@link templates template} at the point in the DOM where the Controller
|
||||
`$scope` properties will be available to the template at the point in the DOM where the Controller
|
||||
is registered.
|
||||
|
||||
The following example demonstrates creating a `GreetingController`, which attaches a `greeting`
|
||||
@@ -72,13 +69,13 @@ now be data-bound to the template:
|
||||
```
|
||||
|
||||
|
||||
## Adding Behavior to a Scope Object
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
In order to react to events or execute computation in the view we must provide behavior to the
|
||||
scope. We add behavior to the scope by attaching methods to the `$scope` object. These methods are
|
||||
then available to be called from the template/view.
|
||||
|
||||
The following example uses a Controller to add a method, which doubles a number, to the scope:
|
||||
The following example uses a Controller to add a method to the scope, which doubles a number:
|
||||
|
||||
```js
|
||||
var myApp = angular.module('myApp',[]);
|
||||
@@ -102,14 +99,14 @@ objects (or primitives) assigned to the scope become model properties. Any metho
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link ng.directive:ngClick ngClick}).
|
||||
|
||||
## Using Controllers Correctly
|
||||
# Using Controllers Correctly
|
||||
|
||||
In general, a Controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.
|
||||
|
||||
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in Controllers via dependency injection.
|
||||
This is discussed in the {@link di Dependency Injection} and {@link services
|
||||
This is discussed in the {@link di Dependency Injection} {@link services
|
||||
Services} sections of this guide.
|
||||
|
||||
|
||||
@@ -128,7 +125,7 @@ following components:
|
||||
- A model consisting of a string named `spice`
|
||||
- A Controller with two functions that set the value of `spice`
|
||||
|
||||
The message in our template contains a binding to the `spice` model which, by default, is set to the
|
||||
The message in our template contains a binding to the `spice` model, which by default is set to the
|
||||
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
|
||||
`jalapeño`, and the message is automatically updated by data-binding.
|
||||
|
||||
@@ -165,7 +162,7 @@ scope is augmented (managed) by the `SpicyController` Controller.
|
||||
starts with capital letter and ends with "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for both the `<div>` element and
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
its children).
|
||||
|
||||
## Spicy Arguments Example
|
||||
@@ -262,7 +259,7 @@ Inheritance works with methods in the same way as it does with properties. So in
|
||||
examples, all of the properties could be replaced with methods that return string values.
|
||||
|
||||
|
||||
# Testing Controllers
|
||||
## Testing Controllers
|
||||
|
||||
Although there are many ways to test a Controller, one of the best conventions, shown below,
|
||||
involves injecting the {@link ng.$rootScope $rootScope} and {@link ng.$controller $controller}:
|
||||
@@ -305,7 +302,7 @@ describe('myController function', function() {
|
||||
```
|
||||
|
||||
|
||||
If you need to test a nested Controller you must create the same scope hierarchy
|
||||
If you need to test a nested Controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM:
|
||||
|
||||
```js
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
@sortOrder 210
|
||||
@description
|
||||
|
||||
# Data Binding
|
||||
|
||||
Data-binding in Angular apps is the automatic synchronization of data between the model and view
|
||||
components. The way that Angular implements data-binding lets you treat the model as the
|
||||
single-source-of-truth in your application. The view is a projection of the model at all times.
|
||||
|
||||
@@ -163,8 +163,8 @@ someModule.controller('MyController', function($scope, greeter) {
|
||||
});
|
||||
```
|
||||
|
||||
Given a function, the injector can infer the names of the services to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example, `$scope` and
|
||||
Given a function the injector can infer the names of the services to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example `$scope`, and
|
||||
`greeter` are two services which need to be injected into the function.
|
||||
|
||||
One advantage of this approach is that there's no array of names to keep in sync with the
|
||||
@@ -293,7 +293,7 @@ Create a new injector that can provide components defined in our `myModule` modu
|
||||
`greeter` service from the injector. (This is usually done automatically by angular bootstrap).
|
||||
|
||||
```js
|
||||
var injector = angular.injector(['ng', 'myModule']);
|
||||
var injector = angular.injector(['myModule', 'ng']);
|
||||
var greeter = injector.get('greeter');
|
||||
```
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user