mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
Compare commits
No commits in common. "master" and "v1.1.4" have entirely different histories.
713 changed files with 50594 additions and 62680 deletions
4
.bowerrc
4
.bowerrc
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"json": "bower.json"
|
||||
}
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
|||
/build/
|
||||
build/
|
||||
angularjs.netrc
|
||||
jstd.log
|
||||
.DS_Store
|
||||
gen_docs.disable
|
||||
test.disable
|
||||
|
|
@ -6,15 +8,7 @@ regression/temp*.html
|
|||
performance/temp*.html
|
||||
.idea/workspace.xml
|
||||
*~
|
||||
*.swp
|
||||
angular.js.tmproj
|
||||
/node_modules/
|
||||
/components/
|
||||
/bower_components/
|
||||
node_modules
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
.agignore
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
/tmp/
|
||||
/scripts/bower/bower-*
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"disallowKeywords": ["with"]
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// 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.
|
||||
|
||||
{
|
||||
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireLineFeedAtFileEnd": true,
|
||||
"validateJSDoc": {
|
||||
"checkParamNames": true,
|
||||
"requireParamTypes": true
|
||||
}
|
||||
}
|
||||
27
.travis.yml
27
.travis.yml
|
|
@ -1,28 +1,13 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.10
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- JOB=unit
|
||||
- JOB=e2e
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||
- 0.8
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install -g grunt-cli
|
||||
- grunt bower
|
||||
- grunt bower
|
||||
- grunt package-without-bower
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
- grunt package
|
||||
- grunt webserver > /dev/null &
|
||||
|
||||
script:
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/print_logs.sh
|
||||
- grunt test --browsers Firefox --reporters=dots
|
||||
|
|
|
|||
2153
CHANGELOG.md
2153
CHANGELOG.md
File diff suppressed because it is too large
Load diff
272
CONTRIBUTING.md
272
CONTRIBUTING.md
|
|
@ -1,262 +1,32 @@
|
|||
#Contributing to AngularJS
|
||||
## Submitting issues
|
||||
|
||||
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:
|
||||
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].
|
||||
|
||||
## Got a Question or Problem?
|
||||
### Guidelines
|
||||
|
||||
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].
|
||||
|
||||
## Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
submitting and issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
|
||||
with a fix.
|
||||
|
||||
***Localization Issue:*** *Angular.js uses the [Google Closure I18N library], to generate its own I18N files. This means that
|
||||
any changes to these files would be lost the next time that we import the library. The recommended
|
||||
approach is to submit a patch to the I18N project directly, instead of submitting it here.*
|
||||
|
||||
**Please see the Submission Guidelines below**.
|
||||
|
||||
## Want a Feature?
|
||||
You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you
|
||||
would like to implement a new feature then consider what kind of change it is:
|
||||
|
||||
* **Major Changes** that you wish to contribute to the project should be discussed first on our
|
||||
[dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, prevent
|
||||
duplication of work, and help you to craft the change so that it is successfully accepted into the
|
||||
project.
|
||||
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
||||
|
||||
|
||||
## Want a Doc Fix?
|
||||
If you want to help improve the docs, it's a good idea to let others know what you're working on to
|
||||
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
|
||||
Comment on an issue to let others know what you're working on, or create a new issue if your work
|
||||
doesn't fit within the scope of any of the existing doc fix projects.
|
||||
|
||||
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
|
||||
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.
|
||||
|
||||
## Submission Guidelines
|
||||
|
||||
### Submitting an Issue
|
||||
Before you submit your issue search the archive, maybe your question was already answered.
|
||||
* Search the archive first, it's likely that your question was already answered.
|
||||
* A live example demonstrating your problem or question, will get an answer faster.
|
||||
* Create one using this [template][template]
|
||||
* If you get help, help others. Good karma rulez!
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
features, by not reporting duplicate issues.
|
||||
|
||||
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **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 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)
|
||||
|
||||
Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069
|
||||
|
||||
**If you get help, help others. Good karma rulez!**
|
||||
|
||||
### Submitting a Pull Request
|
||||
Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests. We cannot accept code without this.
|
||||
* Make your changes in a new git branch
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
`validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
* Build your changes locally to ensure all the tests pass
|
||||
|
||||
```shell
|
||||
grunt test
|
||||
```
|
||||
|
||||
* Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In Github, send a pull request to `angular:master`.
|
||||
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
|
||||
repository to update the Pull Request:
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on Github:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
```
|
||||
|
||||
* Check out the master branch:
|
||||
|
||||
```shell
|
||||
git checkout master -f
|
||||
```
|
||||
|
||||
* Delete the local branch:
|
||||
|
||||
```shell
|
||||
git branch -D my-fix-branch
|
||||
```
|
||||
|
||||
* Update your master with the latest upstream version:
|
||||
|
||||
```shell
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
### GitHub Pull Request Helper
|
||||
|
||||
We track Pull Requests by attaching labels and assigning to milestones. For some reason GitHub
|
||||
does not provide a good UI for managing labels on Pull Requests (unlike Issues). We have developed
|
||||
a simple Chrome Extension that enables you to view (and manage if you have permission) the labels
|
||||
on Pull Requests. You can get the extension from the Chrome WebStore -
|
||||
[GitHub PR Helper][github-pr-helper]
|
||||
|
||||
## Coding Rules
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more [specs][unit-testing].
|
||||
* All public API methods **must be documented** with ngdoc, an extended version of jsdoc (we added
|
||||
support for markdown and templating via @ngdoc tag). To see how we document our APIs, please check
|
||||
out the existing ngdocs and see [this wiki page][ngDocs].
|
||||
* With the exceptions listed below, we follow the rules contained in
|
||||
[Google's JavaScript Style Guide][js-style-guide]:
|
||||
* **Do not use namespaces**: Instead, wrap the entire angular code base in an anonymous closure and
|
||||
export our API explicitly rather than implicitly.
|
||||
* Wrap all code at **100 characters**.
|
||||
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypical
|
||||
inheritance only when absolutely necessary.
|
||||
* We **love functions and closures** and, whenever possible, prefer them over objects.
|
||||
* To write concise code that can be better minified, we **use aliases internally** that map to the
|
||||
external API. See our existing code to see what we mean.
|
||||
* We **don't go crazy with type annotations** for private internal APIs unless it's an internal API
|
||||
that is used throughout AngularJS. The best guidance is to do what makes the most sense.
|
||||
|
||||
## Git Commit Guidelines
|
||||
|
||||
We have very precise rules over how our git commit messages can be formatted. This leads to **more
|
||||
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**.
|
||||
|
||||
### 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**:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
* **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 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
|
||||
generation
|
||||
|
||||
### Scope
|
||||
The scope could be anything specifying place of the commit change. For example `$location`,
|
||||
`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc...
|
||||
|
||||
### Subject
|
||||
The subject contains succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* 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"
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
###Footer
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
## Signing the CLA
|
||||
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
* For individuals we have a [simple click-through form][individual-cla].
|
||||
* For corporations we'll need you to
|
||||
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||
|
||||
## Further Information
|
||||
You can find out more detailed information about contributing in the
|
||||
[AngularJS documentation][contributing].
|
||||
|
||||
|
||||
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[contributing]: http://docs.angularjs.org/misc/contribute
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
[template]: http://plnkr.co/edit/gist:3510140
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
## Contributing to Source Code
|
||||
|
||||
We'd love for you to contribute to our source code and to make AngularJS even
|
||||
better than it is today!
|
||||
|
||||
Please read the [contribution guidelines][contribute] to learn about how to submit code as well as
|
||||
other useful info like how to build and test AngularJS code.
|
||||
|
||||
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
|
|
|
|||
219
Gruntfile.js
219
Gruntfile.js
|
|
@ -1,11 +1,12 @@
|
|||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
|
|
@ -20,15 +21,6 @@ module.exports = function(grunt) {
|
|||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
|
||||
parallel: {
|
||||
travis: {
|
||||
tasks: [
|
||||
util.parallelTask(['test:unit', 'test:docgen', 'test:promises-aplus', 'tests:docs'], {stream: true}),
|
||||
util.parallelTask(['test:e2e'])
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
connect: {
|
||||
devserver: {
|
||||
options: {
|
||||
|
|
@ -48,106 +40,33 @@ module.exports = function(grunt) {
|
|||
}
|
||||
}
|
||||
},
|
||||
testserver: {
|
||||
options: {
|
||||
// We use end2end task (which does not start the webserver)
|
||||
// and start the webserver as a separate process (in travis_build.sh)
|
||||
// to avoid https://github.com/joyent/libuv/issues/826
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
function(req, resp, next) {
|
||||
// cache get requests to speed up tests on travis
|
||||
if (req.method === 'GET') {
|
||||
resp.setHeader('Cache-control', 'public, max-age=3600');
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
testserver: {}
|
||||
},
|
||||
|
||||
|
||||
tests: {
|
||||
test: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
docs: 'karma-docs.conf.js',
|
||||
modules: 'karma-modules.conf.js'
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
},
|
||||
|
||||
|
||||
autotest: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
docs: 'karma-docs.conf.js'
|
||||
jquery: 'karma-jquery.conf.js'
|
||||
},
|
||||
|
||||
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
},
|
||||
clean: {build: ['build']},
|
||||
|
||||
|
||||
clean: {
|
||||
build: ['build'],
|
||||
tmp: ['tmp']
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: true,
|
||||
},
|
||||
ng: {
|
||||
files: { src: files['angularSrc'] },
|
||||
},
|
||||
ngAnimate: {
|
||||
files: { src: 'src/ngAnimate/**/*.js' },
|
||||
},
|
||||
ngCookies: {
|
||||
files: { src: 'src/ngCookies/**/*.js' },
|
||||
},
|
||||
ngLocale: {
|
||||
files: { src: 'src/ngLocale/**/*.js' },
|
||||
},
|
||||
ngMock: {
|
||||
files: { src: 'src/ngMock/**/*.js' },
|
||||
},
|
||||
ngResource: {
|
||||
files: { src: 'src/ngResource/**/*.js' },
|
||||
},
|
||||
ngRoute: {
|
||||
files: { src: 'src/ngRoute/**/*.js' },
|
||||
},
|
||||
ngSanitize: {
|
||||
files: { src: 'src/ngSanitize/**/*.js' },
|
||||
},
|
||||
ngScenario: {
|
||||
files: { src: 'src/ngScenario/**/*.js' },
|
||||
},
|
||||
ngTouch: {
|
||||
files: { src: 'src/ngTouch/**/*.js' },
|
||||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: ".jscs.json"
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
src: [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'lib/jquery/jquery.js',
|
||||
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
|
||||
],
|
||||
styles: {
|
||||
|
|
@ -159,59 +78,65 @@ module.exports = function(grunt) {
|
|||
src: util.wrap([files['angularSrc']], 'angular'),
|
||||
styles: {
|
||||
css: ['css/angular.css'],
|
||||
generateCspCssFile: true,
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
dest: 'build/angular-loader.js',
|
||||
src: util.wrap(files['angularLoader'], 'loader')
|
||||
src: util.wrap(['src/loader.js'], 'loader')
|
||||
},
|
||||
touch: {
|
||||
dest: 'build/angular-touch.js',
|
||||
src: util.wrap(files['angularModules']['ngTouch'], 'module')
|
||||
mobile: {
|
||||
dest: 'build/angular-mobile.js',
|
||||
src: util.wrap([
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js'
|
||||
], 'module')
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: util.wrap(files['angularModules']['ngMock'], 'module'),
|
||||
src: ['src/ngMock/angular-mocks.js'],
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
dest: 'build/angular-sanitize.js',
|
||||
src: util.wrap(files['angularModules']['ngSanitize'], 'module')
|
||||
src: util.wrap([
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
], 'module')
|
||||
},
|
||||
resource: {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(files['angularModules']['ngResource'], 'module')
|
||||
},
|
||||
animate: {
|
||||
dest: 'build/angular-animate.js',
|
||||
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
|
||||
},
|
||||
route: {
|
||||
dest: 'build/angular-route.js',
|
||||
src: util.wrap(files['angularModules']['ngRoute'], 'module')
|
||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||
},
|
||||
cookies: {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(files['angularModules']['ngCookies'], 'module')
|
||||
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
|
||||
},
|
||||
"promises-aplus-adapter": {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
bootstrap: {
|
||||
dest: 'build/angular-bootstrap.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
|
||||
},
|
||||
bootstrapPrettify: {
|
||||
dest: 'build/angular-bootstrap-prettify.js',
|
||||
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
|
||||
styles: {
|
||||
css: ['src/bootstrap/google-prettify/prettify.css'],
|
||||
minify: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
min: {
|
||||
angular: 'build/angular.js',
|
||||
animate: 'build/angular-animate.js',
|
||||
cookies: 'build/angular-cookies.js',
|
||||
loader: 'build/angular-loader.js',
|
||||
touch: 'build/angular-touch.js',
|
||||
mobile: 'build/angular-mobile.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
route: 'build/angular-route.js',
|
||||
sanitize: 'build/angular-sanitize.js'
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
bootstrap: 'build/angular-bootstrap.js',
|
||||
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -219,25 +144,6 @@ module.exports = function(grunt) {
|
|||
process: ['build/docs/*.html', 'build/docs/.htaccess']
|
||||
},
|
||||
|
||||
"jasmine_node": {
|
||||
projectRoot: 'docs/spec'
|
||||
},
|
||||
|
||||
"ddescribe-iit": {
|
||||
files: [
|
||||
'test/**/*.js',
|
||||
'!test/ngScenario/DescribeSpec.js'
|
||||
]
|
||||
},
|
||||
|
||||
"merge-conflict": {
|
||||
files: [
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
'docs/**/*',
|
||||
'css/**/*'
|
||||
]
|
||||
},
|
||||
|
||||
copy: {
|
||||
i18n: {
|
||||
|
|
@ -250,55 +156,24 @@ module.exports = function(grunt) {
|
|||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip', mode: 'zip'},
|
||||
options: {archive: 'build/' + dist +'.zip'},
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
|
||||
shell:{
|
||||
"promises-aplus-tests":{
|
||||
options:{
|
||||
//stdout:true,
|
||||
stderr:true,
|
||||
failOnError:true
|
||||
},
|
||||
command:path.normalize('./node_modules/.bin/promises-aplus-tests tmp/promises-aplus-adapter++.js')
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
},
|
||||
|
||||
bump: {
|
||||
options: {
|
||||
files: ['package.json'],
|
||||
commit: false,
|
||||
createTag: false,
|
||||
push: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', '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', ['tests:modules']);
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:docgen', ['jasmine_node']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
|
||||
grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']);
|
||||
grunt.registerTask('minify', ['clean', 'build', 'minall']);
|
||||
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2012 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
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
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
|
||||
pass allows using type annotations for AngularJS objects. For example,
|
||||
Angular's $scope objects can be annotated as:
|
||||
```js
|
||||
/** @type {angular.Scope} */
|
||||
var scope = $scope;
|
||||
```
|
||||
|
||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||
missing methods or incorrect arguments, and also prevents renaming of property
|
||||
accesses with advanced compilation.
|
||||
|
||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||
be correct. Externs for individual modules should be added in separate files.
|
||||
|
||||
See https://developers.google.com/closure/compiler/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
AngularJS [](https://travis-ci.org/angular/angular.js)
|
||||
AngularJS
|
||||
=========
|
||||
|
||||
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
|
||||
|
|
@ -7,7 +7,7 @@ syntax to express your application’s components clearly and succinctly. It au
|
|||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
|
|
@ -16,7 +16,6 @@ it makes development fun!
|
|||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
|
|
@ -38,7 +37,3 @@ To execute end-to-end (e2e) tests, use:
|
|||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
|
|
|
|||
63
TRIAGING.md
63
TRIAGING.md
|
|
@ -1,63 +0,0 @@
|
|||
# Triage new issues/PRs on github
|
||||
|
||||
This document shows the steps the Angular team is using to triage issues.
|
||||
The labels are used later on for planning releases.
|
||||
|
||||
## Tips ##
|
||||
|
||||
* install [github pr helper extension](https://github.com/petebacondarwin/github-pr-helper) and become 356% more productive
|
||||
* Label "resolution:*"
|
||||
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
|
||||
|
||||
|
||||
## Automatic processing ##
|
||||
|
||||
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
|
||||
The following is done automatically and should not be done manually:
|
||||
|
||||
* Label "cla: yes" or "cla: no" for pull requests
|
||||
|
||||
## Process ##
|
||||
|
||||
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||
1. Assign milestone:
|
||||
* "Docs only" milestone - for documentation PR -> **Done**.
|
||||
* Current/next milestone - regressions
|
||||
* 1.2.x - everything else
|
||||
1. Label "GH: *" (to be automated via Mary Poppins)
|
||||
* PR - issue is a PR
|
||||
* issue - otherwise
|
||||
1. Bugs:
|
||||
* Label "Type: Bug"
|
||||
* Label "Type: Regression" - if the bug is a regression
|
||||
* Duplicate? - Check if there are comments pointing out that this is a dupe, if they do exist verify that this is indeed a dupe and close it and go to the last step
|
||||
* Reproducible? - Steps to reproduce the bug are clear, if not ask for clarification (ideally plunker or fiddle)
|
||||
* Reproducible on master? - http://code.angularjs.org/snapshot/
|
||||
|
||||
1. Non bugs:
|
||||
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
|
||||
* Label "needs: breaking change" - if needed
|
||||
* Label "needs: public api" - if a new public api is needed
|
||||
* Understandable? - verify if the description of the request is clear. if not ask for clarification
|
||||
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||
|
||||
1. Label "component: *"
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label "impact: *"
|
||||
* small - obscure issue affecting one or handful of developers
|
||||
* medium - impacts some usage patterns
|
||||
* large - impacts most or all of angular apps
|
||||
1. Label "complexity: *"
|
||||
* small - trivial change
|
||||
* medium - non-trivial but straightforward change
|
||||
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
|
||||
1. Label "PRs plz!" for "GH: issue"
|
||||
* if complexity is small or medium and the problem as well as solution are well captured in the issue
|
||||
1. Label "origin: google" for issues from Google
|
||||
1. Label "high priority" for security issues, major performance regressions or memory leaks
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
186
angularFiles.js
vendored
Executable file → Normal file
186
angularFiles.js
vendored
Executable file → Normal file
|
|
@ -1,6 +1,5 @@
|
|||
angularFiles = {
|
||||
'angularSrc': [
|
||||
'src/minErr.js',
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/AngularPublic.js',
|
||||
|
|
@ -10,29 +9,28 @@ angularFiles = {
|
|||
'src/auto/injector.js',
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/animate.js',
|
||||
'src/ng/animation.js',
|
||||
'src/ng/animator.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/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/interval.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/route.js',
|
||||
'src/ng/routeParams.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/timeout.js',
|
||||
'src/ng/urlUtils.js',
|
||||
'src/ng/window.js',
|
||||
'src/ng/http.js',
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/timeout.js',
|
||||
|
||||
'src/ng/filter.js',
|
||||
'src/ng/filter/filter.js',
|
||||
|
|
@ -51,7 +49,6 @@ angularFiles = {
|
|||
'src/ng/directive/ngController.js',
|
||||
'src/ng/directive/ngCsp.js',
|
||||
'src/ng/directive/ngEventDirs.js',
|
||||
'src/ng/directive/ngIf.js',
|
||||
'src/ng/directive/ngInclude.js',
|
||||
'src/ng/directive/ngInit.js',
|
||||
'src/ng/directive/ngNonBindable.js',
|
||||
|
|
@ -61,49 +58,26 @@ angularFiles = {
|
|||
'src/ng/directive/ngStyle.js',
|
||||
'src/ng/directive/ngSwitch.js',
|
||||
'src/ng/directive/ngTransclude.js',
|
||||
'src/ng/directive/ngView.js',
|
||||
'src/ng/directive/script.js',
|
||||
'src/ng/directive/select.js',
|
||||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularLoader': [
|
||||
'src/minErr.js',
|
||||
'src/loader.js'
|
||||
'angularSrcModules': [
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js',
|
||||
'src/bootstrap/bootstrap.js'
|
||||
],
|
||||
|
||||
'angularModules': {
|
||||
'ngAnimate': [
|
||||
'src/ngAnimate/animate.js'
|
||||
],
|
||||
'ngCookies': [
|
||||
'src/ngCookies/cookies.js'
|
||||
],
|
||||
'ngResource': [
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
'ngRoute': [
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js'
|
||||
],
|
||||
'ngSanitize': [
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/filter/linky.js'
|
||||
],
|
||||
'ngMock': [
|
||||
'src/ngMock/angular-mocks.js'
|
||||
],
|
||||
'ngTouch': [
|
||||
'src/ngTouch/touch.js',
|
||||
'src/ngTouch/swipe.js',
|
||||
'src/ngTouch/directive/ngClick.js',
|
||||
'src/ngTouch/directive/ngSwipe.js'
|
||||
],
|
||||
},
|
||||
|
||||
'angularScenario': [
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'src/ngScenario/Application.js',
|
||||
'src/ngScenario/Describe.js',
|
||||
'src/ngScenario/Future.js',
|
||||
|
|
@ -119,104 +93,140 @@ angularFiles = {
|
|||
],
|
||||
|
||||
'angularTest': [
|
||||
'test/helpers/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/ng/**/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/bootstrap/*.js',
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
'test/ngSanitize/**/*.js',
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngTouch/**/*.js'
|
||||
'test/ngMobile/directive/*.js'
|
||||
],
|
||||
|
||||
'karma': [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'karmaExclude': [
|
||||
'jstdExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'karmaScenario': [
|
||||
'jstdScenario': [
|
||||
'build/angular-scenario.js',
|
||||
'build/jstd-scenario-adapter-config.js',
|
||||
'build/jstd-scenario-adapter.js',
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
"karmaModules": [
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'@angularSrcModules',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'build/angular-scenario.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
'src/ngResource/resource.js',
|
||||
'src/ngMobile/mobile.js',
|
||||
'src/ngMobile/directive/ngClick.js',
|
||||
'src/ngSanitize/sanitize.js',
|
||||
'src/ngSanitize/directive/ngBindHtml.js',
|
||||
'src/ngSanitize/filter/linky.js',
|
||||
'test/matchers.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/**/*.js',
|
||||
'test/ngTouch/**/*.js'
|
||||
'test/ngSanitize/*.js',
|
||||
'test/ngSanitize/directive/*.js',
|
||||
'test/ngSanitize/filter/*.js',
|
||||
'test/ngMobile/directive/*.js'
|
||||
],
|
||||
|
||||
'karmaJquery': [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'karmaJqueryExclude': [
|
||||
'jstdJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
]
|
||||
};
|
||||
|
||||
angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngAnimate'],
|
||||
angularFiles['angularModules']['ngCookies'],
|
||||
angularFiles['angularModules']['ngResource'],
|
||||
angularFiles['angularModules']['ngRoute'],
|
||||
angularFiles['angularModules']['ngSanitize'],
|
||||
angularFiles['angularModules']['ngMock'],
|
||||
angularFiles['angularModules']['ngTouch']
|
||||
);
|
||||
|
||||
if (exports) {
|
||||
exports.files = angularFiles;
|
||||
exports.mergeFilesFor = function() {
|
||||
exports.files = angularFiles
|
||||
exports.mergeFiles = function mergeFiles() {
|
||||
var files = [];
|
||||
|
||||
Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
|
||||
angularFiles[filegroup].forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
files = files.concat(angularFiles[match[1]]);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
[].splice.call(arguments, 0).forEach(function(file) {
|
||||
if (file.match(/karma/)) {
|
||||
files.push(file);
|
||||
} else {
|
||||
angularFiles[file].forEach(function(f) {
|
||||
// replace @ref
|
||||
var match = f.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
files = files.concat(deps);
|
||||
} else {
|
||||
if (!/jstd|jasmine/.test(f)) { //TODO(i): remove once we don't have jstd/jasmine in repo
|
||||
files.push(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
bower.json
12
bower.json
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "AngularJS",
|
||||
"devDependencies": {
|
||||
"jquery": "1.10.2",
|
||||
"lunr.js": "0.4.0",
|
||||
"google-code-prettify": "1.0.0",
|
||||
"components-font-awesome": "3.1.0",
|
||||
"bootstrap": "https://raw.github.com/twbs/bootstrap/v2.0.2/docs/assets/bootstrap.zip",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.2/assets/ng-closure-runner.zip"
|
||||
}
|
||||
}
|
||||
10
changelog.js
10
changelog.js
|
|
@ -16,6 +16,7 @@ var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
|
|||
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
|
||||
|
||||
var EMPTY_COMPONENT = '$$';
|
||||
var MAX_SUBJECT_LENGTH = 80;
|
||||
|
||||
|
||||
var warn = function() {
|
||||
|
|
@ -53,6 +54,11 @@ var parseRawCommit = function(raw) {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (match[3].length > MAX_SUBJECT_LENGTH) {
|
||||
warn('Too long subject: %s %s', msg.hash, msg.subject);
|
||||
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
|
||||
}
|
||||
|
||||
msg.type = match[1];
|
||||
msg.component = match[2];
|
||||
msg.subject = match[3];
|
||||
|
|
@ -142,7 +148,6 @@ var writeChangelog = function(stream, commits, version) {
|
|||
var sections = {
|
||||
fix: {},
|
||||
feat: {},
|
||||
perf: {},
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
|
|
@ -170,7 +175,6 @@ var writeChangelog = function(stream, commits, version) {
|
|||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
printSection(stream, 'Bug Fixes', sections.fix);
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Performance Improvements', sections.perf);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +192,7 @@ var getPreviousTag = function() {
|
|||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
|
|
|
|||
80
changelog.tmp.md
Normal file
80
changelog.tmp.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
#!/usr/local/bin/node
|
||||
|
||||
var util = require('util');
|
||||
var cp = require('child_process');
|
||||
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var semver = require('semver');
|
||||
|
||||
var exec = function (cmd) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.unshift(cmd);
|
||||
var fullCmd = util.format.apply(util, args);
|
||||
return Q.nfcall(cp.exec, fullCmd).then(function (out) {
|
||||
return out[0].split('\n');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var andThen = function (fn, after) {
|
||||
return function () {
|
||||
return fn.apply(this, arguments).then(after);
|
||||
};
|
||||
};
|
||||
|
||||
var oneArg = function (fn) {
|
||||
return function (arg) {
|
||||
return fn(arg);
|
||||
};
|
||||
};
|
||||
|
||||
var oneLine = function (lines) {
|
||||
return lines[0].trim();
|
||||
};
|
||||
|
||||
var noArgs = function (fn) {
|
||||
return function () {
|
||||
return fn();
|
||||
};
|
||||
};
|
||||
|
||||
var identity = function (i) { return i; };
|
||||
|
||||
// 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) {
|
||||
var results = [];
|
||||
var def;
|
||||
while (args.length > 0) {
|
||||
(function (arg) {
|
||||
if (def) {
|
||||
def = def.then(function () {
|
||||
return fn(arg);
|
||||
});
|
||||
} else {
|
||||
def = fn(arg);
|
||||
}
|
||||
def = def.then(function (res) {
|
||||
results.push(res);
|
||||
});
|
||||
}(args.pop()));
|
||||
}
|
||||
return def.then(function () {
|
||||
return results;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var compareBranches = function (left, right) {
|
||||
console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
|
||||
console.log(_(left.log).
|
||||
difference(right.log).
|
||||
map(function (line) {
|
||||
return left.full[left.log.indexOf(line)]; // lol O(n^2)
|
||||
}).
|
||||
value().
|
||||
join('\n'));
|
||||
};
|
||||
|
||||
var checkout = oneArg(exec('git checkout %s'));
|
||||
|
||||
var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
|
||||
var getTags = noArgs(exec('git tag'));
|
||||
var getShaOfTag = oneArg(exec('git rev-list %s | head -n 1'));
|
||||
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));
|
||||
|
||||
// remember this so we can restore state
|
||||
var currentBranch;
|
||||
|
||||
getCurrentBranch().
|
||||
then(function (branch) {
|
||||
currentBranch = branch;
|
||||
}).
|
||||
then(getTags).
|
||||
then(function (tags) {
|
||||
return tags.
|
||||
filter(semver.valid).
|
||||
map(semver.clean).
|
||||
sort(semver.rcompare);
|
||||
}).
|
||||
then(function (tags) {
|
||||
var major = tags[0].split('.')[0] + '.x';
|
||||
return tags.
|
||||
filter(function (ver) {
|
||||
return semver.satisfies(ver, major);
|
||||
});
|
||||
}).
|
||||
then(function (tags) {
|
||||
return _(tags).
|
||||
groupBy(function (tag) {
|
||||
return tag.split('.')[1];
|
||||
}).
|
||||
map(function (group) {
|
||||
return _.first(group);
|
||||
}).
|
||||
map(function (tag) {
|
||||
return 'v' + tag;
|
||||
}).
|
||||
value();
|
||||
}).
|
||||
then(function (tags) {
|
||||
return [
|
||||
{ name: 'v1.0.x', tag: tags[0] },
|
||||
{ name: 'master', tag: tags[1] }
|
||||
];
|
||||
}).
|
||||
then(allInSeries(function (branch) {
|
||||
return checkout(branch.name).
|
||||
then(function () {
|
||||
return getTheLog(branch.tag);
|
||||
}).
|
||||
then(function (log) {
|
||||
return log.
|
||||
filter(identity);
|
||||
}).
|
||||
then(function (log) {
|
||||
branch.full = log.map(function (line) {
|
||||
line = line.split(' ');
|
||||
var sha = line.shift();
|
||||
var msg = line.join(' ');
|
||||
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
|
||||
});
|
||||
branch.log = log.map(function (line) {
|
||||
return line.substr(41)
|
||||
});
|
||||
return branch;
|
||||
});
|
||||
})).
|
||||
then(function (pairs) {
|
||||
compareBranches(pairs[0], pairs[1]);
|
||||
console.log('\n');
|
||||
compareBranches(pairs[1], pairs[0]);
|
||||
return pairs;
|
||||
}).
|
||||
then(function () {
|
||||
return checkout(currentBranch);
|
||||
}).
|
||||
catch(function (e) {
|
||||
console.log(e.stack);
|
||||
});
|
||||
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak,
|
||||
.ng-hide {
|
||||
display: none !important;
|
||||
.ng-cloak, .x-ng-cloak {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ng\:form {
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
describe("DocsNavigationCtrl", function() {
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
var ctrl, $scope;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('docsPages', []);
|
||||
$provide.factory('docsSearch', function() {
|
||||
return function(q) {
|
||||
return ['one','two','three'];
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($controller, $rootScope, $location, docsSearch) {
|
||||
$scope = $rootScope.$new();
|
||||
ctrl = $controller('DocsNavigationCtrl', {
|
||||
$scope : $scope,
|
||||
$location : $location,
|
||||
docsSearch : docsSearch
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should search and return data from docsSearch", function() {
|
||||
$scope.search('1234')
|
||||
expect($scope.results.join(',')).toBe('one,two,three');
|
||||
expect($scope.hasResults).toBe(true);
|
||||
});
|
||||
|
||||
it("should avoid searching if the search term is too short", function() {
|
||||
$scope.search('1')
|
||||
expect($scope.results.length).toBe(0);
|
||||
expect($scope.hasResults).toBe(false);
|
||||
});
|
||||
|
||||
it("should set the columns classname based on the total grouped results", function() {
|
||||
$scope.search('1234');
|
||||
expect($scope.colClassName).toBe('cols-3');
|
||||
|
||||
$scope.search('1');
|
||||
expect($scope.colClassName).toBe(null);
|
||||
});
|
||||
|
||||
it("should hide and clear the results when called", function() {
|
||||
$scope.hasResults = true;
|
||||
$scope.results = ['one'];
|
||||
$scope.colClassName = '...';
|
||||
$scope.hideResults();
|
||||
expect($scope.hasResults).toBe(false);
|
||||
expect($scope.results.length).toBe(0);
|
||||
expect($scope.colClassName).toBe(null);
|
||||
});
|
||||
|
||||
it("should hide, clear and change the path of the page when submitted", inject(function($location) {
|
||||
$scope.hasResults = true;
|
||||
$scope.results = {
|
||||
api : [
|
||||
{url : '/home'}
|
||||
],
|
||||
tutorial : [
|
||||
{url : '/tutorial'}
|
||||
]
|
||||
};
|
||||
$scope.submit();
|
||||
expect($location.path()).toBe('/home');
|
||||
expect($scope.results.length).toBe(0);
|
||||
expect($scope.hasResults).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
describe('Docs Annotations', function() {
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
var body;
|
||||
beforeEach(function() {
|
||||
body = angular.element(document.body);
|
||||
body.empty();
|
||||
});
|
||||
|
||||
var normalizeHtml = function(html) {
|
||||
return html.toLowerCase().replace(/\s*$/, '');
|
||||
};
|
||||
|
||||
describe('popover directive', function() {
|
||||
|
||||
var $scope, element;
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
$scope = $rootScope.$new();
|
||||
element = angular.element(
|
||||
'<div style="margin:200px;" data-title="title_text" data-content="content_text" popover></div>'
|
||||
);
|
||||
element.attr('id','idx');
|
||||
body.append(element);
|
||||
$compile(element)($scope);
|
||||
$scope.$apply();
|
||||
}));
|
||||
|
||||
it('should be hidden by default', inject(function(popoverElement) {
|
||||
expect(popoverElement.visible()).toBe(false);
|
||||
}));
|
||||
|
||||
it('should capture the click event and set the title and content and position the tip', inject(function(popoverElement) {
|
||||
element.triggerHandler('click');
|
||||
expect(popoverElement.isSituatedAt(element)).toBe(true);
|
||||
expect(popoverElement.visible()).toBe(true);
|
||||
expect(popoverElement.title()).toBe('title_text');
|
||||
expect(popoverElement.content()).toContain('content_text');
|
||||
expect(popoverElement.besideElement.attr('id')).toBe('idx');
|
||||
}));
|
||||
|
||||
it('should hide and clear the title and content if the same element is clicked again', inject(function(popoverElement) {
|
||||
//show the element
|
||||
element.triggerHandler('click');
|
||||
expect(popoverElement.isSituatedAt(element)).toBe(true);
|
||||
|
||||
//hide the element
|
||||
element.triggerHandler('click');
|
||||
expect(popoverElement.isSituatedAt(element)).toBe(false);
|
||||
expect(popoverElement.visible()).toBe(false);
|
||||
expect(popoverElement.title()).toBe('');
|
||||
expect(popoverElement.content()).toBe('');
|
||||
}));
|
||||
|
||||
it('should parse markdown content', inject(function(popoverElement, $compile) {
|
||||
element = angular.element(
|
||||
'<div style="margin:200px;" data-title="#title_text" data-content="#heading" popover></div>'
|
||||
);
|
||||
body.append(element);
|
||||
$compile(element)($scope);
|
||||
$scope.$apply();
|
||||
element.triggerHandler('click');
|
||||
expect(popoverElement.title()).toBe('#title_text');
|
||||
expect(normalizeHtml(popoverElement.content())).toMatch('<h1>heading</h1>');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('foldout directive', function() {
|
||||
|
||||
// Do not run this suite on Internet Explorer.
|
||||
if (msie < 10) return;
|
||||
|
||||
var $scope, parent, element, url;
|
||||
beforeEach(function() {
|
||||
module(function($provide, $animateProvider) {
|
||||
$animateProvider.register('.foldout', function($timeout) {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
$timeout(done, 1000);
|
||||
},
|
||||
removeClass : function(element, className, done) {
|
||||
$timeout(done, 500);
|
||||
},
|
||||
addClass : function(element, className, done) {
|
||||
$timeout(done, 200);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $templateCache, $rootElement, $animate) {
|
||||
$animate.enabled(true);
|
||||
url = '/page.html';
|
||||
$scope = $rootScope.$new();
|
||||
parent = angular.element('<div class="parent"></div>');
|
||||
|
||||
//we're injecting the element to the $rootElement since the changes in
|
||||
//$animate only detect and perform animations if the root element has
|
||||
//animations enabled. If the element is not apart of the DOM
|
||||
//then animations are skipped.
|
||||
element = angular.element('<div data-url="' + url + '" class="foldout" foldout></div>');
|
||||
parent.append(element);
|
||||
$rootElement.append(parent);
|
||||
body.append($rootElement);
|
||||
|
||||
$compile(parent)($scope);
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
it('should inform that it is loading', inject(function($httpBackend) {
|
||||
$httpBackend.expect('GET', url).respond('hello');
|
||||
element.triggerHandler('click');
|
||||
|
||||
var kids = body.children();
|
||||
var foldout = angular.element(kids[kids.length-1]);
|
||||
expect(foldout.html()).toContain('loading');
|
||||
}));
|
||||
|
||||
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
|
||||
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
|
||||
// these brittle tests.
|
||||
xit('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout, $sniffer) {
|
||||
$httpBackend.expect('GET', url).respond('hello');
|
||||
|
||||
element.triggerHandler('click');
|
||||
$httpBackend.flush();
|
||||
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1000);
|
||||
|
||||
var kids = body.children();
|
||||
var foldout = angular.element(kids[kids.length-1]);
|
||||
expect(foldout.text()).toContain('hello');
|
||||
}));
|
||||
|
||||
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
|
||||
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
|
||||
// these brittle tests.
|
||||
xit('should hide then show when clicked again', inject(function($httpBackend, $timeout, $sniffer) {
|
||||
$httpBackend.expect('GET', url).respond('hello');
|
||||
|
||||
//enter
|
||||
element.triggerHandler('click');
|
||||
$httpBackend.flush();
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(1000);
|
||||
|
||||
//hide
|
||||
element.triggerHandler('click');
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(200);
|
||||
|
||||
//show
|
||||
element.triggerHandler('click');
|
||||
$timeout.flushNext(0);
|
||||
$timeout.flushNext(500);
|
||||
$timeout.flushNext(0);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('DocsController fold', function() {
|
||||
|
||||
var $scope, ctrl;
|
||||
beforeEach(function() {
|
||||
inject(function($rootScope, $controller, $location, $cookies, sections) {
|
||||
$scope = $rootScope.$new();
|
||||
ctrl = $controller('DocsController',{
|
||||
$scope : $scope,
|
||||
$location : $location,
|
||||
$cookies : $cookies,
|
||||
sections : sections
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should download and reveal the foldover container', inject(function($compile, $httpBackend) {
|
||||
var url = '/page.html';
|
||||
var fullUrl = '/notes/' + url;
|
||||
$httpBackend.expect('GET', fullUrl).respond('hello');
|
||||
|
||||
var element = angular.element('<div ng-include="docs_fold"></div>');
|
||||
$compile(element)($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.fold(url);
|
||||
|
||||
$httpBackend.flush();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
describe("docsSearch", function() {
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
var interceptedLunrResults;
|
||||
beforeEach(function() {
|
||||
interceptedLunrResults = [];
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
var results = [];
|
||||
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
|
||||
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
|
||||
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
|
||||
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
|
||||
$provide.value('NG_PAGES', results);
|
||||
$provide.factory('lunrSearch', function() {
|
||||
return function() {
|
||||
return {
|
||||
store : function(value) {
|
||||
interceptedLunrResults.push(value);
|
||||
},
|
||||
search : function(q) {
|
||||
var data = [];
|
||||
angular.forEach(results, function(res, i) {
|
||||
data.push({ ref : i });
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should lookup and organize values properly", inject(function(docsSearch) {
|
||||
var items = docsSearch('item');
|
||||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
it("should return all results without a search", inject(function(docsSearch) {
|
||||
var items = docsSearch();
|
||||
expect(items['tutorial'].length).toBe(2);
|
||||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
it("should store values with and without a ng prefix", inject(function(docsSearch) {
|
||||
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
describe("errorDisplay", function () {
|
||||
|
||||
var $location, compileHTML;
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
var $rootScope = $injector.get('$rootScope'),
|
||||
$compile = $injector.get('$compile');
|
||||
|
||||
$location = $injector.get('$location');
|
||||
|
||||
compileHTML = function (code) {
|
||||
var elm = angular.element(code);
|
||||
$compile(elm)($rootScope);
|
||||
$rootScope.$digest();
|
||||
return elm;
|
||||
};
|
||||
|
||||
this.addMatchers({
|
||||
toInterpolateTo: function (expected) {
|
||||
// Given a compiled DOM node with a minerr-display attribute,
|
||||
// assert that its interpolated string matches the expected text.
|
||||
return this.actual.text() === expected;
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
it('should interpolate a template with no parameters', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({});
|
||||
elm = compileHTML('<div error-display="This is a test"></div>');
|
||||
expect(elm).toInterpolateTo('This is a test');
|
||||
});
|
||||
|
||||
it('should interpolate a template with no parameters when search parameters are present', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({ p0: 'foobaz' });
|
||||
elm = compileHTML('<div error-display="This is a test"></div>');
|
||||
expect(elm).toInterpolateTo('This is a test');
|
||||
});
|
||||
|
||||
it('should correctly interpolate search parameters', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({ p0: '42' });
|
||||
elm = compileHTML('<div error-display="The answer is {0}"></div>');
|
||||
expect(elm).toInterpolateTo('The answer is 42');
|
||||
});
|
||||
|
||||
it('should interpolate parameters in the specified order', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({ p0: 'second', p1: 'first' });
|
||||
elm = compileHTML('<div error-display="{1} {0}"></div>');
|
||||
expect(elm).toInterpolateTo('first second');
|
||||
});
|
||||
|
||||
it('should preserve interpolation markers when fewer arguments than needed are provided', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({ p0: 'Fooooo' });
|
||||
elm = compileHTML('<div error-display="This {0} is {1} on {2}"></div>');
|
||||
expect(elm).toInterpolateTo('This Fooooo is {1} on {2}');
|
||||
});
|
||||
|
||||
it('should correctly handle the empty string as an interpolation parameter', function () {
|
||||
var elm;
|
||||
|
||||
spyOn($location, 'search').andReturn({ p0: 'test', p1: '' });
|
||||
elm = compileHTML('<div error-display="This {0} is a {1}"></div>');
|
||||
expect(elm).toInterpolateTo('This test is a ');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
describe("errorLinkFilter", function () {
|
||||
|
||||
var errorLinkFilter;
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
beforeEach(inject(function ($filter) {
|
||||
errorLinkFilter = $filter('errorLink');
|
||||
}));
|
||||
|
||||
it('should not change text that does not contain links', function () {
|
||||
expect(errorLinkFilter('This is a test')).toBe('This is a test');
|
||||
});
|
||||
|
||||
it('should find links in text and linkify them', function () {
|
||||
var output = errorLinkFilter("http://ab/ (http://a/) http://1.2/v:~-123. c");
|
||||
//temporary fix for IE8 sanitization whitespace bug
|
||||
output = output.replace('</a>(','</a> (');
|
||||
expect(output).
|
||||
toBe('<a href="http://ab/">http://ab/</a> ' +
|
||||
'(<a href="http://a/">http://a/</a>) ' +
|
||||
'<a href="http://1.2/v:~-123">http://1.2/v:~-123</a>. c');
|
||||
expect(errorLinkFilter(undefined)).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle mailto', function () {
|
||||
expect(errorLinkFilter("mailto:me@example.com")).
|
||||
toBe('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(errorLinkFilter("me@example.com")).
|
||||
toBe('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(errorLinkFilter("send email to me@example.com, but")).
|
||||
toBe('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
|
||||
});
|
||||
|
||||
it('should handle target', function () {
|
||||
expect(errorLinkFilter("http://example.com", "_blank")).
|
||||
toBe('<a target="_blank" href="http://example.com">http://example.com</a>')
|
||||
expect(errorLinkFilter("http://example.com", "someNamedIFrame")).
|
||||
toBe('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
|
||||
});
|
||||
|
||||
it('should not linkify stack trace URLs', function () {
|
||||
expect(errorLinkFilter("http://example.com/angular.min.js:42:1337")).
|
||||
toBe("http://example.com/angular.min.js:42:1337");
|
||||
});
|
||||
|
||||
it('should truncate linked URLs at 60 characters', function () {
|
||||
expect(errorLinkFilter("http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp")).
|
||||
toBe('<a href="http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp">' +
|
||||
'http://errors.angularjs.org/very-long-version-string/$inj...</a>');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// Copy/pasted from src/Angular.js, so that we can disable specific tests on IE.
|
||||
var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
|
||||
|
||||
var createMockWindow = function() {
|
||||
var mockWindow = {};
|
||||
var setTimeoutQueue = [];
|
||||
|
||||
mockWindow.location = window.location;
|
||||
mockWindow.document = window.document;
|
||||
mockWindow.getComputedStyle = angular.bind(window, window.getComputedStyle);
|
||||
mockWindow.scrollTo = angular.bind(window, window.scrollTo);
|
||||
mockWindow.navigator = window.navigator;
|
||||
mockWindow.setTimeout = function(fn, delay) {
|
||||
setTimeoutQueue.push({fn: fn, delay: delay});
|
||||
};
|
||||
mockWindow.setTimeout.queue = setTimeoutQueue;
|
||||
mockWindow.setTimeout.expect = function(delay) {
|
||||
if (setTimeoutQueue.length > 0) {
|
||||
return {
|
||||
process: function() {
|
||||
var tick = setTimeoutQueue.shift();
|
||||
expect(tick.delay).toEqual(delay);
|
||||
tick.fn();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
expect('SetTimoutQueue empty. Expecting delay of ').toEqual(delay);
|
||||
}
|
||||
};
|
||||
|
||||
return mockWindow;
|
||||
};
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
describe('Docs Syntax', function() {
|
||||
|
||||
beforeEach(module('bootstrap'));
|
||||
|
||||
describe('syntax', function() {
|
||||
|
||||
var id, element, document;
|
||||
|
||||
beforeEach(inject(function($compile, $rootScope, $document) {
|
||||
document = $document[0];
|
||||
//create the HTML elements missing in IE8 for this directive
|
||||
document.createElement('nav');
|
||||
|
||||
element = angular.element(
|
||||
'<div>' +
|
||||
'<pre syntax ' +
|
||||
'syntax-github="gh-url" ' +
|
||||
'syntax-plunkr="pl-url" ' +
|
||||
'syntax-fiddle="jf-url">' +
|
||||
'</pre>' +
|
||||
'</div>'
|
||||
);
|
||||
$compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
element = element[0];
|
||||
document.body.appendChild(element);
|
||||
}));
|
||||
|
||||
it("should properly prepare a github link in the page", function() {
|
||||
var github = element.querySelector('.syntax-github');
|
||||
expect(github.innerHTML).toMatch(/View on Github/i);
|
||||
expect(github.getAttribute('href')).toBe('gh-url');
|
||||
});
|
||||
|
||||
it("should properly prepare a plunkr link in the page", function() {
|
||||
var plunkr = element.querySelector('.syntax-plunkr');
|
||||
expect(plunkr.innerHTML).toMatch(/View on Plunkr/i);
|
||||
expect(plunkr.getAttribute('href')).toBe('pl-url');
|
||||
});
|
||||
|
||||
it("should properly prepare a jsfiddle link in the page", function() {
|
||||
var jsfiddle = element.querySelector('.syntax-jsfiddle');
|
||||
expect(jsfiddle.innerHTML).toMatch(/View on JSFiddle/i);
|
||||
expect(jsfiddle.getAttribute('href')).toBe('jf-url');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
describe('DocsApp', function() {
|
||||
|
||||
// Do not run this suite on Internet Explorer.
|
||||
if (msie < 10) return;
|
||||
|
||||
beforeEach(module('docsApp'));
|
||||
|
||||
describe('DocsVersionsCtrl', function() {
|
||||
var $scope, ctrl, window, version = '9.8.7';
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
$provide.value('$window', window = createMockWindow());
|
||||
});
|
||||
inject(function($controller, $rootScope) {
|
||||
$scope = $rootScope.$new();
|
||||
$scope.version = version;
|
||||
ctrl = $controller('DocsVersionsCtrl',{
|
||||
$scope : $scope,
|
||||
$window : window
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing the URL', function() {
|
||||
it('should jump to the url provided', function() {
|
||||
$scope.jumpToDocsVersion({ version: '1.0.1', url : 'page123'});
|
||||
expect(window.location).toBe('page123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
392
docs/components/angular-bootstrap/bootstrap.js
vendored
392
docs/components/angular-bootstrap/bootstrap.js
vendored
|
|
@ -1,392 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
|
||||
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.bind('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
var self = this;
|
||||
angular.element(document.body).bind('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.bind('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.bind('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
|
||||
});
|
||||
});
|
||||
|
|
@ -2,297 +2,6 @@
|
|||
@name API Reference
|
||||
@description
|
||||
|
||||
# AngularJS API Docs
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
|
||||
To prevent accidental name collisions with your code,
|
||||
Angular prefixes names of public objects with `$` and names of private objects with `$$`.
|
||||
Please do not use the `$` or `$$` prefix in your code.
|
||||
</div>
|
||||
|
||||
## Angular Namespace
|
||||
|
||||
|
||||
## {@link ng ng (core module)}
|
||||
This module is provided by default and contains the core components of AngularJS.
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>{@link ng#directive Directives}</td>
|
||||
<td>
|
||||
<p>
|
||||
This is the core collection of directives you would use in your template code to build an AngularJS application.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.directive:ngClick ngClick},
|
||||
{@link ng.directive:ngInclude ngInclude},
|
||||
{@link ng.directive:ngRepeat ngRepeat},
|
||||
etc… <br />
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
This is the core collection of services which are used within the DI of your application.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.$compile $compile},
|
||||
{@link ng.$http $http},
|
||||
{@link ngRoute.$routeParams $routeParams},
|
||||
{@link ng.$location $location},
|
||||
etc…
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#filter Filters}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ng.filter:filter filter},
|
||||
{@link ng.filter:date date},
|
||||
{@link ng.filter:currency currency},
|
||||
{@link ng.filter:lowercase lowercase},
|
||||
{@link ng.filter:uppercase uppercase},
|
||||
etc...
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ng#function Global APIs}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core global API functions are attached to the angular object. These core functions are useful for low level JavaScript operations within your application.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link angular.copy angular.copy()},
|
||||
{@link angular.equals angular.equals()},
|
||||
{@link angular.element angular.element()},
|
||||
etc...
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngRoute ngRoute}
|
||||
|
||||
Use ngRoute to enable URL routing to your application. The ngRoute module supports URL management via both hashbang and HTML5 pushState.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-route.js** file and set **ngRoute** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngRoute#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The following services are used for route management:
|
||||
<ul>
|
||||
<li>{@link ngRoute.$routeParams $routeParams} is used to access the querystring values present in the URL.</li>
|
||||
<li>{@link ngRoute.$route $route} is used to access the details of the route that is currently being accessed.</li>
|
||||
<li>{@link ngRoute.$routeProvider $routeProvider} is used to register routes for the application.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngRoute#directive Directives}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngRoute.directive:ngView ngView} directive will display the template of the current route within the page.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngAnimate ngAnimate}
|
||||
|
||||
Use ngAnimate to enable animation features into your application. Various core ng directives will provide
|
||||
animation hooks into your application when ngAnimate is included. Animations are defined by using CSS transitions/animations
|
||||
or JavaScript callbacks.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-animate.js** file and set **ngAnimate** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link ngAnimate.$animate $animate} to trigger animation operations within your directive code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate CSS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Follow ngAnimate’s CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngAnimate JS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link angular.Module#methods_animation module.animation()} to register a JavaScript animation. Once registered the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## {@link ngResource ngResource}
|
||||
|
||||
Use the ngResource module when querying and posting data to a REST API.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-resource.js** file and set **ngResource** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngResource#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngResource.$resource $resource} service is used to define RESTful objects which communicate with a REST API.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngCookies ngCookies}
|
||||
|
||||
Use the ngCookies module to handle cookie management within your application.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-cookies.js** file and set **ngCookies** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngCookies#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The following services are used for cookie management:
|
||||
<ul>
|
||||
<li>The {@link ngCookies.$cookies $cookie} service is a convenient wrapper to store simple data within browser cookies.</li>
|
||||
<li>{@link ngCookies.$cookieStore $cookieStore} is used to store more complex data using serialization.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngTouch ngTouch}
|
||||
|
||||
Use ngTouch when developing for mobile browsers/devices.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-touch.js** file and set **ngTouch** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#directive Directives}
|
||||
</td>
|
||||
<td>
|
||||
Various directives are available in ngTouch to emulate mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngSanitize ngSanitize}
|
||||
|
||||
Use ngSanitize to securely parse and manipulate HTML data in your application.
|
||||
|
||||
<div class="alert alert-info">Include the **angular-sanitize.js** file and set **ngSanitize** as a dependency for this to work in your application.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngSanitize#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngSanitize.$sanitize $sanitize} service is used to clean up dangerous HTML code in a quick and convenient way.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngTouch#filter Filters}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngSanitize.filter:linky linky filter} is used to turn URLs into HTML links within the provided string.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## {@link ngMock ngMock}
|
||||
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
|
||||
<div class="alert alert-info">Include the **angular-mocks.js** file into your test runner for this to work.</div>
|
||||
|
||||
<table class="definition-table spaced">
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngMock#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
ngMock will extend the behavior of various core services to become testing aware and manageable in a synchronous manner.
|
||||
<p>
|
||||
|
||||
<p>
|
||||
Some examples include:
|
||||
{@link ngMock.$timeout $timeout},
|
||||
{@link ngMock.$interval $interval},
|
||||
{@link ngMock.$log $log},
|
||||
{@link ngMock.$httpBackend $httpBackend},
|
||||
etc...
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{@link ngMock#function Global APIs}
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
Various helper functions are available to inject and mock modules within unit test code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some examples
|
||||
{@link angular.mock.inject inject()},
|
||||
{@link angular.mock.module module()},
|
||||
{@link angular.mock.dump dump()},
|
||||
etc...
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
Use the API Reference documentation when you need more information about a specific feature. Check out
|
||||
{@link guide/ Developer Guide} for AngularJS concepts. If you are new to AngularJS we recommend the
|
||||
{@link tutorial/ Tutorial}.
|
||||
|
|
|
|||
|
|
@ -2,10 +2,4 @@
|
|||
@name ng
|
||||
@description
|
||||
|
||||
# ng (core module)
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself
|
||||
contains the essential components for an AngularJS application to function. The table below
|
||||
lists a high level breakdown of each of the services/factories, filters, directives and testing
|
||||
components available within this core module.
|
||||
|
||||
<div doc-module-components="ng"></div>
|
||||
The `ng` is an angular module which contains all of the core angular services.
|
||||
|
|
|
|||
127
docs/content/cookbook/advancedform.ngdoc
Normal file
127
docs/content/cookbook/advancedform.ngdoc
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Advanced Form
|
||||
@description
|
||||
|
||||
Here we extend the basic form example to include common features such as reverting, dirty state
|
||||
detection, and preventing invalid form submission.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function UserForm($scope) {
|
||||
var master = {
|
||||
name: 'John Smith',
|
||||
address:{
|
||||
line1: '123 Main St.',
|
||||
city:'Anytown',
|
||||
state:'AA',
|
||||
zip:'12345'
|
||||
},
|
||||
contacts:[
|
||||
{type:'phone', value:'1(234) 555-1212'}
|
||||
]
|
||||
};
|
||||
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy(master);
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
master = $scope.form;
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.addContact = function() {
|
||||
$scope.form.contacts.push({type:'', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
var contacts = $scope.form.contacts;
|
||||
for (var i = 0, ii = contacts.length; i < ii; i++) {
|
||||
if (contact === contacts[i]) {
|
||||
contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isCancelDisabled = function() {
|
||||
return angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.isSaveDisabled = function() {
|
||||
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="UserForm">
|
||||
|
||||
<form name="myForm">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng-model="form.name" required/> <br/><br/>
|
||||
|
||||
<label>Address:</label> <br/>
|
||||
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
|
||||
<input type="text" ng-model="form.address.city" size="12" required/>,
|
||||
<input type="text" ng-model="form.address.state" size="2"
|
||||
ng-pattern="state" required/>
|
||||
<input type="text" ng-model="form.address.zip" size="5"
|
||||
ng-pattern="zip" required/><br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in form.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required/>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function() {
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function() {
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
#Things to notice
|
||||
|
||||
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
|
||||
save.
|
||||
* Save button is only enabled if there are no validation errors on the form.
|
||||
* Cancel reverts the form changes back to original state.
|
||||
* Save updates the internal model of the form.
|
||||
* Debug view shows the two models. One presented to the user form and the other being the pristine
|
||||
copy master.
|
||||
63
docs/content/cookbook/buzz.ngdoc
Normal file
63
docs/content/cookbook/buzz.ngdoc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Resources - Buzz
|
||||
@description
|
||||
|
||||
External resources are URLs that provide JSON data, which are then rendered with the help of
|
||||
templates. Angular has a resource factory that can be used to give names to the URLs and then
|
||||
attach behavior to them. For example you can use the
|
||||
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
|
||||
API}
|
||||
to retrieve Buzz activity and comments.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
BuzzController.$inject = ['$scope', '$resource'];
|
||||
function BuzzController($scope, $resource) {
|
||||
$scope.userId = 'googlebuzz';
|
||||
$scope.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt: 'json', callback: 'JSON_CALLBACK'},
|
||||
{ get: {method: 'JSONP', params: {visibility: '@self'}},
|
||||
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
|
||||
});
|
||||
|
||||
$scope.fetch = function() {
|
||||
$scope.activities = $scope.Activity.get({userId:this.userId});
|
||||
}
|
||||
|
||||
$scope.expandReplies = function(activity) {
|
||||
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng-controller="BuzzController">
|
||||
<input ng-model="userId"/>
|
||||
<button ng-click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div class="buzz" ng-repeat="item in activities.data.items">
|
||||
<h1 style="font-size: 15px;">
|
||||
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng-click="expandReplies(item)" style="float: right;">
|
||||
Expand replies: {{item.links.replies[0].count}}
|
||||
</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
|
||||
{{reply.content | html}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
xit('fetch buzz and expand', function() {
|
||||
element(':button:contains(fetch)').click();
|
||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
||||
element('.buzz a:contains(Expand replies):first').click();
|
||||
expect(repeater('div.reply').count()).toBeGreaterThan(0);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
151
docs/content/cookbook/deeplinking.ngdoc
Normal file
151
docs/content/cookbook/deeplinking.ngdoc
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Deep Linking
|
||||
@description
|
||||
|
||||
Deep linking allows you to encode the state of the application in the URL so that it can be
|
||||
bookmarked and the application can be restored from the URL to the same state.
|
||||
|
||||
While Angular does not force you to deal with bookmarks in any particular way, it has services
|
||||
which make the common case described here very easy to implement.
|
||||
|
||||
# Assumptions
|
||||
|
||||
Your application consists of a single HTML page which bootstraps the application. We will refer
|
||||
to this page as the chrome.
|
||||
Your application is divided into several screens (or views) which the user can visit. For example,
|
||||
the home screen, settings screen, details screen, etc. For each of these screens, we would like to
|
||||
assign a URL so that it can be bookmarked and later restored. Each of these screens will be
|
||||
associated with a controller which define the screen's behavior. The most common case is that the
|
||||
screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can
|
||||
have multiple partials, but a single partial is the most common construct. This example makes the
|
||||
partial boundary visible using a blue line.
|
||||
|
||||
You can make a routing table which shows which URL maps to which partial view template and which
|
||||
controller.
|
||||
|
||||
# Example
|
||||
|
||||
In this example we have a simple app which consist of two screens:
|
||||
|
||||
* Welcome: url `welcome` Show the user contact information.
|
||||
* Settings: url `settings` Show an edit screen for user contact information.
|
||||
|
||||
<example module="deepLinking" deps="angular-sanitize.js">
|
||||
<file name="script.js">
|
||||
angular.module('deepLinking', ['ngSanitize'])
|
||||
.config(function($routeProvider) {
|
||||
$routeProvider.
|
||||
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
|
||||
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
|
||||
});
|
||||
|
||||
AppCntl.$inject = ['$scope', '$route']
|
||||
function AppCntl($scope, $route) {
|
||||
$scope.$route = $route;
|
||||
|
||||
// initialize the model to something useful
|
||||
$scope.person = {
|
||||
name:'anonymous',
|
||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
||||
};
|
||||
}
|
||||
|
||||
function WelcomeCntl($scope) {
|
||||
$scope.greet = function() {
|
||||
alert("Hello " + $scope.person.name);
|
||||
};
|
||||
}
|
||||
|
||||
function SettingsCntl($scope, $location) {
|
||||
$scope.cancel = function() {
|
||||
$scope.form = angular.copy($scope.person);
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
angular.copy($scope.form, $scope.person);
|
||||
$location.path('/welcome');
|
||||
};
|
||||
|
||||
$scope.cancel();
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
[ng-view] {
|
||||
border: 1px solid blue;
|
||||
margin: 0;
|
||||
padding:1em;
|
||||
}
|
||||
|
||||
.partial-info {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="AppCntl">
|
||||
<h1>Your App Chrome</h1>
|
||||
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
|
||||
<hr/>
|
||||
<span class="partial-info">
|
||||
Partial: {{$route.current.template}}
|
||||
</span>
|
||||
<div ng-view></div>
|
||||
<small>Your app footer </small>
|
||||
</div>
|
||||
</file>
|
||||
<file name="settings.html">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
</file>
|
||||
<file name="welcome.html">
|
||||
Hello {{person.name}},
|
||||
<div>
|
||||
Your contact information:
|
||||
<div ng:repeat="contact in person.contacts">{{contact.type}}:
|
||||
<span ng-bind-html="contact.url|linky"></span>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should navigate to URL', function() {
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
input('form.name').enter('yourname');
|
||||
element(':button:contains(Save)').click();
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
|
||||
initialization of the {@link api/ng.$route $route} service with the proper URL
|
||||
routes.
|
||||
* The {@link api/ng.$route $route} service then watches the URL and instantiates the
|
||||
appropriate controller when the URL changes.
|
||||
* The {@link api/ng.directive:ngView ngView} widget loads the
|
||||
view when the URL changes. It also sets the view scope to the newly instantiated controller.
|
||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
||||
the URL is changed programatically or by the user.
|
||||
114
docs/content/cookbook/form.ngdoc
Normal file
114
docs/content/cookbook/form.ngdoc
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Form
|
||||
@description
|
||||
|
||||
A web application's main purpose is to present and gather data. For this reason Angular strives
|
||||
to make both of these operations trivial. This example shows off how you can build a simple form to
|
||||
allow a user to enter data.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function FormController($scope) {
|
||||
var user = $scope.user = {
|
||||
name: 'John Smith',
|
||||
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
|
||||
contacts:[{type:'phone', value:'1(234) 555-1212'}]
|
||||
};
|
||||
$scope.state = /^\w\w$/;
|
||||
$scope.zip = /^\d\d\d\d\d$/;
|
||||
|
||||
$scope.addContact = function() {
|
||||
user.contacts.push({type:'email', value:''});
|
||||
};
|
||||
|
||||
$scope.removeContact = function(contact) {
|
||||
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
|
||||
if (contact === user.contacts[i]) {
|
||||
$scope.user.contacts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br>
|
||||
<input type="text" ng-model="user.name" required/> <br><br>
|
||||
|
||||
<label>Address:</label><br>
|
||||
<input type="text" ng-model="user.address.line1" size="33" required> <br>
|
||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
||||
<input type="text" ng-model="user.address.state"
|
||||
ng-pattern="state" size="2" required>
|
||||
<input type="text" ng-model="user.address.zip" size="5"
|
||||
ng-pattern="zip" required><br><br>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng-click="addContact()">add</a> ]
|
||||
<div ng-repeat="contact in user.contacts">
|
||||
<select ng-model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng-model="contact.value" required>
|
||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>user={{user | json}}</pre>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function() {
|
||||
expect(binding('user')).toMatch(/John Smith/);
|
||||
});
|
||||
it('should add contact', function() {
|
||||
using('.example').element('a:contains(add)').click();
|
||||
using('.example div:last').input('contact.value').enter('you@example.org');
|
||||
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
|
||||
expect(binding('user')).toMatch(/you@example.org/);
|
||||
});
|
||||
|
||||
it('should remove contact', function() {
|
||||
using('.example').element('a:contains(X)').click();
|
||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
||||
});
|
||||
|
||||
it('should validate zip', function() {
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.zip').enter('abc');
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).toMatch(/ng-invalid/);
|
||||
});
|
||||
|
||||
it('should validate state', function() {
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.state').enter('XXX');
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.toMatch(/ng-invalid/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
|
||||
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
* The {@link api/ng.directive:input input directives} simply refer
|
||||
to the model and are data-bound.
|
||||
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
|
||||
* In your application you can simply read from or write to the model and the form will be updated.
|
||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
||||
reflected in the view.
|
||||
39
docs/content/cookbook/helloworld.ngdoc
Normal file
39
docs/content/cookbook/helloworld.ngdoc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function HelloCntl($scope) {
|
||||
$scope.name = 'World';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="HelloCntl">
|
||||
Your name: <input type="text" ng-model="name" value="World"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should change the binding when user enters text', function() {
|
||||
expect(binding('name')).toEqual('World');
|
||||
input('name').enter('angular');
|
||||
expect(binding('name')).toEqual('angular');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# Things to notice
|
||||
|
||||
Take a look through the source and note:
|
||||
|
||||
* The script tag that {@link guide/bootstrap bootstraps} the Angular environment.
|
||||
* The text {@link api/ng.directive:input input form control} which is
|
||||
bound to the greeting name text.
|
||||
* There is no need for listener registration and event firing on change events.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
|
||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
||||
* The concept of {@link guide/dev_guide.templates.databinding data binding}, which reflects any
|
||||
changes to the
|
||||
input field in the greeting text.
|
||||
58
docs/content/cookbook/index.ngdoc
Normal file
58
docs/content/cookbook/index.ngdoc
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook
|
||||
@description
|
||||
|
||||
Welcome to the Angular cookbook. Here we will show you typical uses of Angular by example.
|
||||
|
||||
|
||||
# Hello World
|
||||
|
||||
{@link helloworld Hello World}: The simplest possible application that demonstrates the
|
||||
classic Hello World!
|
||||
|
||||
|
||||
# Basic Form
|
||||
|
||||
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
|
||||
of web applications. Angular makes forms easy through bidirectional data binding.
|
||||
|
||||
|
||||
# Advanced Form
|
||||
|
||||
{@link advancedform Advanced Form}: Taking the form example to the next level and
|
||||
providing advanced features such as dirty detection, form reverting and submit disabling if
|
||||
validation errors exist.
|
||||
|
||||
|
||||
# Model View Controller
|
||||
|
||||
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
|
||||
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
|
||||
separation aids in maintainability and testability of your project.
|
||||
|
||||
|
||||
# Multi-page App and Deep Linking
|
||||
|
||||
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
|
||||
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
|
||||
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
|
||||
prevent you from emailing links to a particular screen within your application.
|
||||
|
||||
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
|
||||
allowing you to send links to specific screens in your app.
|
||||
|
||||
|
||||
# Services
|
||||
|
||||
{@link api/ng Services}: Services are long lived objects in your applications that are
|
||||
available across controllers. A collection of useful services are pre-bundled with Angular but you
|
||||
will likely add your own. Services are initialized using dependency injection, which resolves the
|
||||
order of initialization. This safeguards you from the perils of global state (a common way to
|
||||
implement long lived objects).
|
||||
|
||||
|
||||
# External Resources
|
||||
|
||||
{@link buzz Resources}: Web applications must be able to communicate with the external
|
||||
services to get and update data. Resources are the abstractions of external URLs which are
|
||||
specially tailored to Angular data binding.
|
||||
128
docs/content/cookbook/mvc.ngdoc
Normal file
128
docs/content/cookbook/mvc.ngdoc
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
@ngdoc overview
|
||||
@name Cookbook: MVC
|
||||
@description
|
||||
|
||||
MVC allows for a clean and testable separation between the behavior (controller) and the view
|
||||
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
|
||||
view. This makes it very easy for the controller and the view to share the model.
|
||||
|
||||
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
|
||||
This makes it very easy to test the controller in isolation since one can simply instantiate the
|
||||
controller and test without a view, because there is no connection between the controller and the
|
||||
view.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function TicTacToeCntl($scope, $location) {
|
||||
$scope.cellStyle= {
|
||||
'height': '20px',
|
||||
'width': '20px',
|
||||
'border': '1px solid black',
|
||||
'text-align': 'center',
|
||||
'vertical-align': 'middle',
|
||||
'cursor': 'pointer'
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.board = [
|
||||
['', '', ''],
|
||||
['', '', ''],
|
||||
['', '', '']
|
||||
];
|
||||
$scope.nextMove = 'X';
|
||||
$scope.winner = '';
|
||||
setUrl();
|
||||
};
|
||||
|
||||
$scope.dropPiece = function(row, col) {
|
||||
if (!$scope.winner && !$scope.board[row][col]) {
|
||||
$scope.board[row][col] = $scope.nextMove;
|
||||
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
|
||||
setUrl();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
$scope.$watch(function() { return $location.search().board;}, readUrl);
|
||||
|
||||
function setUrl() {
|
||||
var rows = [];
|
||||
angular.forEach($scope.board, function(row) {
|
||||
rows.push(row.join(','));
|
||||
});
|
||||
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
|
||||
}
|
||||
|
||||
function grade() {
|
||||
var b = $scope.board;
|
||||
$scope.winner =
|
||||
row(0) || row(1) || row(2) ||
|
||||
col(0) || col(1) || col(2) ||
|
||||
diagonal(-1) || diagonal(1);
|
||||
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
|
||||
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
}
|
||||
|
||||
function readUrl(value) {
|
||||
if (value) {
|
||||
value = value.split('/');
|
||||
$scope.nextMove = value[1];
|
||||
angular.forEach(value[0].split(';'), function(row, col){
|
||||
$scope.board[col] = row.split(',');
|
||||
});
|
||||
grade();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3>Tic-Tac-Toe</h3>
|
||||
<div ng-controller="TicTacToeCntl">
|
||||
Next Player: {{nextMove}}
|
||||
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
|
||||
<table class="board">
|
||||
<tr ng-repeat="row in board" style="height:15px;">
|
||||
<td ng-repeat="cell in row" ng-style="cellStyle"
|
||||
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng-click="reset()">reset board</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function() {
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
expect(binding('nextMove')).toEqual('X');
|
||||
piece(1, 2);
|
||||
piece(3, 2);
|
||||
piece(1, 3);
|
||||
expect(element('.winner').text()).toEqual('Player X has won!');
|
||||
});
|
||||
|
||||
function piece(row, col) {
|
||||
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
|
||||
}
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Things to notice
|
||||
|
||||
* The controller is defined in JavaScript and has no reference to the rendering logic.
|
||||
* The controller is instantiated by Angular and injected into the view.
|
||||
* The controller can be instantiated in isolation (without a view) and the code will still execute.
|
||||
This makes it very testable.
|
||||
* The HTML view is a projection of the model. In the above example, the model is stored in the
|
||||
board variable.
|
||||
* All of the controller's properties (such as board and nextMove) are available to the view.
|
||||
* Changing the model changes the view.
|
||||
* The view can call any controller function.
|
||||
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
|
||||
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
|
||||
api/ng.$rootScope.Scope#$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $animate:notcsel
|
||||
@fullName Not class CSS selector
|
||||
@description
|
||||
|
||||
Expecting a CSS selector for class. Class selectors must start with `.`, for example: `.my-class-name`.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $cacheFactory:iid
|
||||
@fullName Invalid ID
|
||||
@description
|
||||
|
||||
This error occurs when trying to create a new `cache` object via {@link api/ng.$cacheFactory} with an ID that was already used to create another cache object.
|
||||
|
||||
To resolve the error please use a different cache ID when calling `$cacheFactory`.
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:ctreq
|
||||
@fullName Missing Required Controller
|
||||
@description
|
||||
|
||||
This error occurs when {@link api/ng.$compile HTML compiler} tries to process a directive that specifies the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object `require` option} in a {@link api/ng.$compile#description_comprehensive-directive-api directive definition},
|
||||
but the required directive controller is not present on the current DOM element (or its ancestor element, if `^` was specified).
|
||||
|
||||
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 a ancestor element, make ensure that you prefix the controller name in the `require` definition with `^`.
|
||||
|
||||
If the required controller is optionally requested, use `?` or `^?` to specify that.
|
||||
|
||||
|
||||
Example of a directive that requires {@link api/ng.directive:ngModel ngModel} controller:
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This directive can then be used as:
|
||||
```
|
||||
<input ng-model="some.path" my-directive>
|
||||
```
|
||||
|
||||
|
||||
Example of a directive that optionally requires a {@link api/ng.directive:form form} controller from an ancestor:
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
require: '^?form',
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This directive can then be used as:
|
||||
```
|
||||
<form name="myForm">
|
||||
<div>
|
||||
<span my-directive></span>
|
||||
</div>
|
||||
</form>
|
||||
```
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:iscp
|
||||
@fullName Invalid Isolate Scope Definition
|
||||
@description
|
||||
|
||||
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() {
|
||||
return {
|
||||
...
|
||||
scope: {
|
||||
'attrName': '@', // OK
|
||||
'attrName2': '=localName', // OK
|
||||
'attrName3': 'name', // ERROR: missing mode @&=
|
||||
'attrName4': ' = name', // ERROR: extra spaces
|
||||
'attrName5': 'name=', // ERROR: must be prefixed with @&=
|
||||
}
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Please refer to the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
|
||||
`scope` option} of the directive definition documentation to learn more about the API.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:multidir
|
||||
@fullName Multiple Directive Resource Contention
|
||||
@description
|
||||
|
||||
This error occurs when multiple directives are applied to the same DOM element, and
|
||||
processing them would result in a collision or an unsupported configuration.
|
||||
|
||||
|
||||
To resolve this issue remove one of the directives which is causing the collision.
|
||||
|
||||
Example scenarios of multiple incompatible directives applied to the same element include:
|
||||
|
||||
* Multiple directives requesting `isolated scope`.
|
||||
* Multiple directives publishing a controller under the same name.
|
||||
* Multiple directives declared with the `transclusion` option.
|
||||
* Multiple directives attempting to define a `template` or `templateURL`.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:nodomevents
|
||||
@fullName Interpolated Event Attributes
|
||||
@description
|
||||
|
||||
This error occurs when one tries to create a binding for event handler attributes like `onclick`, `onload`, `onsubmit`, etc.
|
||||
|
||||
There is no practical value in binding to these attributes and doing so only exposes your application to security vulnerabilities like XSS.
|
||||
For these reasons binding to event handler attributes (all attributes that start with `on` and `formaction` attribute) is not supported.
|
||||
|
||||
|
||||
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
|
||||
```
|
||||
<input ng-model="username">
|
||||
<div onclick="{{username}}">click me</div>
|
||||
```
|
||||
|
||||
Since the `onclick` evaluates the value as JavaScript code in the window context, setting the `username` model to a value like `javascript:alert('PWND')` would result in script injection when the `div` is clicked.
|
||||
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:nonassign
|
||||
@fullName Non-Assignable Expression
|
||||
@description
|
||||
|
||||
This error occurs when a directive defines an isolate scope property
|
||||
(using the `=` mode in the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
|
||||
`scope` option} of a directive definition) but the directive is used with an expression that is not-assignable.
|
||||
|
||||
In order for the two-way data-binding to work, it must be possible to write new values back into the path defined with the expression.
|
||||
|
||||
For example, given a directive:
|
||||
|
||||
```
|
||||
myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
...
|
||||
scope: {
|
||||
'bind': '=localValue'
|
||||
}
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Following are invalid uses of this directive:
|
||||
```
|
||||
<!-- ERROR because `1+2=localValue` is an invalid statement -->
|
||||
<my-directive bind="1+2">
|
||||
|
||||
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
|
||||
<my-directive bind="myFn()">
|
||||
```
|
||||
|
||||
|
||||
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']">
|
||||
```
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:selmulti
|
||||
@fullName Binding to Multiple Attribute
|
||||
@description
|
||||
|
||||
Binding to the `multiple` attribute of `select` element is not supported since switching between multiple and single mode changes the {@link api/ng.directive:ngModel `ngModel`} object type from instance to array of instances which breaks the model semantics.
|
||||
|
||||
If you need to use different types of `select` elements in your template based on some variable, please use {@link api/ng.directive:ngIf ngIf} or {@link api/ng.directive:ngSwitch ngSwitch} directives to select one of them to be used at runtime.
|
||||
|
||||
|
||||
Example with invalid usage:
|
||||
```
|
||||
<select ng-model="some.model" multiple="{{mode}}"></select>
|
||||
```
|
||||
|
||||
Example that uses ngIf to pick one of the `select` elements based on a variable:
|
||||
```
|
||||
<select ng-if="mode == 'multiple'" ng-model="some.model" multiple></select>
|
||||
<select ng-if="mode != 'multiple'" ng-model="some.model"></select>
|
||||
```
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:tpload
|
||||
@fullName Error Loading Template
|
||||
@description
|
||||
|
||||
This error occurs when {@link api/ng.$compile `$compile`} attempts to fetch a template from some URL, and the request fails.
|
||||
|
||||
To resolve this error, ensure that the URL of the template is spelled correctly and resolves to correct absolute URL.
|
||||
The [Chrome Developer Tools](https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview) might also be helpful in determining why the request failed.
|
||||
|
||||
If you are using {@link api/ng.$templateCache} to pre-load templates, ensure that the cache was populated with the template.
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:tplrt
|
||||
@fullName Invalid Template Root
|
||||
@description
|
||||
|
||||
When a directive is declared with `template` (or `templateUrl`) and `replace` mode on, the template
|
||||
must have exactly one root element. That is, the text of the template property or the content
|
||||
referenced by the templateUrl must be contained within a single html element.
|
||||
For example, `<p>blah <em>blah</em> blah</p>` instead of simply `blah <em>blah</em> blah`.
|
||||
Otherwise, the replacement operation would result in a single element (the directive) being replaced
|
||||
with multiple elements or nodes, which is unsupported and not commonly needed in practice.
|
||||
|
||||
|
||||
For example a directive with definition:
|
||||
|
||||
```
|
||||
myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
...
|
||||
replace: true,
|
||||
templateUrl: 'someUrl'
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
And a template provided at URL `someUrl`. The template must be an html fragment that has only a
|
||||
single root element, like the `div` element in this template:
|
||||
|
||||
```
|
||||
<div><b>Hello</b> World!</div>
|
||||
```
|
||||
|
||||
An an invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
elements. For example:
|
||||
|
||||
```
|
||||
<b>Hello</b> World!
|
||||
```
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $compile:uterdir
|
||||
@fullName Unterminated Directive
|
||||
@description
|
||||
|
||||
This error occurs when using multi-element directives and a `directive-start` attribute fails to form a matching pair with a corresponding `directive-end` attribute.
|
||||
A `directive-start` should have a matching `directive-end` on a sibling node in the DOM. For instance,
|
||||
|
||||
```
|
||||
<table>
|
||||
<tr ng-repeat-start="item in list">I get repeated</tr>
|
||||
<tr ng-repeat-end>I also get repeated</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
is a valid example.
|
||||
|
||||
This error can occur in several different ways. One is by leaving out the `directive-end` attribute, like so:
|
||||
|
||||
```
|
||||
<div>
|
||||
<span foo-start></span>
|
||||
</div>
|
||||
```
|
||||
|
||||
Another is by nesting a `directive-end` inside of `directive-start`, or vice versa:
|
||||
|
||||
```
|
||||
<div>
|
||||
<span foo-start><span foo-end></span></span>
|
||||
</div>
|
||||
```
|
||||
|
||||
To avoid this error, make sure each `directive-start` you use has a matching `directive-end` on a sibling node in the DOM.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $controller:noscp
|
||||
@fullName Missing $scope object
|
||||
@description
|
||||
|
||||
This error occurs when {@link api/ng.$controller $controller} service is called in order to instantiate a new controller but no scope is provided via `$scope` property of the locals map.
|
||||
|
||||
Example of incorrect usage that leads to this error:
|
||||
```
|
||||
$controller(MyController);
|
||||
//or
|
||||
$controller(MyController, {scope: newScope});
|
||||
```
|
||||
|
||||
To fix the example above please provide a scope to the $controller call:
|
||||
|
||||
```
|
||||
$controller(MyController, {$scope, newScope});
|
||||
```
|
||||
|
||||
Please consult the {@link api/ng.$controller $controller} service api docs to learn more.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $httpBackend:noxhr
|
||||
@fullName Unsupported XHR
|
||||
@description
|
||||
|
||||
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
||||
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
||||
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
||||
supported browser.
|
||||
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Error Reference
|
||||
@description
|
||||
|
||||
Use the Error Reference manual to find information about error conditions in
|
||||
your AngularJS app. Errors thrown in production builds of AngularJS will log
|
||||
links to this site on the console.
|
||||
|
||||
Other useful references for debugging your app include:
|
||||
|
||||
- {@link api/ API Reference} for detailed information about specific features
|
||||
- {@link guide/ Developer Guide} for AngularJS concepts
|
||||
- {@link tutorial/ Tutorial} for getting started
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:cdep
|
||||
@fullName Circular Dependency
|
||||
@description
|
||||
|
||||
This error occurs when the {@link api/angular.injector $injector} tries to get
|
||||
a service that depends on itself, either directly or indirectly. To fix this,
|
||||
construct your dependency chain such that there are no circular dependencies.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
.factory('myService', function (myService) {
|
||||
// ...
|
||||
})
|
||||
.controller('MyCtrl', function ($scope, myService) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
When an instance of `MyCtrl` is created, the service `myService` will be created
|
||||
by the `$injector`. `myService` depends on itself, which causes the `$injector`
|
||||
to detect a circular dependency and throw the error.
|
||||
|
||||
For more information, see the {@link guide/di Dependency Injection Guide}.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:itkn
|
||||
@fullName Bad Injection Token
|
||||
@description
|
||||
|
||||
This error occurs when using a bad token as a dependency injection annotation.
|
||||
Dependency injection annotation tokens should always be strings. Using any other
|
||||
type will cause this error to be thrown.
|
||||
|
||||
Examples of code with bad injection tokens include:
|
||||
|
||||
```
|
||||
var myCtrl = function ($scope, $http) { /* ... */ };
|
||||
myCtrl.$inject = ['$scope', 42];
|
||||
|
||||
myAppModule.controller('MyCtrl', ['$scope', {}, function ($scope, $timeout) {
|
||||
// ...
|
||||
}]);
|
||||
```
|
||||
|
||||
The bad injection tokens are `42` in the first example and `{}` in the second.
|
||||
To avoid the error, always use string literals for dependency injection annotation
|
||||
tokens.
|
||||
|
||||
For an explanation of what injection annotations are and how to use them, refer
|
||||
to the {@link guide/di Dependency Injection Guide}.
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:modulerr
|
||||
@fullName Module Error
|
||||
@description
|
||||
|
||||
This error occurs when a module fails to load due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
||||
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
|
||||
If you are getting this error after upgrading to `1.2.x`, be sure that you've
|
||||
installed {@link api/ngRoute `ngRoute`}.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:nomod
|
||||
@fullName Module Unavailable
|
||||
@description
|
||||
|
||||
This error occurs when trying to "re-open" a module that has not yet been defined.
|
||||
|
||||
To define a new module, call {@link api/angular.module angular.module} with a name
|
||||
and an array of dependent modules, like so:
|
||||
|
||||
```
|
||||
// When defining a module with no module dependencies,
|
||||
// the requires array should be defined and empty.
|
||||
var myApp = angular.module('myApp', []);
|
||||
```
|
||||
|
||||
To retrieve a reference to the same module for further configuration, call
|
||||
`angular.module` without the `requires` array.
|
||||
|
||||
```
|
||||
var myApp = angular.module('myApp');
|
||||
```
|
||||
|
||||
Calling `angular.module` without the `requires` array when the module has not yet
|
||||
been defined causes this error to be thrown. To fix it, define your module with
|
||||
a name and an empty array, as in the first example above.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:pget
|
||||
@fullName Provider Missing $get
|
||||
@description
|
||||
|
||||
This error occurs when attempting to register a provider that does not have a
|
||||
`$get` method. For example:
|
||||
|
||||
```
|
||||
function BadProvider() {} // No $get method!
|
||||
angular.module("myApp", [])
|
||||
.provider('bad', BadProvider); // this throws the error
|
||||
```
|
||||
|
||||
To fix the error, fill in the `$get` method on the provider like so:
|
||||
|
||||
```
|
||||
function GoodProvider() {
|
||||
this.$get = angular.noop;
|
||||
}
|
||||
angular.module("myApp", [])
|
||||
.provider('good', GoodProvider);
|
||||
```
|
||||
|
||||
For more information, refer to the {@link api/AUTO.$provide#methods_provider
|
||||
$provide.provider} api doc.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $injector:unpr
|
||||
@fullName Unknown Provider
|
||||
@description
|
||||
|
||||
This error results from the `$injector` being unable to resolve a required
|
||||
dependency. To fix this, make sure the dependency is defined and spelled
|
||||
correctly. For example:
|
||||
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
.controller('myCtrl', ['myService', function (myService) {
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
||||
This code will fail with `$injector:unpr` if `myService` is not defined. Making
|
||||
sure each dependency is defined will fix the problem.
|
||||
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
.service('myService', function () { /* ... */ })
|
||||
.controller('myCtrl', ['myService', function (myService) {
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $interpolate:interr
|
||||
@fullName Interpolation Error
|
||||
@description
|
||||
|
||||
This error occurs when interpolation fails due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $interpolate:noconcat
|
||||
@fullName Multiple Expressions
|
||||
@description
|
||||
|
||||
This error occurs when performing an interpolation that concatenates multiple
|
||||
expressions when a trusted value is required. Concatenating expressions makes
|
||||
it hard to reason about whether some combination of concatenated values are
|
||||
unsafe to use and could easily lead to XSS.
|
||||
|
||||
For more information about how AngularJS helps keep your app secure, refer to
|
||||
the {@link api/ng.$sce $sce} API doc.
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@ngdoc error
|
||||
@name jqLite:nosel
|
||||
@fullName Unsupported Selector Lookup
|
||||
@description
|
||||
|
||||
In order to keep Angular small, Angular implements only a subset of the selectors in {@link api/angular.element#description_angulars-jqlite jqLite}.
|
||||
This error occurs when a jqLite instance is invoked with a selector other than this subset.
|
||||
|
||||
In order to resolve this error, rewrite your code to only use tag name selectors and manually traverse the DOM using the APIs provided by jqLite.
|
||||
|
||||
Alternatively, you can include a full version of jQuery, which Angular will automatically use and that will make all selectors available.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
@ngdoc error
|
||||
@name jqLite:offargs
|
||||
@fullName Invalid jqLite#off() parameter
|
||||
@description
|
||||
|
||||
This error occurs when trying to pass too many arguments to `jqLite#off`. Note
|
||||
that `jqLite#off` does not support namespaces or selectors like jQuery.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name jqLite:onargs
|
||||
@fullName Invalid jqLite#on() Parameters
|
||||
@description
|
||||
|
||||
This error occurs when trying to pass too many arguments to `jqLite#on`. Note
|
||||
that `jqLite#on` does not support the `selector` or `eventData` parameters as
|
||||
jQuery does.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $location:ihshprfx
|
||||
@fullName Missing Hash Prefix
|
||||
@description
|
||||
|
||||
This error occurs when {@link api/ng.$location $location} service is configured to use a hash prefix but this prefix was not present in a url that the `$location` service was asked to parse.
|
||||
|
||||
For example if you configure `$location` service with prefix `'!'`:
|
||||
```
|
||||
myApp.config(function($locationProvider) {
|
||||
$locationProvider.prefix('!');
|
||||
});
|
||||
```
|
||||
|
||||
If you enter the app at url `http:/myapp.com/#/myView` this error will be throw.
|
||||
|
||||
The correct url for this configuration is `http:/myapp.com/#!/myView` (note the `'!'` after `'#'` symbol).
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $location:ipthprfx
|
||||
@fullName Invalid or Missing Path Prefix
|
||||
@description
|
||||
|
||||
This error occurs when you configure the {@link api/ng.$location `$location`} service in the html5 mode, specify a base url for your application via `<base>` element and try to update the location with a path that doesn't match the base prefix.
|
||||
|
||||
To resolve this issue, please check the base url specified via the `<base>` tag in the head of your main html document, as well as the url that you tried to set the location to.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $location:isrcharg
|
||||
@fullName Wrong $location.search() argument type
|
||||
@description
|
||||
|
||||
To resolve this error, ensure that the first argument for the `$location.search` call is a `string` or an object.
|
||||
You can use the stack trace associated with this error to identify the call site that caused this issue.
|
||||
|
||||
To learn more, please consult the {@link api/ng.$location `$location`} api docs.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ng:areq
|
||||
@fullName Bad Argument
|
||||
@description
|
||||
|
||||
AngularJS often asserts that certain values will be present and truthy using a
|
||||
helper function. If the assertion fails, this error is thrown. To fix this problem,
|
||||
make sure that the value the assertion expects is defined and truthy.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ng:badname
|
||||
@fullName Bad `hasOwnProperty` Name
|
||||
@description
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` in a context where it is not allow.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ng:btstrpd
|
||||
@fullName App Already Bootstrapped with this Element
|
||||
@description
|
||||
|
||||
Occurs when calling angular.bootstrap on an element that has already been bootstrapped.
|
||||
|
||||
This usually happens when you accidentally use both `ng-app` and `angular.bootstrap` to bootstrap an application.
|
||||
|
||||
```
|
||||
<html>
|
||||
...
|
||||
<body ng-app="myApp">
|
||||
<script>
|
||||
angular.bootstrap(document.body, ['myApp']);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that for bootrapping purposes, the `<html>` element is the same as `document`, so the following will also throw an error.
|
||||
```
|
||||
<html>
|
||||
...
|
||||
<script>
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
</script>
|
||||
</html>
|
||||
```
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ng:cpi
|
||||
@fullName Bad Copy
|
||||
@description
|
||||
|
||||
This error occurs when attempting to copy an object to itself. Calling {@link
|
||||
api/angular.copy angular.copy} with a `destination` object deletes
|
||||
all of the elements or properties on `destination` before copying to it. Copying
|
||||
an object to itself is not supported. Make sure to check your calls to
|
||||
`angular.copy` and avoid copying objects or arrays to themselves.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ng:cpws
|
||||
@fullName Copying Window or Scope
|
||||
@description
|
||||
|
||||
Copying Window or Scope instances is not supported because of cyclical and self
|
||||
references. Avoid copying windows and scopes, as well as any other cyclical or
|
||||
self-referential structures. Note that trying to deep copy an object containing
|
||||
cyclical references that is neither a window nor a scope will cause infinite
|
||||
recursion and a stack overflow.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngModel:nonassign
|
||||
@fullName Non-Assignable Expression
|
||||
@description
|
||||
|
||||
This error occurs when expression the {@link api/ng.directive:ngModel ngModel} directive is bound to is a non-assignable expression.
|
||||
|
||||
Examples using assignable expressions include:
|
||||
|
||||
```
|
||||
<input ng-model="namedVariable">
|
||||
<input ng-model="myObj.someProperty">
|
||||
<input ng-model="indexedArray[0]">
|
||||
```
|
||||
|
||||
Examples of non-assignable expressions include:
|
||||
|
||||
```
|
||||
<input ng-model="foo + bar">
|
||||
<input ng-model="42">
|
||||
<input ng-model="'oops'">
|
||||
<input ng-model="myFunc()">
|
||||
```
|
||||
|
||||
Always make sure that the expression bound via `ngModel` directive can be assigned to.
|
||||
|
||||
For more information, see the {@link api/ng.directive:ngModel ngModel API doc}.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngOptions:iexp
|
||||
@fullName Invalid Expression
|
||||
@description
|
||||
This error occurs when 'ngOptions' is passed an expression that isn't in an expected form.
|
||||
|
||||
Here's an example of correct syntax:
|
||||
|
||||
```
|
||||
<select ng-model="color" ng-options="c.name for c in colors">
|
||||
```
|
||||
|
||||
For more information on valid expression syntax, see 'ngOptions' in {@link api/ng.directive:select select} directive docs.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngPattern:noregexp
|
||||
@fullName Expected Regular Expression
|
||||
@description
|
||||
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
||||
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngRepeat:dupes
|
||||
@fullName Duplicate Key in Repeater
|
||||
@description
|
||||
|
||||
Occurs if there are duplicate keys in an {@link api/ng.directive:ngRepeat ngRepeat} expression. Duplicate keys are banned because AngularJS uses keys to associate DOM nodes with items.
|
||||
|
||||
By default, collections are keyed by reference which is desirable for most common models but can be problematic for primitive types that are interned (share references).
|
||||
|
||||
For example the issue can be triggered by this *invalid* code:
|
||||
|
||||
```
|
||||
<div ng-repeat="value in [4, 4]"></div>
|
||||
```
|
||||
|
||||
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
|
||||
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||
|
||||
```
|
||||
<div ng-repeat="value in [4, 4] track by $index"></div>
|
||||
```
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngRepeat:iexp
|
||||
@fullName Invalid Expression
|
||||
@description
|
||||
|
||||
Occurs when there is a syntax error in an {@link api/ng.directive:ngRepeat ngRepeat}'s expression. The expression should be in the form '_item_ in _collection_[ track by _id_]'.
|
||||
|
||||
Be aware, the ngRepeat directive parses the expression using a regex before sending _collection_ and optionally _id_ to the AngularJS parser. This error comes from the regex parsing.
|
||||
|
||||
To resolve, identify and fix errors in the expression, paying special attention to the 'in' and 'track by' keywords in the expression.
|
||||
|
||||
Please consult the api documentation of {@link api/ng.directive:ngRepeat ngRepeat} to learn more about valid syntax.
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngRepeat:iidexp
|
||||
@fullName Invalid Identifier
|
||||
@description
|
||||
|
||||
Occurs when there is an error in the identifier part of {@link api/ng.directive:ngRepeat ngRepeat}'s expression.
|
||||
|
||||
To resolve, use either a valid identifier or a tuple (_key_, _value_) where both _key_ and _value_ are valid identifiers.
|
||||
|
||||
Examples of *invalid* syntax:
|
||||
|
||||
```
|
||||
<div ng-repeat="33 in users"></div>
|
||||
<div ng-repeat="someFn() in users"></div>
|
||||
<div ng-repeat="some user in users"></div>
|
||||
```
|
||||
|
||||
Examples of *valid* syntax:
|
||||
|
||||
```
|
||||
<div ng-repeat="user in users"></div>
|
||||
<div ng-repeat="(id, user) in userMap"></div>
|
||||
```
|
||||
|
||||
Please consult the api documentation of {@link api/ng.directive:ngRepeat ngRepeat} to learn more about valid syntax.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
@ngdoc error
|
||||
@name ngTransclude:orphan
|
||||
@fullName Orphan ngTransclude Directive
|
||||
@description
|
||||
|
||||
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
|
||||
|
||||
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
|
||||
|
||||
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
|
||||
|
||||
Consult the API documentation for {@link guide/directive writing directives} to learn more.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecdom
|
||||
@fullName Referencing a DOM node in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access a DOM node.
|
||||
|
||||
AngularJS restricts access to DOM nodes from within expressions since it's a known way to
|
||||
execute arbitrary Javascript code.
|
||||
|
||||
This check is only performed on object index and function calls in Angular expressions. These are
|
||||
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
|
||||
perform this check - it's up to the developer to not expose such sensitive and powerful objects
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid access to DOM nodes.
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecfld
|
||||
@fullName Referencing 'constructor' Field in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access an objects constructor field.
|
||||
|
||||
AngularJS bans constructor access from within expressions since constructor
|
||||
access is a known way to execute arbitrary Javascript code.
|
||||
|
||||
To resolve this error, avoid constructor access. As a last resort, alias
|
||||
the constructor and access it through the alias instead.
|
||||
|
||||
Example expression that would result in this error:
|
||||
|
||||
```
|
||||
<div>{{user.constructor.name}}</div>
|
||||
```
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecfn
|
||||
@fullName Referencing Function Disallowed
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access the 'Function' object (constructor for all functions in JavaScript).
|
||||
|
||||
Angular bans access to Function from within expressions since constructor access is a known way to execute arbitrary Javascript code.
|
||||
|
||||
To resolve this error, avoid Function access.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:isecwindow
|
||||
@fullName Referencing Window object in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access a Window object.
|
||||
|
||||
AngularJS restricts access to the Window object from within expressions since it's a known way to
|
||||
execute arbitrary Javascript code.
|
||||
|
||||
This check is only performed on object index and function calls in Angular expressions. These are
|
||||
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
|
||||
perform this check - it's up to the developer to not expose such sensitive and powerful objects
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid Window access.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:lexerr
|
||||
@fullName Lexer Error
|
||||
@description
|
||||
|
||||
Occurs when an expression has a lexical error, for example a malformed number (0.5e-) or an invalid unicode escape.
|
||||
|
||||
The error message contains a more precise error.
|
||||
|
||||
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:syntax
|
||||
@fullName Syntax Error
|
||||
@description
|
||||
|
||||
Occurs when there is a syntax error in an expression. These errors are thrown while compiling the expression.
|
||||
The error message contains a more precise description of the error, including the location (column) in the expression where the error occurred.
|
||||
|
||||
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $parse:ueoe
|
||||
@fullName Unexpected End of Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression is missing tokens at the end of the expression.
|
||||
For example, forgetting a closing bracket in an expression will trigger this error.
|
||||
|
||||
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $resource:badargs
|
||||
@fullName Too Many Arguments
|
||||
@description
|
||||
|
||||
This error occurs when specifying too many arguments to a {@link api/ngResource.$resource `$resource`} action, such as `get`, `query` or any user-defined custom action.
|
||||
These actions may take up to 4 arguments.
|
||||
|
||||
For more information, refer to the {@link api/ngResource.$resource `$resource`} API reference documentation.
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $resource:badcfg
|
||||
@fullName Response does not match configured parameter
|
||||
@description
|
||||
|
||||
This error occurs when the {@link api/ngResource.$resource `$resource`} service expects a response that can be deserialized as an array, receives an object, or vice versa.
|
||||
By default, all resource actions expect objects, except `query` which expects arrays.
|
||||
|
||||
To resolve this error, make sure your `$resource` configuration matches the actual format of the data returned from the server.
|
||||
|
||||
For more information, see the {@link api/ngResource.$resource `$resource`} API reference documentation.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $resource:badmember
|
||||
@fullName Syntax error in param value using @member lookup
|
||||
@description
|
||||
|
||||
Occurs when there is a syntax error when attempting to extract a param
|
||||
value from the data object.
|
||||
|
||||
Here's an example of valid syntax for `params` or `paramsDefault`:
|
||||
|
||||
````javascript
|
||||
{
|
||||
bar: '@foo.bar'
|
||||
}
|
||||
````
|
||||
|
||||
The part following the `@`, `foo.bar` in this case, should be a simple
|
||||
dotted member lookup using only ASCII identifiers. This error occurs
|
||||
when there is an error in that expression. The following are all syntax
|
||||
errors
|
||||
|
||||
| Value | Error |
|
||||
|---------|----------------|
|
||||
| `@` | Empty expression following `@`. |
|
||||
| `@1.a` | `1` is an invalid javascript identifier. |
|
||||
| `@.a` | Leading `.` is invalid. |
|
||||
| `@a[1]` | Only dotted lookups are supported (no index operator) |
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $resource:badname
|
||||
@fullName Cannot use hasOwnProperty as a parameter name
|
||||
@description
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` as a name of a parameter.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $rootScope:infdig
|
||||
@fullName Infinite $digest Loop
|
||||
@description
|
||||
|
||||
This error occurs when the application's model becomes unstable and each `$digest` cycle triggers a state change and subsequent `$digest` cycle.
|
||||
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 watch on a path and subsequently updating the same path when the value changes.
|
||||
|
||||
```
|
||||
$scope.$watch('foo', function() {
|
||||
$scope.foo = $scope.foo + 1;
|
||||
});
|
||||
```
|
||||
|
||||
The maximum number of allowed iterations of the `$digest` cycle is controlled via TTL setting which can be configured via {@link api/ng.$rootScopeProvider $rootScopeProvider}.
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $rootScope:inprog
|
||||
@fullName Action Already In Progress
|
||||
@description
|
||||
|
||||
At any point in time there can be only one `$digest` or $apply operation in progress.
|
||||
The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call.
|
||||
|
||||
`$digest` or `$apply` are processing operational states of the Scope - data-structure in Angular that provides context for models and enables model mutation observation.
|
||||
|
||||
Trying to reenter a `$digest` or `$apply` while one of them is already in progress is typically a sign of programming error that needs to be fixed.
|
||||
|
||||
This error is often seen when interacting with an API that is sometimes sync and sometimes async.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
function MyController() {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
scope.$apply(function() {
|
||||
scope.someData = someData;
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The controller constructor is always instantiated from within an $apply cycle, so if the third-party component called our callback synchronously, we'd be trying to enter the $apply again.
|
||||
|
||||
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or wrap the call to the api with setTimeout call to make it always asynchronous.
|
||||
|
||||
|
||||
Other situation that leads to this error is when you are trying to reuse a function to by using it as a callback for code that is called by various apis inside and outside of $apply.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
$scope.$apply(function() {
|
||||
// do work here, and update the model
|
||||
};
|
||||
}
|
||||
|
||||
$element.on('click', doSomeWork);
|
||||
doSomeWork(); // << this will throw an exception because templates are compiled within $apply
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
The fix for the example above looks like this:
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
// do work here, and update the model
|
||||
}
|
||||
|
||||
$element.on('click', function() {
|
||||
$scope.$apply(doSomeWork); // <<< the $apply call was moved to the callsite that doesn't execute in $apply call already
|
||||
});
|
||||
|
||||
doSomeWork();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
To learn more about Angular processing model please check out the {@link guide/concepts concepts doc} as well as the {@link api/ng.$rootScope.Scope api} doc.
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@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,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:icontext
|
||||
@fullName Invalid / Unknown SCE context
|
||||
@description
|
||||
|
||||
The context enum passed to {@link api/ng.$sce#methods_trustAs $sce.trustAs} was not recognized.
|
||||
|
||||
Please consult the list of {@link api/ng.$sce#contexts supported Strict Contextual Escaping (SCE) contexts}.
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:iequirks
|
||||
@fullName IE8 in quirks mode is unsupported
|
||||
@description
|
||||
|
||||
This error occurs when you are using AngularJS with {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode enabled (the default) on IE8 or lower in quirks mode.
|
||||
|
||||
In this mode, IE8 allows one to execute arbitrary javascript by the use of the `expression()` syntax and is not supported.
|
||||
Refer {@link http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx MSDN Blogs > IEBlog > Ending Expressions} to learn more about them.
|
||||
|
||||
To resolve this error please specify the proper doctype at the top of your main html document:
|
||||
|
||||
```
|
||||
<!doctype html>
|
||||
```
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:imatcher
|
||||
@fullName Invalid matcher (only string patterns and RegExp instances are supported)
|
||||
@description
|
||||
|
||||
Please see {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
|
||||
$sceDelegateProvider.resourceUrlWhitelist} and {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} for the
|
||||
list of acceptable items.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:insecurl
|
||||
@fullName Processing of a Resource from Untrusted Source Blocked
|
||||
@description
|
||||
|
||||
AngularJS' {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode (enabled by default) has blocked loading a resource from an insecure URL.
|
||||
|
||||
Typically, this would occur if you're attempting to load an Angular template from an untrusted source.
|
||||
It's also possible that a custom directive threw this error for a similar reason.
|
||||
|
||||
Angular only loads templates from trusted URLs (by calling {@link api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl} on the template URL).
|
||||
|
||||
By default, only URLs that belong to the same origin are trusted. These are urls with the same domain and protocol as the application document.
|
||||
|
||||
The {@link api/ng.directive:ngInclude ngInclude} directive and {@link guide/directive directives} that specify a `templateUrl` require a trusted resource URL.
|
||||
|
||||
To load templates from other domains and/or protocols, either adjust the {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist}/ {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
|
||||
api/ng.$sce#methods_trustAsResourceUrl $sce.trustAsResourceUrl}.
|
||||
|
||||
**Note**: The browser's {@link
|
||||
https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest Same Origin
|
||||
Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing (CORS)} policy apply
|
||||
that may further restrict whether the template is successfully loaded. (e.g. neither cross-domain
|
||||
requests won't work on all browsers nor `file://` requests on some browsers)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:itype
|
||||
@fullName String Value is Required for SCE Trust Call
|
||||
@description
|
||||
|
||||
{@link api/ng.$sce#methods_trustAs $sce.trustAs} requires a string value.
|
||||
|
||||
Read more about {@link api/ng.$sce Strict Contextual Escaping (SCE)} in AngularJS.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:iwcard
|
||||
@fullName The sequence *** is not a valid pattern wildcard
|
||||
@description
|
||||
|
||||
The strings in {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
|
||||
$sceDelegateProvider.resourceUrlWhitelist} and {@link
|
||||
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} may not
|
||||
contain the undefined sequence `***`. Only `*` and `**` wildcard patterns are defined.
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
@ngdoc error
|
||||
@name $sce:unsafe
|
||||
@fullName Require a safe/trusted value
|
||||
@description
|
||||
|
||||
The value provided for use in a specific context was not found to be safe/trusted for use.
|
||||
|
||||
Angular's {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode
|
||||
(enabled by default), requires bindings in certain
|
||||
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
|
||||
Angular template from a URL requires that the URL is one considered safe for loading resources.)
|
||||
|
||||
This helps prevent XSS and other security issues. Read more at {@link
|
||||
api/ng.$sce Strict Contextual Escaping (SCE)}
|
||||
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
@ngdoc overview
|
||||
@name Developer Guide: Animations
|
||||
@description
|
||||
|
||||
|
||||
# Animations
|
||||
|
||||
AngularJS 1.2 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
|
||||
or with JavaScript code when it's defined as a factory.
|
||||
|
||||
Animations are not available unless you include the {@link api/ngAnimate `ngAnimate` module} as a dependency within your application.
|
||||
|
||||
Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-init="checked=true">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
||||
</label>
|
||||
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
|
||||
Visible...
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.sample-show-hide {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
transition:all linear 0.5s;
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.sample-show-hide.ng-hide-remove {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add,
|
||||
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
opacity:1;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
## Installation
|
||||
|
||||
See the {@link api/ngAnimate API docs for `ngAnimate`} for instructions on installing the module.
|
||||
|
||||
You may also want to setup a separate CSS file for defining CSS-based animations.
|
||||
|
||||
## How they work
|
||||
|
||||
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within
|
||||
your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so:
|
||||
|
||||
<pre>
|
||||
<div ng-repeat="item in items" class="repeated-item">
|
||||
{{ item.id }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
As you can see, the `.repeated-item` class is present on the element that will be repeated and this class will be
|
||||
used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation.
|
||||
|
||||
As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add
|
||||
a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around
|
||||
it will apply a `ng-move` class name.
|
||||
|
||||
Taking a look at the following CSS code, we can see some transition and keyframe animation code set for each of those events that
|
||||
occur when ngRepeat triggers them:
|
||||
|
||||
<pre>
|
||||
/*
|
||||
We're using CSS transitions for when
|
||||
the enter and move events are triggered
|
||||
for the element that has the .repeated-item
|
||||
class
|
||||
*/
|
||||
.repeated-item.ng-enter, .repeated-item.ng-move {
|
||||
-webkit-transition:0.5s linear all;
|
||||
-moz-transition:0.5s linear all;
|
||||
-o-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
/*
|
||||
The ng-enter-active and ng-move-active
|
||||
are where the transition destination properties
|
||||
are set so that the animation knows what to
|
||||
animate.
|
||||
*/
|
||||
.repeated-item.ng-enter.ng-enter-active,
|
||||
.repeated-item.ng-move.ng-move-active {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
/*
|
||||
We're using CSS keyframe animations for when
|
||||
the leave event is triggered for the element
|
||||
that has the .repeated-item class
|
||||
*/
|
||||
.repeated-item.ng-leave {
|
||||
-webkit-animation:0.5s my_animation;
|
||||
-moz-animation:0.5s my_animation;
|
||||
-o-animation:0.5s my_animation;
|
||||
animation:0.5s my_animation;
|
||||
}
|
||||
|
||||
@keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
/*
|
||||
Unfortunately each browser vendor requires
|
||||
its own definition of keyframe animation code...
|
||||
*/
|
||||
@-webkit-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
@-moz-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
|
||||
@-o-keyframes my_animation {
|
||||
from { opacity:1; }
|
||||
to { opacity:0; }
|
||||
}
|
||||
</pre>
|
||||
|
||||
The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**):
|
||||
|
||||
<pre>
|
||||
myModule.animation('.repeated-item', function() {
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
element.css('opacity',0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
leave : function(element, done) {
|
||||
element.css('opacity', 1);
|
||||
jQuery(element).animate({
|
||||
opacity: 0
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
move : function(element, done) {
|
||||
element.css('opacity', 0);
|
||||
jQuery(element).animate({
|
||||
opacity: 1
|
||||
}, done);
|
||||
|
||||
// optional onDone or onCancel callback
|
||||
// function to handle any post-animation
|
||||
// cleanup operations
|
||||
return function(isCancelled) {
|
||||
if(isCancelled) {
|
||||
jQuery(element).stop();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// you can also capture these animation events
|
||||
addClass : function(element, className, done) {},
|
||||
removeClass : function(element, className, done) {}
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
With these generated CSS class names present on the element at the time, AngularJS automatically
|
||||
figures out whether to perform a CSS and/or JavaScript animation. If both CSS and JavaScript animation
|
||||
code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time.
|
||||
|
||||
## Class and ngClass animation hooks
|
||||
|
||||
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
|
||||
This means that if a CSS class is added to or removed from an element then an animation can be executed in between
|
||||
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
|
||||
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
|
||||
|
||||
The example below shows how to perform animations during class changes:
|
||||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<p>
|
||||
<input type="button" value="set" ng-click="myCssVar='css-class'">
|
||||
<input type="button" value="clear" ng-click="myCssVar=''">
|
||||
<br>
|
||||
<span ng-class="myCssVar">CSS-Animated Text</span>
|
||||
</p>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.css-class-add, .css-class-remove {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
}
|
||||
|
||||
.css-class,
|
||||
.css-class-add.css-class-add-active {
|
||||
color: red;
|
||||
font-size:3em;
|
||||
}
|
||||
|
||||
.css-class-remove.css-class-remove-active {
|
||||
font-size:1.0em;
|
||||
color:black;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Although the CSS is a little different then what we saw before, the idea is the same.
|
||||
|
||||
## Which directives support animations?
|
||||
|
||||
A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during its life cycle.
|
||||
The table below explains in detail which animation events are triggered
|
||||
|
||||
| Directive | Supported Animations |
|
||||
|-------------------------------------------------------------------------------------|------------------------------------------|
|
||||
| {@link api/ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave, and move |
|
||||
| {@link api/ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
|
||||
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
||||
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
||||
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
||||
| {@link api/ng.directive:ngClass#usage_animations ngClass or {{class}}} | add and remove |
|
||||
| {@link api/ng.directive:ngShow#usage_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 api/ngAnimate.$animate API docs}.
|
||||
|
||||
## How do I use animations in my own directives?
|
||||
|
||||
Animations within custom directives can also be established by injecting `$animate` directly into your directive and
|
||||
making calls to it when needed.
|
||||
|
||||
<pre>
|
||||
myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
return function(element, scope, attrs) {
|
||||
element.bind('click', function() {
|
||||
if(element.hasClass('clicked')) {
|
||||
$animate.removeClass(element, 'clicked');
|
||||
} else {
|
||||
$animate.addClass(element, 'clicked');
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link api/ngAnimate.$animate API documentation}.
|
||||
|
||||
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.
|
||||
|
||||
## Angular `<script>` Tag
|
||||
|
||||
# Angular `<script>` Tag
|
||||
|
||||
This example shows the recommended path for integrating Angular with what we call automatic
|
||||
initialization.
|
||||
|
|
@ -37,10 +38,6 @@ initialization.
|
|||
|
||||
<html ng-app>
|
||||
|
||||
* If IE7 support is required add `id="ng-app"`
|
||||
|
||||
<html ng-app id="ng-app">
|
||||
|
||||
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
|
||||
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
|
||||
`ng:`.)
|
||||
|
|
@ -49,9 +46,7 @@ initialization.
|
|||
|
||||
|
||||
|
||||
## Automatic Initialization
|
||||
|
||||
<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png">
|
||||
# Automatic 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
|
||||
|
|
@ -77,14 +72,16 @@ If the {@link api/ng.directive:ngApp `ng-app`} directive is found then Angular w
|
|||
|
||||
|
||||
|
||||
## Manual Initialization
|
||||
# Manual Initialization
|
||||
|
||||
|
||||
If you need to have more control over the initialization process, you can use a manual
|
||||
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
|
||||
or the need to perform an operation before Angular compiles a page.
|
||||
|
||||
Here is an example of manually initializing Angular:
|
||||
|
||||
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
|
||||
api/ng.directive:ngApp ng-app} directive.
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
|
|
@ -94,37 +91,17 @@ Here is an example of manually initializing Angular:
|
|||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.module('myApp', []);
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
angular.bootstrap(document);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
Note that we have provided the name of our application module to be loaded into the injector as the second
|
||||
parameter of the {@link api/angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
|
||||
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
1. After the page and all of the code is loaded, find the root element of your AngularJS
|
||||
application, which is typically the root of the document.
|
||||
1. After the page and all of the code is loaded, find the root of the HTML template, which is
|
||||
typically the root of the document.
|
||||
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
## Deferred Bootstrap
|
||||
|
||||
This feature enables tools like Batarang and test runners to
|
||||
hook into angular's bootstrap process and sneak in more modules
|
||||
into the DI registry which can replace or augment DI services for
|
||||
the purpose of instrumentation or mocking out heavy dependencies.
|
||||
|
||||
If `window.name` contains prefix `NG_DEFER_BOOTSTRAP!` when
|
||||
{@link api/angular.bootstrap} is called, the bootstrap process will be paused
|
||||
until `angular.resumeBootstrap()` is called.
|
||||
|
||||
`angular.resumeBootstrap()` takes an optional array of modules that
|
||||
should be added to the original list of modules that the app was
|
||||
about to be bootstrapped with.
|
||||
|
|
|
|||
|
|
@ -2,44 +2,35 @@
|
|||
@name Developer Guide: HTML Compiler
|
||||
@description
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
|
||||
|
||||
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
|
||||
If you just want to create custom directives, we recommend the {@link guide/directive directives guide}.
|
||||
If you want a deeper look into Angular's compilation process, you're in the right place.
|
||||
</div>
|
||||
|
||||
|
||||
# Overview
|
||||
|
||||
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#methods_directive directives}.
|
||||
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#directive directives}.
|
||||
|
||||
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
browser how the window size needs to be divided in half so that the center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
|
||||
browser how the window size needs to be divided in half so that center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.
|
||||
|
||||
But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
|
||||
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
|
||||
|
||||
Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
expect that you will create directives that are specific to your app. These extensions become a
|
||||
expect that you will create directives that are specific to your app. These extension become a
|
||||
Domain Specific Language for building your application.
|
||||
|
||||
All of this compilation takes place in the web browser; no server side or pre-compilation step is
|
||||
involved.
|
||||
|
||||
|
||||
## Compiler
|
||||
# Compiler
|
||||
|
||||
Compiler is an Angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens in two phases.
|
||||
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
|
||||
process happens into two phases.
|
||||
|
||||
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
|
||||
function.
|
||||
|
|
@ -48,16 +39,17 @@ process happens in two phases.
|
|||
scope model are reflected in the view, and any user interactions with the view are reflected
|
||||
in the scope model. This makes the scope model the single source of truth.
|
||||
|
||||
Some directives such as {@link api/ng.directive:ngRepeat `ng-repeat`} clone DOM elements once
|
||||
for each item in a collection. Having a compile and link phase improves performance since the
|
||||
cloned template only needs to be compiled once, and then linked once for each clone instance.
|
||||
Some directives such {@link api/ng.directive:ngRepeat
|
||||
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
|
||||
improves performance since the cloned template only needs to be compiled once, and then linked
|
||||
once for each clone instance.
|
||||
|
||||
|
||||
## Directive
|
||||
# Directive
|
||||
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered
|
||||
during the compilation process. The directives can be placed in element names, attributes, class
|
||||
names, as well as comments. Here are some equivalent examples of invoking the {@link
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered in
|
||||
the compilation process. The directives can be placed in element names, attributes, class names, as
|
||||
well as comments. Here are some equivalent examples of invoking the {@link
|
||||
api/ng.directive:ngBind `ng-bind`} directive.
|
||||
|
||||
<pre>
|
||||
|
|
@ -68,7 +60,7 @@ api/ng.directive:ngBind `ng-bind`} directive.
|
|||
</pre>
|
||||
|
||||
A directive is just a function which executes when the compiler encounters it in the DOM. See {@link
|
||||
api/ng.$compileProvider#methods_directive directive API} for in-depth documentation on how
|
||||
api/ng.$compileProvider#directive directive API} for in-depth documentation on how
|
||||
to write directives.
|
||||
|
||||
Here is a directive which makes any element draggable. Notice the `draggable` attribute on the
|
||||
|
|
@ -78,21 +70,19 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
|||
<file name="script.js">
|
||||
angular.module('drag', []).
|
||||
directive('draggable', function($document) {
|
||||
var startX=0, startY=0, x = 0, y = 0;
|
||||
return function(scope, element, attr) {
|
||||
var startX = 0, startY = 0, x = 0, y = 0;
|
||||
element.css({
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.on('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
event.preventDefault();
|
||||
element.bind('mousedown', function(event) {
|
||||
startX = event.screenX - x;
|
||||
startY = event.screenY - y;
|
||||
$document.on('mousemove', mousemove);
|
||||
$document.on('mouseup', mouseup);
|
||||
$document.bind('mousemove', mousemove);
|
||||
$document.bind('mouseup', mouseup);
|
||||
});
|
||||
|
||||
function mousemove(event) {
|
||||
|
|
@ -117,265 +107,35 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
|||
</example>
|
||||
|
||||
|
||||
The presence of the `draggable` attribute on any element gives the element new behavior.
|
||||
We extended the vocabulary of the browser in a way which is natural to anyone who is familiar with the principles of HTML.
|
||||
The presence of the `draggable` attribute on any element gives the element new behavior. The beauty of
|
||||
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
|
||||
what the browser understands in a way which is natural to anyone who is familiar with HTML
|
||||
principles.
|
||||
|
||||
|
||||
## Understanding View
|
||||
# Understanding View
|
||||
|
||||
Most other templating systems consume a static string template and
|
||||
There are many templating systems out there. Most of them consume a static string template and
|
||||
combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into
|
||||
an element.
|
||||
|
||||
<img src="img/One_Way_Data_Binding.png">
|
||||
|
||||
This means that any changes to the data need to be re-merged with the template and then
|
||||
`innerHTML`ed into the DOM. Some of the issues with this approach are:
|
||||
`innerHTML`ed into the DOM. Some of the issues with this approach are: reading user input and merging it with data,
|
||||
clobbering user input by overwriting it, managing the whole update process, and lack of behavior
|
||||
expressiveness.
|
||||
|
||||
1. reading user input and merging it with data
|
||||
2. clobbering user input by overwriting it
|
||||
3. managing the whole update process
|
||||
4. lack of behavior expressiveness
|
||||
|
||||
Angular is different. The Angular compiler consumes the DOM, not string templates.
|
||||
Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
|
||||
The result is a linking function, which when combined with a scope model results in a live view. The
|
||||
view and scope model bindings are transparent. The developer does not need to make any special calls to update
|
||||
the view. And because `innerHTML` is not used, you won't accidentally clobber user input.
|
||||
view and scope model bindings are transparent. No action from the developer is needed to update
|
||||
the view. And because no `innerHTML` is used there are no issues of clobbering user input.
|
||||
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
|
||||
well.
|
||||
|
||||
<img src="img/Two_Way_Data_Binding.png">
|
||||
|
||||
The Angular approach produces a stable DOM. The DOM element instance bound to a model
|
||||
The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
|
||||
item instance does not change for the lifetime of the binding. This means that the code can get
|
||||
hold of the elements and register event handlers and know that the reference will not be destroyed
|
||||
by template data merge.
|
||||
|
||||
|
||||
|
||||
## How directives are compiled
|
||||
|
||||
It's important to note that Angular operates on DOM nodes rather than strings. Usually, you don't
|
||||
notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically.
|
||||
|
||||
However it's important to keep this in mind when calling `$compile` yourself, because passing it a string
|
||||
will fail. Instead, use `angular.element` to convert a string to DOM before passing elements into
|
||||
Angular's `$compile` service.
|
||||
|
||||
HTML compilation happens in three phases:
|
||||
|
||||
1. {@link api/ng.$compile `$compile`} traverses the DOM and matches directives.
|
||||
|
||||
If the compiler finds that an element matches a directive, then the directive is added to the list of
|
||||
directives that match the DOM element. A single element may match multiple directives.
|
||||
|
||||
2. Once all directives matching a DOM element have been identified, the compiler sorts the directives
|
||||
by their `priority`.
|
||||
|
||||
Each directive's `compile` functions are executed. Each `compile` function has a chance to
|
||||
modify the DOM. Each `compile` function returns a `link` function. These functions are composed into
|
||||
a "combined" link function, which invokes each directive's returned `link` function.
|
||||
|
||||
3. `$compile` links the template with the scope by calling the combined linking function from the previous step.
|
||||
This in turn will call the linking function of the individual directives, registering listeners on the elements
|
||||
and setting up {@link api/ng.$rootScope.Scope#methods_$watch `$watch`s} with the {@link api/ng.$rootScope.Scope `scope`}
|
||||
as each directive is configured to do.
|
||||
|
||||
The result of this is a live binding between the scope and the DOM. So at this point, a change in
|
||||
a model on the compiled scope will be reflected in the DOM.
|
||||
|
||||
Below is the corresponding code using the `$compile` service.
|
||||
This should help give you an idea of what Angular does internally.
|
||||
|
||||
<pre>
|
||||
var $compile = ...; // injected into your code
|
||||
var scope = ...;
|
||||
var parent = ...; // DOM element where the compiled template can be appended
|
||||
|
||||
var html = '<div ng-bind="exp"></div>';
|
||||
|
||||
// Step 1: parse HTML into DOM element
|
||||
var template = angular.element(html);
|
||||
|
||||
// Step 2: compile the template
|
||||
var linkFn = $compile(template);
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
var element = linkFn(scope);
|
||||
|
||||
// Step 4: Append to DOM (optional)
|
||||
parent.appendChild(element);
|
||||
</pre>
|
||||
|
||||
### The difference between Compile and Link
|
||||
|
||||
At this point you may wonder why the compile process has separate compile and link phases. The
|
||||
short answer is that compile and link separation is needed any time a change in a model causes
|
||||
a change in the **structure** of the DOM.
|
||||
|
||||
It's rare for directives to have a **compile function**, since most directives are concerned with
|
||||
working with a specific DOM element instance rather than changing its overall structure.
|
||||
|
||||
Directives often have a **link function**. A link function allows the directive to register
|
||||
listeners to the specific cloned DOM element instance as well as to copy content into the DOM
|
||||
from the scope.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** Any operation which can be shared among the instance of directives should be
|
||||
moved to the compile function for performance reasons.
|
||||
</div>
|
||||
|
||||
#### An Example of "Compile" Versus "Link"
|
||||
|
||||
To understand, let's look at a real-world example with `ngRepeat`:
|
||||
|
||||
<pre>
|
||||
Hello {{user}}, you have these actions:
|
||||
<ul>
|
||||
<li ng-repeat="action in user.actions">
|
||||
{{action.description}}
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
When the above example is compiled, the compiler visits every node and looks for directives.
|
||||
|
||||
`{{user}}` matches the {@link api/ng.$interpolate interpolation directive}
|
||||
and `ng-repeat` matches the {@link api/ng.directive:ngRepeat `ngRepeat` directive}.
|
||||
|
||||
But {@link api/ng.directive:ngRepeat ngRepeat} has a dilemma.
|
||||
|
||||
It needs to be able to clone new `<li>` elements for every `action` in `user.actions`.
|
||||
This initially seems trivial, but it becomes more complicated when you consider that `user.actions`
|
||||
might have items added to it later. This means that it needs to save a clean copy of the `<li>`
|
||||
element for cloning purposes.
|
||||
|
||||
As new `action`s are inserted, the template `<li>` element needs to be cloned and inserted into `ul`.
|
||||
But cloning the `<li>` element is not enough. It also needs to compile the `<li>` so that its
|
||||
directives, like `{{action.description}}`, evaluate against the right {@link api/ng.$rootScope.Scope scope}.
|
||||
|
||||
|
||||
A naive approach to solving this problem would be to simply insert a copy of the `<li>` element and
|
||||
then compile it.
|
||||
The problem with this approach is that compiling on every `<li>` element that we clone would duplicate
|
||||
a lot of the work. Specifically, we'd be traversing `<li>` each time before cloning it to find the
|
||||
directives. This would cause the compilation process to be slower, in turn making applications
|
||||
less responsive when inserting new nodes.
|
||||
|
||||
The solution is to break the compilation process into two phases:
|
||||
|
||||
the **compile phase** where all of the directives are identified and sorted by priority,
|
||||
and a **linking phase** where any work which "links" a specific instance of the
|
||||
{@link api/ng.$rootScope.Scope scope} and the specific instance of an `<li>` is performed.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** *Link* means setting up listeners on the DOM and setting up `$watch` on the Scope to
|
||||
keep the two in sync.
|
||||
</div>
|
||||
|
||||
{@link api/ng.directive:ngRepeat `ngRepeat`} works by preventing the compilation process from
|
||||
descending into the `<li>` element so it can make a clone of the original and handle inserting
|
||||
and removing DOM nodes itself.
|
||||
|
||||
Instead the {@link api/ng.directive:ngRepeat `ngRepeat`} directive compiles `<li>` separately.
|
||||
The result of the `<li>` element compilation is a linking function which contains all of the
|
||||
directives contained in the `<li>` element, ready to be attached to a specific clone of the `<li>`
|
||||
element.
|
||||
|
||||
At runtime the {@link api/ng.directive:ngRepeat `ngRepeat`} watches the expression and as items
|
||||
are added to the array it clones the `<li>` element, creates a new
|
||||
{@link api/ng.$rootScope.Scope scope} for the cloned `<li>` element and calls the link function
|
||||
on the cloned `<li>`.
|
||||
|
||||
|
||||
|
||||
### Understanding How Scopes Work with Transcluded Directives
|
||||
|
||||
One of the most common use cases for directives is to create reusable components.
|
||||
|
||||
Below is a pseudo code showing how a simplified dialog component may work.
|
||||
|
||||
<pre>
|
||||
<div>
|
||||
<button ng-click="show=true">show</button>
|
||||
|
||||
<dialog title="Hello {{username}}."
|
||||
visible="show"
|
||||
on-cancel="show = false"
|
||||
on-ok="show = false; doSomething()">
|
||||
Body goes here: {{username}} is {{title}}.
|
||||
</dialog>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
Clicking on the "show" button will open the dialog. The dialog will have a title, which is
|
||||
data bound to `username`, and it will also have a body which we would like to transclude
|
||||
into the dialog.
|
||||
|
||||
Here is an example of what the template definition for the `dialog` widget may look like.
|
||||
|
||||
<pre>
|
||||
<div ng-show="visible">
|
||||
<h3>{{title}}</h3>
|
||||
<div class="body" ng-transclude></div>
|
||||
<div class="footer">
|
||||
<button ng-click="onOk()">Save changes</button>
|
||||
<button ng-click="onCancel()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
This will not render properly, unless we do some scope magic.
|
||||
|
||||
The first issue we have to solve is that the dialog box template expects `title` to be defined.
|
||||
But we would like the template's scope property `title` to be the result of interpolating the
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
|
||||
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
<pre>
|
||||
scope: {
|
||||
title: '@', // the title uses the data-binding from the parent scope
|
||||
onOk: '&', // create a delegate onOk function
|
||||
onCancel: '&', // create a delegate onCancel function
|
||||
visible: '=' // set up visible to accept data-binding
|
||||
}
|
||||
</pre>
|
||||
|
||||
Creating local properties on widget scope creates two problems:
|
||||
|
||||
1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
|
||||
template will bind to parent scope property. This is unpredictable and undesirable.
|
||||
|
||||
2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
|
||||
properties which the transclusion needs for data-binding. In our example the `title`
|
||||
property of the widget clobbers the `title` property of the transclusion.
|
||||
|
||||
|
||||
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
|
||||
isolated scope does not prototypically inherit from the parent scope, and therefore we don't have
|
||||
to worry about accidentally clobbering any properties.
|
||||
|
||||
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
|
||||
isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
|
||||
is a child of the original scope, before the widget created an isolated scope for its local
|
||||
variables. This makes the transcluded and widget isolated scope siblings.
|
||||
|
||||
This may seem to be unexpected complexity, but it gives the widget user and developer the least
|
||||
surprise.
|
||||
|
||||
Therefore the final directive definition looks something like this:
|
||||
|
||||
<pre>
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@', // the title uses the data-binding from the parent scope
|
||||
onOk: '&', // create a delegate onOk function
|
||||
onCancel: '&', // create a delegate onCancel function
|
||||
visible: '=' // set up visible to accept data-binding
|
||||
},
|
||||
restrict: 'E',
|
||||
replace: true
|
||||
</pre>
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue