Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b4d1e5e492 | |||
| 60394a9d91 | |||
| f5ddb10b56 | |||
| 6a448d3459 | |||
| 6a8c0f5f4a | |||
| 584308fc06 | |||
| 48ad2c44bf | |||
| 100998330a | |||
| b9e85c62be | |||
| 77fad099d2 | |||
| e1f8a6e82b | |||
| 132d767647 | |||
| 9cde98cbc7 | |||
| 1ddbb3ec3e | |||
| 34f40266b2 | |||
| b8c06e3f1b | |||
| a6cf648b3c | |||
| aebde27f1b | |||
| c9be327d53 | |||
| 3dc7d22d90 | |||
| c10c6cac74 | |||
| cee2c4c569 | |||
| a5160c82dc | |||
| 8853312197 | |||
| 049b24de21 | |||
| a9f987a0c9 | |||
| ad4a20d3d2 | |||
| b3972d1b65 | |||
| c3e0d58b3c | |||
| f08156ea9b | |||
| 4bacf5a5da | |||
| df88873bb7 | |||
| c4b1c5e8f1 | |||
| 9822711ad2 |
+1
-5
@@ -4,8 +4,8 @@ node_js:
|
||||
- '6'
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- bower_components
|
||||
- docs/bower_components
|
||||
|
||||
@@ -36,10 +36,6 @@ addons:
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
before_script:
|
||||
- du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true
|
||||
- ./scripts/travis/before_build.sh
|
||||
|
||||
+37
-2
@@ -1,3 +1,38 @@
|
||||
<a name="1.6.4"></a>
|
||||
# 1.6.4 phenomenal-footnote (2017-03-31)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$parse:**
|
||||
- standardize one-time literal vs non-literal and interceptors
|
||||
([60394a](https://github.com/angular/angular.js/commit/60394a9d91dad8932fa900af7c8529837f1d4557),
|
||||
[#15858](https://github.com/angular/angular.js/issues/15858))
|
||||
- fix infinite digest errors when watching objects with .valueOf in literals
|
||||
([f5ddb1](https://github.com/angular/angular.js/commit/f5ddb10b56676c2ad912ce453acb87f0a7a94e01),
|
||||
[#15867](https://github.com/angular/angular.js/issues/15867))
|
||||
- **ngModel:** prevent internal scope reference from being copied
|
||||
([e1f8a6](https://github.com/angular/angular.js/commit/e1f8a6e82bb8a70079ef3db9a891b1c08b5bae31),
|
||||
[#15833](https://github.com/angular/angular.js/issues/15833))
|
||||
- **jqLite:** make jqLite invoke jqLite.cleanData as a method
|
||||
([9cde98](https://github.com/angular/angular.js/commit/9cde98cbc770f8d33fc074ba563b7ab6e2baaf8b),
|
||||
[#15846](https://github.com/angular/angular.js/issues/15846))
|
||||
- **$http:** throw more informative error on invalid JSON response
|
||||
([df8887](https://github.com/angular/angular.js/commit/df88873bb79213057057adb47151b626a7ec0e5d),
|
||||
[#15695](https://github.com/angular/angular.js/issues/15695),
|
||||
[#15724](https://github.com/angular/angular.js/issues/15724))
|
||||
- **dateFilter:** correctly handle newlines in `format` string
|
||||
([982271](https://github.com/angular/angular.js/commit/9822711ad2a401c2449239edc13d18b301714757),
|
||||
[#15794](https://github.com/angular/angular.js/issues/15794),
|
||||
[#15792](https://github.com/angular/angular.js/issues/15792))
|
||||
|
||||
|
||||
## New Features
|
||||
- **$resource:** add `hasBody` action configuration option
|
||||
([a9f987](https://github.com/angular/angular.js/commit/a9f987a0c9653246ea471a89197907d94c0cea2a),
|
||||
[#10128](https://github.com/angular/angular.js/issues/10128),
|
||||
[#12181](https://github.com/angular/angular.js/issues/12181))
|
||||
|
||||
|
||||
<a name="1.6.3"></a>
|
||||
# 1.6.3 scriptalicious-bootstrapping (2017-03-08)
|
||||
|
||||
@@ -1156,7 +1191,7 @@ You configure this list in a module configuration block:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
@@ -2567,7 +2602,7 @@ You configure this list in a module configuration block:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "3.1.0",
|
||||
"jquery": "3.2.1",
|
||||
"jquery-2.2": "jquery#2.2.4",
|
||||
"jquery-2.1": "jquery#2.1.4",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $animate:nocb
|
||||
@fullName Do not pass a callback to animate methods
|
||||
@description
|
||||
|
||||
Since Angular 1.3, the methods of {@link ng.$animate} do not accept a callback as the last parameter.
|
||||
Instead, they return a promise to which you can attach `then` handlers to be run when the animation completes.
|
||||
|
||||
If you are getting this error then you need to update your code to use the promise-based API.
|
||||
|
||||
See https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9 for information about
|
||||
the change to the animation API and the changes you need to make.
|
||||
@@ -0,0 +1,38 @@
|
||||
@ngdoc error
|
||||
@name $compile:noslot
|
||||
@fullName No matching slot in parent directive
|
||||
@description
|
||||
|
||||
This error occurs when declaring a specific slot in a {@link ng.ngTransclude `ngTransclude`}
|
||||
which does not map to a specific slot defined in the transclude property of the directive.
|
||||
|
||||
In this example the template has declared a slot missing from the transclude definition.
|
||||
This example will generate a noslot error.
|
||||
```js
|
||||
var componentConfig = {
|
||||
template: '<div>' +
|
||||
'<div ng-transclude="slotProvided"></div>' +
|
||||
'<div ng-transclude="noSlotProvided"></div>' +
|
||||
'</div>',
|
||||
transclude: {
|
||||
// The key value pairs here are considered "slots" that are provided for components to slot into.
|
||||
slotProvided: 'slottedComponent', // mandatory transclusion
|
||||
// There is no slot provided here for the transclude 'noSlotProvided' declared in the above template.
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If we make the following change we will no longer get the noslot error.
|
||||
```js
|
||||
var componentConfig = {
|
||||
template: '<div>' +
|
||||
'<div ng-transclude="slotProvided"></div>' +
|
||||
'<div ng-transclude="noSlotProvided"></div>' +
|
||||
'</div>',
|
||||
transclude: {
|
||||
slotProvided: 'slottedComponent',
|
||||
noSlotProvided: 'otherComponent' // now it is declared and the error should cease
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
@ngdoc error
|
||||
@name $http:baddata
|
||||
@fullName Bad JSON Data
|
||||
@description
|
||||
|
||||
The default @{link ng.$http#default-transformations `transformResponse`} will try to parse the
|
||||
response as JSON if the `Content-Type` header is `application/json` or the response looks like a
|
||||
valid JSON-stringified object or array.
|
||||
This error occurs when that data is not a valid JSON object.
|
||||
|
||||
The error message should provide additional context such as the actual response.
|
||||
|
||||
To resolve this error, make sure you pass valid JSON data to `transformResponse` or use an
|
||||
appropriate `Content-Type` header for non-JSON data.
|
||||
@@ -5,19 +5,18 @@
|
||||
|
||||
# Component Router
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
<div class="alert alert-danger">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in the new Angular, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the new Angular Router to AngularJS, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
</div>
|
||||
|
||||
This guide describes the new Component Router for AngularJS 1.5.
|
||||
|
||||
<div class="alert alert-info">
|
||||
If you are looking for information about the old router for AngularJS 1.4 and
|
||||
earlier have a look at the {@link ngRoute} module.
|
||||
If you are looking for information about the default router for AngularJS have a look at the {@link ngRoute} module.
|
||||
|
||||
If you are looking for information about the Component Router for Angular 2 then
|
||||
check out the [Angular 2 Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
|
||||
If you are looking for information about the Component Router for the new Angular then
|
||||
check out the [Angular Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -588,7 +588,7 @@ trust a URL:
|
||||
|
||||
```js
|
||||
appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhiteList([
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
// Allow same origin resource loads.
|
||||
'self',
|
||||
// Allow JSONP calls that match this pattern
|
||||
|
||||
+93
-43
@@ -22,39 +22,89 @@ So it's definitely not a plugin or some other native browser extension.
|
||||
|
||||
### What is the AngularJS versioning strategy?
|
||||
|
||||
In Angular 1 we do not allow intentional breaking changes to appear in versions where only the "patch"
|
||||
In AngularJS we do not allow intentional breaking changes to appear in versions where only the "patch"
|
||||
number changes. For example between 1.3.12 and 1.3.13 there can be no breaking changes. We do allow
|
||||
breaking changes happen between "minor" number changes. For example between 1.3.15 and 1.4.0 there
|
||||
will be a number of breaking changes. We also allow breaking changes between beta releases of Angular.
|
||||
are a number of breaking changes. That means AngularJS does not follow
|
||||
[semantic versioning (semver)](http://semver.org/) where breaking changes are only
|
||||
allowed when the "major" version changes.
|
||||
|
||||
We also allow breaking changes between beta releases of AngularJS.
|
||||
For example between 1.4.0-beta.4 and 1.4.0-beta.5 there may be breaking changes. We try hard to minimize
|
||||
these kinds of change only to those where there is a strong use case such as a strongly requested feature
|
||||
improvement, a considerable simplification of the code or a measurable performance improvement.
|
||||
improvement, a considerable simplification of the code, a measurable performance improvement, or a better
|
||||
developer experience (especially with regard to upgrading to Angular).
|
||||
|
||||
When adding new code to branches of Angular, have a very stringent commit policy:
|
||||
When we are making a release we generate updates to the changelog directly from the commits. This
|
||||
generated update contains a highlighted section that contains all the breaking changes that have been
|
||||
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
|
||||
changes and so can application developers when they are deciding whether to update to a new version of
|
||||
Angular.
|
||||
|
||||
- Every commit must contain tests and documentation updates alongside the code changes and that all the
|
||||
tests must pass;
|
||||
Features with non-breaking changes can also appear in the "patch" version, e.g. in version 1.6.3 there might
|
||||
be a feature that is not available in 1.6.2.
|
||||
|
||||
Finally, deprecation of features might also appear in "minor" version updates. That means the features
|
||||
will still work in this version, but sometimes must be activated specifically.
|
||||
|
||||
#### When are deprecated features removed from the library?
|
||||
|
||||
Most of the time we remove a deprecated feature in a next minor version bump. For example, the
|
||||
`preAssignBindingsEnabled` `$compileProvider` method was defined in AngularJS `1.5.10`, deprecated in `1.6` and
|
||||
will be removed in `1.7`.
|
||||
|
||||
In case of jqLite we apply a different strategy - we deprecate features that have an equivalent in jQuery that
|
||||
is also deprecated but we only remove the feature once it's removed from jQuery to improve compatibility between
|
||||
jqLite and jQuery. One such example is the `bind` method, deprecated in favor of `on` but unlikely to be removed
|
||||
from jqLite any time soon.
|
||||
|
||||
#### What is the version compatibility between AngularJS main and optional modules?
|
||||
|
||||
AngularJS code is separated into a main module ("angular"), and a few different optional modules
|
||||
("angular-animate", "angular-route" etc) that are dependant on the main module.
|
||||
When a new AngularJS version is released, all modules are updated to the new version.
|
||||
This means that the main module and the optional modules must always have the exact same version,
|
||||
down to the patch number, otherwise your application might break.
|
||||
|
||||
Therefore you must always explicitly lock down your dependencies, for example in the package.json,
|
||||
the following means that "angular" and "angular-animate" are always updated to the same version:
|
||||
|
||||
```
|
||||
{
|
||||
"angular": "~1.6.0",
|
||||
"angular-animate": "~1.6.0"
|
||||
}
|
||||
```
|
||||
|
||||
If you define exact versions, make sure core and optional modules are the same:
|
||||
|
||||
```
|
||||
{
|
||||
"angular": "1.6.3",
|
||||
"angular-animate": "1.6.3"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### How does AngularJS ensure code quality and guard against regressions?
|
||||
|
||||
When adding new code to AngularJS, we have a very stringent commit policy:
|
||||
|
||||
- Every commit must pass all existing tests, contain tests for code changes, and update the documentation
|
||||
- Commit messages must be written in a specific manner that allows us to parse them and extract the changes
|
||||
for release notes.
|
||||
for release notes ([see the contributing guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md))
|
||||
|
||||
The Angular code base has a very large set of unit tests (over 4000) and end to end tests, which are pretty
|
||||
comprehensive. This means that a breaking change will require one or more tests to be changed to allow the
|
||||
The AngularJS code base has a very large set of unit tests and end-to-end tests. This means that a breaking change will require one or more tests to be changed to allow the
|
||||
tests to pass. So when a commit includes tests that are being removed or modified, this is a flag that the
|
||||
code might include a breaking change. When reviewing the commit we can then decide whether there really is
|
||||
a breaking change and if it is appropriate for the branch to which it is being merged. If so, then we
|
||||
require that the commit message contains an appropriate breaking change message.
|
||||
|
||||
Additionally, when a commit lands in our master repository it is synced to Google where we test it against
|
||||
over 2000 applications using the test suites of these applications. This allows us to catch regressions
|
||||
Additionally, commits are periodically synced to Google where we test it against applications using
|
||||
the test suites of these applications. This allows us to catch regressions
|
||||
quickly before a release. We've had a pretty good experience with this setup. Only bugs that affect features
|
||||
not used at Google or without sufficient test coverage, have a chance of making it through.
|
||||
|
||||
Lastly, when we are making a release we generate updates to the changelog directly from the commits. This
|
||||
generated update contains a highlighted section that contains all the breaking changes that have been
|
||||
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
|
||||
changes and so can application developers when they are deciding whether to update to a new version of
|
||||
Angular.
|
||||
|
||||
|
||||
### Is AngularJS a templating system?
|
||||
|
||||
@@ -86,11 +136,11 @@ Yes. See instructions in {@link downloading}.
|
||||
|
||||
|
||||
|
||||
### What browsers does Angular work with?
|
||||
### What browsers does AngularJS work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: the latest versions of Chrome,
|
||||
Firefox, Safari, and Safari for iOs, as well as Internet Explorer versions 9-11. See {@link guide/ie
|
||||
Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
|
||||
Firefox, Safari, and Safari for iOS, as well as Internet Explorer versions 9-11. See
|
||||
{@link guide/ie Internet Explorer Compatibility} for more details on supporting legacy IE browsers.
|
||||
|
||||
If a browser is untested, it doesn't mean it won't work; for example, older Android (2.3.x)
|
||||
is supported in the sense that we avoid the dot notation for reserved words as property names,
|
||||
@@ -99,7 +149,7 @@ a large part of their codebase with a browser we test, such as Opera > version 1
|
||||
(uses the Blink engine), or the various Firefox derivatives.
|
||||
|
||||
|
||||
### What's Angular's performance like?
|
||||
### What's AngularJS's performance like?
|
||||
|
||||
The startup time heavily depends on your network connection, state of the cache, browser used and
|
||||
available hardware, but typically we measure bootstrap time in tens or hundreds of milliseconds.
|
||||
@@ -114,40 +164,40 @@ illustration, we typically build snappy apps with hundreds or thousands of activ
|
||||
The size of the file is ~50KB compressed and minified.
|
||||
|
||||
|
||||
### Can I use the open-source Closure Library with Angular?
|
||||
### Can I use the open-source Closure Library with AngularJS?
|
||||
|
||||
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
|
||||
in Angular.
|
||||
in AngularJS.
|
||||
|
||||
|
||||
### Does Angular use the jQuery library?
|
||||
### Does AngularJS use the jQuery library?
|
||||
|
||||
Yes, Angular can use [jQuery](http://jquery.com/) if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
|
||||
Yes, AngularJS can use [jQuery](http://jquery.com/) if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, AngularJS falls back
|
||||
to its own implementation of the subset of jQuery that we call {@link angular.element jQLite}.
|
||||
|
||||
Angular 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with Angular
|
||||
AngularJS 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with AngularJS
|
||||
but we don't guarantee that.
|
||||
|
||||
|
||||
### What is testability like in Angular?
|
||||
### What is testability like in AngularJS?
|
||||
|
||||
Very testable and designed this way from the ground up. It has an integrated dependency injection
|
||||
framework, provides mocks for many heavy dependencies (server-side communication). See
|
||||
{@link ngMock} for details.
|
||||
|
||||
|
||||
### How can I learn more about Angular?
|
||||
### How can I learn more about AngularJS?
|
||||
|
||||
Watch the July 17, 2012 talk
|
||||
"[AngularJS Intro + Dependency Injection](http://www.youtube.com/watch?v=1CpiB3Wk25U)".
|
||||
|
||||
|
||||
### How is Angular licensed?
|
||||
### How is AngularJS licensed?
|
||||
|
||||
The [MIT License](https://github.com/angular/angular.js/blob/master/LICENSE).
|
||||
|
||||
### Can I download and use the Angular logo artwork?
|
||||
### Can I download and use the AngularJS logo artwork?
|
||||
|
||||
Yes! You can find design files in our github repository, under "[angular.js/images/logo](https://github.com/angular/angular.js/tree/master/images/logo)"
|
||||
The logo design is licensed under a "[Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)". If you have some other use in mind, contact us.
|
||||
@@ -168,7 +218,7 @@ For a smaller order, or for other countries, we suggest downloading the logo art
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
The Angular support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of Angular fall into.
|
||||
The AngularJS support channel (#angularjs on Freenode) sees a number of recurring pitfalls that new users of AngularJS fall into.
|
||||
This document aims to point them out before you discover them the hard way.
|
||||
|
||||
### DOM Manipulation
|
||||
@@ -179,13 +229,13 @@ Use built-in directives, or write your own where necessary, to do your DOM manip
|
||||
See below about duplicating functionality.
|
||||
|
||||
If you're struggling to break the habit, consider removing jQuery from your app.
|
||||
Really. Angular has the $http service and powerful directives that make it almost always unnecessary.
|
||||
Angular's bundled jQLite has a handful of the features most commonly used in writing Angular directives, especially binding to events.
|
||||
Really. AngularJS has the $http service and powerful directives that make it almost always unnecessary.
|
||||
AngularJS's bundled jQLite has a handful of the features most commonly used in writing AngularJS directives, especially binding to events.
|
||||
|
||||
### Trying to duplicate functionality that already exists
|
||||
|
||||
There's a good chance that your app isn't the first to require certain functionality.
|
||||
There are a few pieces of Angular that are particularly likely to be reimplemented out of old habits.
|
||||
There are a few pieces of AngularJS that are particularly likely to be reimplemented out of old habits.
|
||||
|
||||
**ng-repeat**
|
||||
|
||||
@@ -198,7 +248,7 @@ Store the data from the server in an array on your `$scope`, and bind it to the
|
||||
**ng-show**
|
||||
|
||||
`ng-show` gets this frequently too.
|
||||
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but Angular has a better way.
|
||||
Conditionally showing and hiding things using jQuery is a common pattern in other apps, but AngularJS has a better way.
|
||||
`ng-show` (and `ng-hide`) conditionally show and hide elements based on boolean expressions.
|
||||
Describe the conditions for showing and hiding an element in terms of `$scope` variables:
|
||||
|
||||
@@ -211,7 +261,7 @@ Note especially the powerful `ng-switch` that should be used instead of several
|
||||
|
||||
`ng-class` is the last of the big three.
|
||||
Conditionally applying classes to elements is another thing commonly done manually using jQuery.
|
||||
Angular, of course, has a better way.
|
||||
AngularJS, of course, has a better way.
|
||||
You can give `ng-class` a whitespace-separated set of class names, and then it's identical to ordinary `class`.
|
||||
That's not very exciting, so there's a second syntax:
|
||||
|
||||
@@ -225,22 +275,22 @@ Note also the handy `ng-class-even` and `ng-class-odd`, and the related though s
|
||||
|
||||
### `$watch` and `$apply`
|
||||
|
||||
Angular's two-way data binding is the root of all awesome in Angular.
|
||||
AngularJS's two-way data binding is the root of all awesome in AngularJS.
|
||||
However, it's not magic, and there are some situations where you need to give it a nudge in the right direction.
|
||||
|
||||
When you bind a value to an element in Angular using `ng-model`, `ng-repeat`, etc., Angular creates a `$watch` on that value.
|
||||
When you bind a value to an element in AngularJS using `ng-model`, `ng-repeat`, etc., AngularJS creates a `$watch` on that value.
|
||||
Then whenever a value on a scope changes, all `$watch`es observing that element are executed, and everything updates.
|
||||
|
||||
Sometimes, usually when you're writing a custom directive, you will have to define your own `$watch` on a scope value to make the directive react to changes.
|
||||
|
||||
On the flip side, sometimes you change a scope value in some code, but the app doesn't react to it.
|
||||
Angular checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, Angular will check for changes and react.
|
||||
However, some code is outside of Angular and you'll have to call `scope.$apply()` yourself to trigger the update.
|
||||
AngularJS checks for scope variable changes after pieces of your code have finished running; for example, when `ng-click` calls a function on your scope, AngularJS will check for changes and react.
|
||||
However, some code is outside of AngularJS and you'll have to call `scope.$apply()` yourself to trigger the update.
|
||||
This is most commonly seen in event handlers in custom directives.
|
||||
|
||||
### Combining `ng-repeat` with other directives
|
||||
|
||||
`ng-repeat` is extremely useful, one of the most powerful directives in Angular.
|
||||
`ng-repeat` is extremely useful, one of the most powerful directives in AngularJS.
|
||||
However the transformation it applies to the DOM is substantial.
|
||||
Therefore applying other directives (such as `ng-show`, `ng-controller` and others) to the same element as `ng-repeat` generally leads to problems.
|
||||
|
||||
@@ -249,7 +299,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
|
||||
|
||||
### `$rootScope` exists, but it can be used for evil
|
||||
|
||||
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Scopes in AngularJS form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
|
||||
|
||||
Occasionally there are pieces of data that you want to make global to the whole app.
|
||||
|
||||
@@ -41,7 +41,7 @@ maintain. As we add more and more features, our files will get bigger and bigger
|
||||
difficult to navigate and find the code we are looking for.
|
||||
|
||||
Instead we should put each feature/entity in its own file. Each stand-alone controller will be
|
||||
defined in its own file, each component will be defined in each own file, etc.
|
||||
defined in its own file, each component will be defined in its own file, etc.
|
||||
|
||||
Luckily, we don't need to change anything with respect to that guideline in our code, since we have
|
||||
already defined our `phoneList` component in its own `phone-list.component.js` file. Good job!
|
||||
|
||||
@@ -402,7 +402,7 @@ You can now rerun `npm run protractor` to see the tests run (and hopefully pass)
|
||||
|
||||
<div></div>
|
||||
|
||||
* Try to add a `{{$ctrl.phoneId}` binding in the template string for the phone details view:
|
||||
* Try to add a `{{$ctrl.phoneId}}` binding in the template string for the phone details view:
|
||||
|
||||
```js
|
||||
when('/phones/:phoneId', {
|
||||
|
||||
@@ -46,14 +46,14 @@ Since we are using [Bower][bower] to install client-side dependencies, this step
|
||||
"angular-resource": "1.5.x",
|
||||
"angular-route": "1.5.x",
|
||||
"bootstrap": "3.3.x",
|
||||
"jquery": "2.2.x"
|
||||
"jquery": "3.2.x"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `"angular-animate": "1.5.x"` tells bower to install a version of the angular-animate module that
|
||||
is compatible with version 1.5.x of Angular.
|
||||
* `"jquery": "2.2.x"` tells bower to install the latest patch release of the 2.2 version of jQuery.
|
||||
* `"jquery": "3.2.x"` tells bower to install the latest patch release of the 3.2 version of jQuery.
|
||||
Note that this is not an Angular library; it is the standard jQuery library. We can use bower to
|
||||
install a wide range of 3rd party libraries.
|
||||
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"yarn": ">=0.17.9",
|
||||
"yarn": ">=0.21.3",
|
||||
"grunt": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -59,7 +59,7 @@
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "^2.2.0",
|
||||
"jquery": "^3.1.1",
|
||||
"jquery": "^3.2.1",
|
||||
"karma": "^1.1.2",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
|
||||
@@ -8,7 +8,7 @@ nvm install
|
||||
|
||||
# clean out and install yarn
|
||||
rm -rf ~/.yarn
|
||||
curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.21.3
|
||||
export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
# Ensure that we have the local dependencies installed
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"arrayRemove": false,
|
||||
"copy": false,
|
||||
"shallowCopy": false,
|
||||
"simpleCompare": false,
|
||||
"equals": false,
|
||||
"csp": false,
|
||||
"concat": false,
|
||||
|
||||
+6
-1
@@ -62,6 +62,7 @@
|
||||
includes,
|
||||
arrayRemove,
|
||||
copy,
|
||||
simpleCompare,
|
||||
equals,
|
||||
csp,
|
||||
jq,
|
||||
@@ -1040,6 +1041,10 @@ function copy(source, destination, maxDepth) {
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line no-self-compare
|
||||
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.equals
|
||||
@@ -1120,7 +1125,7 @@ function equals(o1, o2) {
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
return simpleCompare(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
if (!isRegExp(o2)) return false;
|
||||
return o1.toString() === o2.toString();
|
||||
|
||||
+7
-12
@@ -201,12 +201,6 @@ function jqLiteHasData(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
@@ -309,13 +303,10 @@ function jqLiteClone(element) {
|
||||
}
|
||||
|
||||
function jqLiteDealoc(element, onlyDescendants) {
|
||||
if (!onlyDescendants) jqLiteRemoveData(element);
|
||||
if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]);
|
||||
|
||||
if (element.querySelectorAll) {
|
||||
var descendants = element.querySelectorAll('*');
|
||||
for (var i = 0, l = descendants.length; i < l; i++) {
|
||||
jqLiteRemoveData(descendants[i]);
|
||||
}
|
||||
jqLite.cleanData(element.querySelectorAll('*'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +604,11 @@ forEach({
|
||||
data: jqLiteData,
|
||||
removeData: jqLiteRemoveData,
|
||||
hasData: jqLiteHasData,
|
||||
cleanData: jqLiteCleanData
|
||||
cleanData: function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
}, function(fn, name) {
|
||||
JQLite[name] = fn;
|
||||
});
|
||||
|
||||
+13
-9
@@ -375,9 +375,9 @@
|
||||
* initialized.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Deprecation warning:** although bindings for non-ES6 class controllers are currently
|
||||
* bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
|
||||
* code that relies upon bindings inside a `$onInit` method on the controller, instead.
|
||||
* **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class
|
||||
* controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please
|
||||
* place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead.
|
||||
* </div>
|
||||
*
|
||||
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
|
||||
@@ -1390,7 +1390,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
*
|
||||
* If disabled (false), the compiler calls the constructor first before assigning bindings.
|
||||
*
|
||||
* The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
|
||||
* The default value is false.
|
||||
*
|
||||
* @deprecated
|
||||
* sinceVersion="1.6.0"
|
||||
* removeVersion="1.7.0"
|
||||
*
|
||||
* This method and the option to assign the bindings before calling the controller's constructor
|
||||
* will be removed in v1.7.0.
|
||||
*/
|
||||
var preAssignBindingsEnabled = false;
|
||||
this.preAssignBindingsEnabled = function(enabled) {
|
||||
@@ -3480,8 +3487,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
// eslint-disable-next-line no-self-compare
|
||||
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
|
||||
compare = simpleCompare;
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -3556,9 +3562,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
});
|
||||
|
||||
function recordChanges(key, currentValue, previousValue) {
|
||||
if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
|
||||
// eslint-disable-next-line no-self-compare
|
||||
(currentValue === currentValue || previousValue === previousValue)) {
|
||||
if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) {
|
||||
// If we have not already scheduled the top level onChangesQueue handler then do so now
|
||||
if (!onChangesQueue) {
|
||||
scope.$$postDigest(flushOnChangesQueue);
|
||||
|
||||
@@ -14,13 +14,6 @@ function classDirective(name, selector) {
|
||||
return {
|
||||
restrict: 'AC',
|
||||
link: function(scope, element, attr) {
|
||||
var expression = attr[name].trim();
|
||||
var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
|
||||
|
||||
var watchInterceptor = isOneTime ? toFlatValue : toClassString;
|
||||
var watchExpression = $parse(expression, watchInterceptor);
|
||||
var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
|
||||
|
||||
var classCounts = element.data('$classCounts');
|
||||
var oldModulo = true;
|
||||
var oldClassString;
|
||||
@@ -43,7 +36,7 @@ function classDirective(name, selector) {
|
||||
scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
|
||||
}
|
||||
|
||||
scope.$watch(watchExpression, watchAction, isOneTime);
|
||||
scope.$watch($parse(attr[name], toClassString), ngClassWatchAction);
|
||||
|
||||
function addClasses(classString) {
|
||||
classString = digestClassCounts(split(classString), 1);
|
||||
@@ -85,9 +78,9 @@ function classDirective(name, selector) {
|
||||
}
|
||||
|
||||
function ngClassIndexWatchAction(newModulo) {
|
||||
// This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
|
||||
// This watch-action should run before the `ngClassWatchAction()`, thus it
|
||||
// adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
|
||||
// `ngClass[OneTime]WatchAction()` will update the classes.
|
||||
// `ngClassWatchAction()` will update the classes.
|
||||
if (newModulo === selector) {
|
||||
addClasses(oldClassString);
|
||||
} else {
|
||||
@@ -97,15 +90,13 @@ function classDirective(name, selector) {
|
||||
oldModulo = newModulo;
|
||||
}
|
||||
|
||||
function ngClassOneTimeWatchAction(newClassValue) {
|
||||
var newClassString = toClassString(newClassValue);
|
||||
|
||||
if (newClassString !== oldClassString) {
|
||||
ngClassWatchAction(newClassString);
|
||||
}
|
||||
}
|
||||
|
||||
function ngClassWatchAction(newClassString) {
|
||||
// When using a one-time binding the newClassString will return
|
||||
// the pre-interceptor value until the one-time is complete
|
||||
if (!isString(newClassString)) {
|
||||
newClassString = toClassString(newClassString);
|
||||
}
|
||||
|
||||
if (oldModulo === selector) {
|
||||
updateClasses(oldClassString, newClassString);
|
||||
}
|
||||
@@ -152,34 +143,6 @@ function classDirective(name, selector) {
|
||||
|
||||
return classString;
|
||||
}
|
||||
|
||||
function toFlatValue(classValue) {
|
||||
var flatValue = classValue;
|
||||
|
||||
if (isArray(classValue)) {
|
||||
flatValue = classValue.map(toFlatValue);
|
||||
} else if (isObject(classValue)) {
|
||||
var hasUndefined = false;
|
||||
|
||||
flatValue = Object.keys(classValue).filter(function(key) {
|
||||
var value = classValue[key];
|
||||
|
||||
if (!hasUndefined && isUndefined(value)) {
|
||||
hasUndefined = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (hasUndefined) {
|
||||
// Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
|
||||
// the watcher, by including at least one `undefined` value.
|
||||
flatValue.push(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
return flatValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -281,7 +281,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
|
||||
|
||||
this.$$currentValidationRunId = 0;
|
||||
|
||||
this.$$scope = $scope;
|
||||
// https://github.com/angular/angular.js/issues/15833
|
||||
// Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched
|
||||
Object.defineProperty(this, '$$scope', {value: $scope});
|
||||
this.$$attr = $attr;
|
||||
this.$$element = $element;
|
||||
this.$$animate = $animate;
|
||||
@@ -890,8 +892,8 @@ function setupModelWatcher(ctrl) {
|
||||
// -> scope value did not change since the last digest as
|
||||
// ng-change executes in apply phase
|
||||
// 4. view should be changed back to 'a'
|
||||
ctrl.$$scope.$watch(function ngModelWatch() {
|
||||
var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
|
||||
ctrl.$$scope.$watch(function ngModelWatch(scope) {
|
||||
var modelValue = ctrl.$$ngModelGet(scope);
|
||||
|
||||
// if scope model value and ngModel value are out of sync
|
||||
// TODO(perf): why not move this to the action fn?
|
||||
|
||||
@@ -45,8 +45,9 @@
|
||||
* The final result is an array of those elements that the predicate returned true for.
|
||||
*
|
||||
* @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
|
||||
* determining if the expected value (from the filter expression) and actual value (from
|
||||
* the object in the array) should be considered a match.
|
||||
* determining if values retrieved using `expression` (when it is not a function) should be
|
||||
* considered a match based on the the expected value (from the filter expression) and actual
|
||||
* value (from the object in the array).
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
|
||||
@@ -475,7 +475,7 @@ var DATE_FORMATS = {
|
||||
GGGG: longEraGetter
|
||||
};
|
||||
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,
|
||||
NUMBER_STRING = /^-?\d+$/;
|
||||
|
||||
/**
|
||||
@@ -534,6 +534,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+
|
||||
* `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* Any other characters in the `format` string will be output as-is.
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
|
||||
+6
-1
@@ -138,7 +138,12 @@ function defaultHttpResponseTransform(data, headers) {
|
||||
if (tempData) {
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
|
||||
data = fromJson(tempData);
|
||||
try {
|
||||
data = fromJson(tempData);
|
||||
} catch (e) {
|
||||
throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
|
||||
'Parse error: "{1}"', data, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ function $IntervalProvider() {
|
||||
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
||||
* @param {...*=} Pass additional parameters to the executed function.
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
* @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete.
|
||||
*
|
||||
* @example
|
||||
* <example module="intervalExample" name="interval-service">
|
||||
|
||||
+43
-63
@@ -769,15 +769,13 @@ function isConstant(ast) {
|
||||
return ast.constant;
|
||||
}
|
||||
|
||||
function ASTCompiler(astBuilder, $filter) {
|
||||
this.astBuilder = astBuilder;
|
||||
function ASTCompiler($filter) {
|
||||
this.$filter = $filter;
|
||||
}
|
||||
|
||||
ASTCompiler.prototype = {
|
||||
compile: function(expression) {
|
||||
compile: function(ast) {
|
||||
var self = this;
|
||||
var ast = this.astBuilder.ast(expression);
|
||||
this.state = {
|
||||
nextId: 0,
|
||||
filters: {},
|
||||
@@ -832,8 +830,6 @@ ASTCompiler.prototype = {
|
||||
ifDefined,
|
||||
plusFn);
|
||||
this.state = this.stage = undefined;
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -1236,15 +1232,13 @@ ASTCompiler.prototype = {
|
||||
};
|
||||
|
||||
|
||||
function ASTInterpreter(astBuilder, $filter) {
|
||||
this.astBuilder = astBuilder;
|
||||
function ASTInterpreter($filter) {
|
||||
this.$filter = $filter;
|
||||
}
|
||||
|
||||
ASTInterpreter.prototype = {
|
||||
compile: function(expression) {
|
||||
compile: function(ast) {
|
||||
var self = this;
|
||||
var ast = this.astBuilder.ast(expression);
|
||||
findConstantAndWatchExpressions(ast, self.$filter);
|
||||
var assignable;
|
||||
var assign;
|
||||
@@ -1283,8 +1277,6 @@ ASTInterpreter.prototype = {
|
||||
if (inputs) {
|
||||
fn.inputs = inputs;
|
||||
}
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
},
|
||||
|
||||
@@ -1613,20 +1605,21 @@ ASTInterpreter.prototype = {
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var Parser = function Parser(lexer, $filter, options) {
|
||||
this.lexer = lexer;
|
||||
this.$filter = $filter;
|
||||
this.options = options;
|
||||
function Parser(lexer, $filter, options) {
|
||||
this.ast = new AST(lexer, options);
|
||||
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
|
||||
new ASTCompiler(this.ast, $filter);
|
||||
};
|
||||
this.astCompiler = options.csp ? new ASTInterpreter($filter) :
|
||||
new ASTCompiler($filter);
|
||||
}
|
||||
|
||||
Parser.prototype = {
|
||||
constructor: Parser,
|
||||
|
||||
parse: function(text) {
|
||||
return this.astCompiler.compile(text);
|
||||
var ast = this.ast.ast(text);
|
||||
var fn = this.astCompiler.compile(ast);
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1772,8 +1765,8 @@ function $ParseProvider() {
|
||||
if (parsedExpression.constant) {
|
||||
parsedExpression.$$watchDelegate = constantWatchDelegate;
|
||||
} else if (oneTime) {
|
||||
parsedExpression.$$watchDelegate = parsedExpression.literal ?
|
||||
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
|
||||
parsedExpression.oneTime = true;
|
||||
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
|
||||
} else if (parsedExpression.inputs) {
|
||||
parsedExpression.$$watchDelegate = inputsWatchDelegate;
|
||||
}
|
||||
@@ -1795,14 +1788,14 @@ function $ParseProvider() {
|
||||
return newValue === oldValueOfValue;
|
||||
}
|
||||
|
||||
if (typeof newValue === 'object' && !compareObjectIdentity) {
|
||||
if (typeof newValue === 'object') {
|
||||
|
||||
// attempt to convert the value to a primitive type
|
||||
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
|
||||
// be cheaply dirty-checked
|
||||
newValue = getValueOf(newValue);
|
||||
|
||||
if (typeof newValue === 'object') {
|
||||
if (typeof newValue === 'object' && !compareObjectIdentity) {
|
||||
// objects/arrays are not supported - deep-watching them would be too expensive
|
||||
return false;
|
||||
}
|
||||
@@ -1859,6 +1852,7 @@ function $ParseProvider() {
|
||||
}
|
||||
|
||||
function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
|
||||
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
|
||||
var unwatch, lastValue;
|
||||
if (parsedExpression.inputs) {
|
||||
unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression);
|
||||
@@ -1875,9 +1869,9 @@ function $ParseProvider() {
|
||||
if (isFunction(listener)) {
|
||||
listener(value, old, scope);
|
||||
}
|
||||
if (isDefined(value)) {
|
||||
if (isDone(value)) {
|
||||
scope.$$postDigest(function() {
|
||||
if (isDefined(lastValue)) {
|
||||
if (isDone(lastValue)) {
|
||||
unwatch();
|
||||
}
|
||||
});
|
||||
@@ -1885,31 +1879,12 @@ function $ParseProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
|
||||
var unwatch, lastValue;
|
||||
unwatch = scope.$watch(function oneTimeWatch(scope) {
|
||||
return parsedExpression(scope);
|
||||
}, function oneTimeListener(value, old, scope) {
|
||||
lastValue = value;
|
||||
if (isFunction(listener)) {
|
||||
listener(value, old, scope);
|
||||
}
|
||||
if (isAllDefined(value)) {
|
||||
scope.$$postDigest(function() {
|
||||
if (isAllDefined(lastValue)) unwatch();
|
||||
});
|
||||
}
|
||||
}, objectEquality);
|
||||
|
||||
return unwatch;
|
||||
|
||||
function isAllDefined(value) {
|
||||
var allDefined = true;
|
||||
forEach(value, function(val) {
|
||||
if (!isDefined(val)) allDefined = false;
|
||||
});
|
||||
return allDefined;
|
||||
}
|
||||
function isAllDefined(value) {
|
||||
var allDefined = true;
|
||||
forEach(value, function(val) {
|
||||
if (!isDefined(val)) allDefined = false;
|
||||
});
|
||||
return allDefined;
|
||||
}
|
||||
|
||||
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
|
||||
@@ -1925,26 +1900,31 @@ function $ParseProvider() {
|
||||
var watchDelegate = parsedExpression.$$watchDelegate;
|
||||
var useInputs = false;
|
||||
|
||||
var regularWatch =
|
||||
watchDelegate !== oneTimeLiteralWatchDelegate &&
|
||||
watchDelegate !== oneTimeWatchDelegate;
|
||||
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
|
||||
|
||||
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
|
||||
function regularInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
|
||||
return interceptorFn(value, scope, locals);
|
||||
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = parsedExpression(scope, locals, assign, inputs);
|
||||
}
|
||||
|
||||
function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
|
||||
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
|
||||
var result = interceptorFn(value, scope, locals);
|
||||
// we only return the interceptor's result if the
|
||||
// initial value is defined (for bind-once)
|
||||
return isDefined(value) ? result : value;
|
||||
};
|
||||
return isDone(value) ? result : value;
|
||||
}
|
||||
|
||||
// Propagate $$watchDelegates other then inputsWatchDelegate
|
||||
var fn = parsedExpression.oneTime ? oneTimeInterceptedExpression : regularInterceptedExpression;
|
||||
|
||||
// Propogate the literal/oneTime attributes
|
||||
fn.literal = parsedExpression.literal;
|
||||
fn.oneTime = parsedExpression.oneTime;
|
||||
|
||||
// Propagate or create inputs / $$watchDelegates
|
||||
useInputs = !parsedExpression.inputs;
|
||||
if (parsedExpression.$$watchDelegate &&
|
||||
parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
|
||||
fn.$$watchDelegate = parsedExpression.$$watchDelegate;
|
||||
if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
|
||||
fn.$$watchDelegate = watchDelegate;
|
||||
fn.inputs = parsedExpression.inputs;
|
||||
} else if (!interceptorFn.$stateful) {
|
||||
// If there is an interceptor, but no watchDelegate then treat the interceptor like
|
||||
|
||||
+252
-182
@@ -16,12 +16,21 @@
|
||||
var $sceMinErr = minErr('$sce');
|
||||
|
||||
var SCE_CONTEXTS = {
|
||||
// HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding).
|
||||
HTML: 'html',
|
||||
|
||||
// Style statements or stylesheets. Currently unused in AngularJS.
|
||||
CSS: 'css',
|
||||
|
||||
// An URL used in a context where it does not refer to a resource that loads code. Currently
|
||||
// unused in AngularJS.
|
||||
URL: 'url',
|
||||
// RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
|
||||
// url. (e.g. ng-include, script src, templateUrl)
|
||||
|
||||
// RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as
|
||||
// code. (e.g. ng-include, script src binding, templateUrl)
|
||||
RESOURCE_URL: 'resourceUrl',
|
||||
|
||||
// Script. Currently unused in AngularJS.
|
||||
JS: 'js'
|
||||
};
|
||||
|
||||
@@ -83,6 +92,16 @@ function adjustMatchers(matchers) {
|
||||
* `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
|
||||
* Contextual Escaping (SCE)} services to AngularJS.
|
||||
*
|
||||
* For an overview of this service and the functionnality it provides in AngularJS, see the main
|
||||
* page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how
|
||||
* SCE works in their application, which shouldn't be needed in most cases.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* AngularJS strongly relies on contextual escaping for the security of bindings: disabling or
|
||||
* modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners,
|
||||
* changes to this service will also influence users, so be extra careful and document your changes.
|
||||
* </div>
|
||||
*
|
||||
* Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
|
||||
* the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
|
||||
* because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
|
||||
@@ -108,10 +127,14 @@ function adjustMatchers(matchers) {
|
||||
* @description
|
||||
*
|
||||
* The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
|
||||
* $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
|
||||
* that the URLs used for sourcing Angular templates are safe. Refer {@link
|
||||
* ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
|
||||
* $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}.
|
||||
*
|
||||
* The `$sceDelegateProvider` allows one to get/set the whitelists and blacklists used to ensure
|
||||
* that the URLs used for sourcing AngularJS templates and other script-running URLs are safe (all
|
||||
* places that use the `$sce.RESOURCE_URL` context). See
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist}
|
||||
* and
|
||||
* {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist},
|
||||
*
|
||||
* For the general details about this service in Angular, read the main page for {@link ng.$sce
|
||||
* Strict Contextual Escaping (SCE)}.
|
||||
@@ -140,6 +163,13 @@ function adjustMatchers(matchers) {
|
||||
* ]);
|
||||
* });
|
||||
* ```
|
||||
* Note that an empty whitelist will block every resource URL from being loaded, and will require
|
||||
* you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates
|
||||
* requested by {@link ng.$templateRequest $templateRequest} that are present in
|
||||
* {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism
|
||||
* to populate your templates in that cache at config time, then it is a good idea to remove 'self'
|
||||
* from that whitelist. This helps to mitigate the security impact of certain types of issues, like
|
||||
* for instance attacker-controlled `ng-includes`.
|
||||
*/
|
||||
|
||||
function $SceDelegateProvider() {
|
||||
@@ -155,23 +185,23 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
* @return {Array} The currently set whitelist array.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** an empty whitelist array will block all URLs!
|
||||
* </div>
|
||||
*
|
||||
* @return {Array} the currently set whitelist array.
|
||||
* @description
|
||||
* Sets/Gets the whitelist of trusted resource URLs.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is `['self']` allowing only
|
||||
* same origin resource requests.
|
||||
*
|
||||
* @description
|
||||
* Sets/Gets the whitelist of trusted resource URLs.
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** the default whitelist of 'self' is not recommended if your app shares its origin
|
||||
* with other apps! It is a good idea to limit it to only your application's directory.
|
||||
* </div>
|
||||
*/
|
||||
this.resourceUrlWhitelist = function(value) {
|
||||
if (arguments.length) {
|
||||
@@ -186,25 +216,23 @@ function $SceDelegateProvider() {
|
||||
* @kind function
|
||||
*
|
||||
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.
|
||||
* provided. This must be an array or null. A snapshot of this array is used so further
|
||||
* changes to the array are ignored.</p><p>
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.</p><p>
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
* </p><p>
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
*
|
||||
* Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
|
||||
* allowed in this array.
|
||||
*
|
||||
* The typical usage for the blacklist is to **block
|
||||
* [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
|
||||
* these would otherwise be trusted but actually return content from the redirected domain.
|
||||
*
|
||||
* Finally, **the blacklist overrides the whitelist** and has the final say.
|
||||
*
|
||||
* @return {Array} the currently set blacklist array.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
|
||||
* is no blacklist.)
|
||||
* @return {Array} The currently set blacklist array.
|
||||
*
|
||||
* @description
|
||||
* Sets/Gets the blacklist of trusted resource URLs.
|
||||
*
|
||||
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
|
||||
* is no blacklist.)
|
||||
*/
|
||||
|
||||
this.resourceUrlBlacklist = function(value) {
|
||||
@@ -288,17 +316,24 @@ function $SceDelegateProvider() {
|
||||
* @name $sceDelegate#trustAs
|
||||
*
|
||||
* @description
|
||||
* Returns an object that is trusted by angular for use in specified strict
|
||||
* contextual escaping contexts (such as ng-bind-html, ng-include, any src
|
||||
* attribute interpolation, any dom event binding attribute interpolation
|
||||
* such as for onclick, etc.) that uses the provided value.
|
||||
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
|
||||
* Returns a trusted representation of the parameter for the specified context. This trusted
|
||||
* object will later on be used as-is, without any security check, by bindings or directives
|
||||
* that require this security context.
|
||||
* For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass
|
||||
* the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as
|
||||
* `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the
|
||||
* sanitizer loaded, passing the value itself will render all the HTML that does not pose a
|
||||
* security risk.
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||||
* resourceUrl, html, js and css.
|
||||
* @param {*} value The value that that should be considered trusted/safe.
|
||||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||||
* where Angular expects a $sce.trustAs() return value.
|
||||
* See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those
|
||||
* trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual
|
||||
* escaping.
|
||||
*
|
||||
* @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
|
||||
* `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
|
||||
*
|
||||
* @param {*} value The value that should be considered trusted.
|
||||
* @return {*} A trusted representation of value, that can be used in the given context.
|
||||
*/
|
||||
function trustAs(type, trustedValue) {
|
||||
var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||||
@@ -330,11 +365,11 @@ function $SceDelegateProvider() {
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
|
||||
*
|
||||
* If the passed parameter is not a value that had been returned by {@link
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is.
|
||||
*
|
||||
* @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
|
||||
* call or anything else.
|
||||
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* call or anything else.
|
||||
* @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
||||
* `value` unchanged.
|
||||
*/
|
||||
@@ -351,33 +386,38 @@ function $SceDelegateProvider() {
|
||||
* @name $sceDelegate#getTrusted
|
||||
*
|
||||
* @description
|
||||
* Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
|
||||
* returns the originally supplied value if the queried context type is a supertype of the
|
||||
* created type. If this condition isn't satisfied, throws an exception.
|
||||
* Takes any input, and either returns a value that's safe to use in the specified context, or
|
||||
* throws an exception.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
|
||||
* (XSS) vulnerability in your application.
|
||||
* </div>
|
||||
* In practice, there are several cases. When given a string, this function runs checks
|
||||
* and sanitization to make it safe without prior assumptions. When given the result of a {@link
|
||||
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied
|
||||
* value if that value's context is valid for this call's context. Finally, this function can
|
||||
* also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization
|
||||
* is available or possible.)
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is to be used.
|
||||
* @param {string} type The context in which this value is to be used (such as `$sce.HTML`).
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} call.
|
||||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
|
||||
* `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
|
||||
* `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.)
|
||||
* @return {*} A version of the value that's safe to use in the given context, or throws an
|
||||
* exception if this is impossible.
|
||||
*/
|
||||
function getTrusted(type, maybeTrusted) {
|
||||
if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
|
||||
return maybeTrusted;
|
||||
}
|
||||
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
|
||||
// If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return
|
||||
// as-is.
|
||||
if (constructor && maybeTrusted instanceof constructor) {
|
||||
return maybeTrusted.$$unwrapTrustedValue();
|
||||
}
|
||||
// If we get here, then we may only take one of two actions.
|
||||
// 1. sanitize the value for the requested type, or
|
||||
// 2. throw an exception.
|
||||
// Otherwise, if we get here, then we may either make it safe, or throw an exception. This
|
||||
// depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL),
|
||||
// some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS
|
||||
// has no corresponding sinks.
|
||||
if (type === SCE_CONTEXTS.RESOURCE_URL) {
|
||||
// RESOURCE_URL uses a whitelist.
|
||||
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
|
||||
return maybeTrusted;
|
||||
} else {
|
||||
@@ -386,8 +426,10 @@ function $SceDelegateProvider() {
|
||||
maybeTrusted.toString());
|
||||
}
|
||||
} else if (type === SCE_CONTEXTS.HTML) {
|
||||
// htmlSanitizer throws its own error when no sanitizer is available.
|
||||
return htmlSanitizer(maybeTrusted);
|
||||
}
|
||||
// Default error when the $sce service has no way to make the input safe.
|
||||
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
|
||||
}
|
||||
|
||||
@@ -423,21 +465,27 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* # Strict Contextual Escaping
|
||||
*
|
||||
* Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
|
||||
* contexts to result in a value that is marked as safe to use for that context. One example of
|
||||
* such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
|
||||
* to these contexts as privileged or SCE contexts.
|
||||
* Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render
|
||||
* trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and
|
||||
* (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
|
||||
*
|
||||
* As of version 1.2, Angular ships with SCE enabled by default.
|
||||
* ## Overview
|
||||
*
|
||||
* Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
|
||||
* one to execute arbitrary javascript by the use of the expression() syntax. Refer
|
||||
* <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
|
||||
* You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
|
||||
* to the top of your HTML document.
|
||||
* To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in
|
||||
* HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically
|
||||
* run security checks on them (sanitizations, whitelists, depending on context), or throw when it
|
||||
* cannot guarantee the security of the result. That behavior depends strongly on contexts: HTML
|
||||
* can be sanitized, but template URLs cannot, for instance.
|
||||
*
|
||||
* SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
|
||||
* security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
|
||||
* To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML:
|
||||
* we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it
|
||||
* before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and
|
||||
* render the input as-is, you will need to mark it as trusted for that context before attempting
|
||||
* to bind it.
|
||||
*
|
||||
* As of version 1.2, AngularJS ships with SCE enabled by default.
|
||||
*
|
||||
* ## In practice
|
||||
*
|
||||
* Here's an example of a binding in a privileged context:
|
||||
*
|
||||
@@ -447,10 +495,10 @@ function $SceDelegateProvider() {
|
||||
* ```
|
||||
*
|
||||
* Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
|
||||
* disabled, this application allows the user to render arbitrary HTML into the DIV.
|
||||
* In a more realistic example, one may be rendering user comments, blog articles, etc. via
|
||||
* bindings. (HTML is just one example of a context where rendering user controlled input creates
|
||||
* security vulnerabilities.)
|
||||
* disabled, this application allows the user to render arbitrary HTML into the DIV, which would
|
||||
* be an XSS security bug. In a more realistic example, one may be rendering user comments, blog
|
||||
* articles, etc. via bindings. (HTML is just one example of a context where rendering user
|
||||
* controlled input creates security vulnerabilities.)
|
||||
*
|
||||
* For the case of HTML, you might use a library, either on the client side, or on the server side,
|
||||
* to sanitize unsafe HTML before binding to the value and rendering it in the document.
|
||||
@@ -460,25 +508,29 @@ function $SceDelegateProvider() {
|
||||
* ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
|
||||
* properties/fields and forgot to update the binding to the sanitized value?
|
||||
*
|
||||
* To be secure by default, you want to ensure that any such bindings are disallowed unless you can
|
||||
* determine that something explicitly says it's safe to use a value for binding in that
|
||||
* context. You can then audit your code (a simple grep would do) to ensure that this is only done
|
||||
* for those values that you can easily tell are safe - because they were received from your server,
|
||||
* sanitized by your library, etc. You can organize your codebase to help with this - perhaps
|
||||
* allowing only the files in a specific directory to do this. Ensuring that the internal API
|
||||
* exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
|
||||
* To be secure by default, AngularJS makes sure bindings go through that sanitization, or
|
||||
* any similar validation process, unless there's a good reason to trust the given value in this
|
||||
* context. That trust is formalized with a function call. This means that as a developer, you
|
||||
* can assume all untrusted bindings are safe. Then, to audit your code for binding security issues,
|
||||
* you just need to ensure the values you mark as trusted indeed are safe - because they were
|
||||
* received from your server, sanitized by your library, etc. You can organize your codebase to
|
||||
* help with this - perhaps allowing only the files in a specific directory to do this.
|
||||
* Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then
|
||||
* becomes a more manageable task.
|
||||
*
|
||||
* In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
|
||||
* (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
|
||||
* obtain values that will be accepted by SCE / privileged contexts.
|
||||
*
|
||||
* build the trusted versions of your values.
|
||||
*
|
||||
* ## How does it work?
|
||||
*
|
||||
* In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
|
||||
* $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
|
||||
* ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
|
||||
* {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
|
||||
* $sce.getTrusted(context, value)} rather than to the value directly. Think of this function as
|
||||
* a way to enforce the required security context in your data sink. Directives use {@link
|
||||
* ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs
|
||||
* the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also,
|
||||
* when binding without directives, AngularJS will understand the context of your bindings
|
||||
* automatically.
|
||||
*
|
||||
* As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
|
||||
* ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
|
||||
@@ -519,11 +571,12 @@ function $SceDelegateProvider() {
|
||||
* It's important to remember that SCE only applies to interpolation expressions.
|
||||
*
|
||||
* If your expressions are constant literals, they're automatically trusted and you don't need to
|
||||
* call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
|
||||
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
|
||||
*
|
||||
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||||
* through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
|
||||
* call `$sce.trustAs` on them (e.g.
|
||||
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. The `$sceDelegate` will
|
||||
* also use the `$sanitize` service if it is available when binding untrusted values to
|
||||
* `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you
|
||||
* wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in
|
||||
* your application.
|
||||
*
|
||||
* The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
|
||||
* templates in `ng-include` from your application's domain without having to even know about SCE.
|
||||
@@ -541,11 +594,17 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* | Context | Notes |
|
||||
* |---------------------|----------------|
|
||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
|
||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
|
||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
|
||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered, and the {@link ngSanitize.$sanitize $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. |
|
||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=`, `<img src=`, and some others sanitize their urls and don't constitute an SCE context.) |
|
||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. |
|
||||
*
|
||||
*
|
||||
* Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||||
* through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings
|
||||
* in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This
|
||||
* might evolve.
|
||||
*
|
||||
* ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
|
||||
*
|
||||
@@ -664,14 +723,15 @@ function $SceDelegateProvider() {
|
||||
* for little coding overhead. It will be much harder to take an SCE disabled application and
|
||||
* either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
|
||||
* for cases where you have a lot of existing code that was written before SCE was introduced and
|
||||
* you're migrating them a module at a time.
|
||||
* you're migrating them a module at a time. Also do note that this is an app-wide setting, so if
|
||||
* you are writing a library, you will cause security bugs applications using it.
|
||||
*
|
||||
* That said, here's how you can completely disable SCE:
|
||||
*
|
||||
* ```
|
||||
* angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
|
||||
* // Completely disable SCE. For demonstration purposes only!
|
||||
* // Do not use in new projects.
|
||||
* // Do not use in new projects or libraries.
|
||||
* $sceProvider.enabled(false);
|
||||
* });
|
||||
* ```
|
||||
@@ -686,8 +746,8 @@ function $SceProvider() {
|
||||
* @name $sceProvider#enabled
|
||||
* @kind function
|
||||
*
|
||||
* @param {boolean=} value If provided, then enables/disables SCE.
|
||||
* @return {boolean} true if SCE is enabled, false otherwise.
|
||||
* @param {boolean=} value If provided, then enables/disables SCE application-wide.
|
||||
* @return {boolean} True if SCE is enabled, false otherwise.
|
||||
*
|
||||
* @description
|
||||
* Enables/disables SCE and returns the current value.
|
||||
@@ -741,9 +801,9 @@ function $SceProvider() {
|
||||
* getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
|
||||
* will also succeed.
|
||||
*
|
||||
* Inheritance happens to capture this in a natural way. In some future, we
|
||||
* may not use inheritance anymore. That is OK because no code outside of
|
||||
* sce.js and sceSpecs.js would need to be aware of this detail.
|
||||
* Inheritance happens to capture this in a natural way. In some future, we may not use
|
||||
* inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to
|
||||
* be aware of this detail.
|
||||
*/
|
||||
|
||||
this.$get = ['$parse', '$sceDelegate', function(
|
||||
@@ -765,8 +825,8 @@ function $SceProvider() {
|
||||
* @name $sce#isEnabled
|
||||
* @kind function
|
||||
*
|
||||
* @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
|
||||
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
|
||||
* @return {Boolean} True if SCE is enabled, false otherwise. If you want to set the value, you
|
||||
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
|
||||
*
|
||||
* @description
|
||||
* Returns a boolean indicating if SCE is enabled.
|
||||
@@ -793,14 +853,14 @@ function $SceProvider() {
|
||||
* wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
|
||||
* *result*)}
|
||||
*
|
||||
* @param {string} type The kind of SCE context in which this result will be used.
|
||||
* @param {string} type The SCE context in which this result will be used.
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
sce.parseAs = function sceParseAs(type, expr) {
|
||||
var parsed = $parse(expr);
|
||||
@@ -818,18 +878,18 @@ function $SceProvider() {
|
||||
* @name $sce#trustAs
|
||||
*
|
||||
* @description
|
||||
* Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
|
||||
* returns an object that is trusted by angular for use in specified strict contextual
|
||||
* escaping contexts (such as ng-bind-html, ng-include, any src attribute
|
||||
* interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
|
||||
* that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
|
||||
* escaping.
|
||||
* Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a
|
||||
* wrapped object that represents your value, and the trust you have in its safety for the given
|
||||
* context. AngularJS can then use that value as-is in bindings of the specified secure context.
|
||||
* This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute
|
||||
* interpolations. See {@link ng.$sce $sce} for strict contextual escaping.
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is safe for use. e.g. url,
|
||||
* resourceUrl, html, js and css.
|
||||
* @param {*} value The value that that should be considered trusted/safe.
|
||||
* @returns {*} A value that can be used to stand in for the provided `value` in places
|
||||
* where Angular expects a $sce.trustAs() return value.
|
||||
* @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
|
||||
* `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
|
||||
*
|
||||
* @param {*} value The value that that should be considered trusted.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in the context you specified.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -840,11 +900,23 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsHtml(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
|
||||
* $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.HTML` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.HTML` context (like `ng-bind-html`).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $sce#trustAsCss
|
||||
*
|
||||
* @description
|
||||
* Shorthand method. `$sce.trustAsCss(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`}
|
||||
*
|
||||
* @param {*} value The value to mark as trusted for `$sce.CSS` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant
|
||||
* of your `value` in `$sce.CSS` context. This context is currently unused, so there are
|
||||
* almost no reasons to use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -855,11 +927,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsUrl(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
|
||||
* $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.URL` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.URL` context. That context is currently unused, so there are almost no reasons
|
||||
* to use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -870,11 +941,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsResourceUrl(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
|
||||
* $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the return
|
||||
* value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute
|
||||
* bindings, ...)
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -885,11 +955,10 @@ function $SceProvider() {
|
||||
* Shorthand method. `$sce.trustAsJs(value)` →
|
||||
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
|
||||
*
|
||||
* @param {*} value The value to trustAs.
|
||||
* @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
|
||||
* $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
|
||||
* only accept expressions that are either literal constants or are the
|
||||
* return value of {@link ng.$sce#trustAs $sce.trustAs}.)
|
||||
* @param {*} value The value to mark as trusted for `$sce.JS` context.
|
||||
* @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
|
||||
* in `$sce.JS` context. That context is currently unused, so there are almost no reasons to
|
||||
* use this function so far.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -898,16 +967,17 @@ function $SceProvider() {
|
||||
*
|
||||
* @description
|
||||
* Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
|
||||
* takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
|
||||
* originally supplied value if the queried context type is a supertype of the created type.
|
||||
* If this condition isn't satisfied, throws an exception.
|
||||
* takes any input, and either returns a value that's safe to use in the specified context,
|
||||
* or throws an exception. This function is aware of trusted values created by the `trustAs`
|
||||
* function and its shorthands, and when contexts are appropriate, returns the unwrapped value
|
||||
* as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a
|
||||
* safe value (e.g., no sanitization is available or possible.)
|
||||
*
|
||||
* @param {string} type The kind of context in which this value is to be used.
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
|
||||
* call.
|
||||
* @returns {*} The value the was originally provided to
|
||||
* {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
|
||||
* Otherwise, throws an exception.
|
||||
* @param {string} type The context in which this value is to be used.
|
||||
* @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs
|
||||
* `$sce.trustAs`} call, or anything else (which will not be considered trusted.)
|
||||
* @return {*} A version of the value that's safe to use in the given context, or throws an
|
||||
* exception if this is impossible.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -919,7 +989,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.HTML, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -931,7 +1001,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.CSS, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -943,7 +1013,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.URL, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -955,7 +1025,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sceDelegate.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -967,7 +1037,7 @@ function $SceProvider() {
|
||||
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
|
||||
*
|
||||
* @param {*} value The value to pass to `$sce.getTrusted`.
|
||||
* @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
|
||||
* @return {*} The return value of `$sce.getTrusted($sce.JS, value)`
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -979,12 +1049,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -996,12 +1066,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1013,12 +1083,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1030,12 +1100,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1047,12 +1117,12 @@ function $SceProvider() {
|
||||
* {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
|
||||
*
|
||||
* @param {string} expression String expression to compile.
|
||||
* @returns {function(context, locals)} a function which represents the compiled expression:
|
||||
* @return {function(context, locals)} A function which represents the compiled expression:
|
||||
*
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
||||
* are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
||||
* `context`.
|
||||
* * `context` – `{object}` – an object against which any expressions embedded in the
|
||||
* strings are evaluated against (typically a scope object).
|
||||
* * `locals` – `{object=}` – local variables context object, useful for overriding values
|
||||
* in `context`.
|
||||
*/
|
||||
|
||||
// Shorthand delegations.
|
||||
|
||||
Vendored
+2
@@ -1133,6 +1133,8 @@ angular.mock.dump = function(object) {
|
||||
$http.get('/auth.py').then(function(response) {
|
||||
authToken = response.headers('A-Token');
|
||||
$scope.user = response.data;
|
||||
}).catch(function() {
|
||||
$scope.status = 'Failed...';
|
||||
});
|
||||
|
||||
$scope.saveMessage = function(message) {
|
||||
|
||||
@@ -125,8 +125,8 @@ function shallowClearAndCopy(src, dst) {
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@`, then the value for that parameter will be
|
||||
* extracted from the corresponding property on the `data` object (provided when calling a
|
||||
* "non-GET" action method).
|
||||
* extracted from the corresponding property on the `data` object (provided when calling actions
|
||||
* with a request body).
|
||||
* For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
|
||||
* `someParam` will be `data.someProp`.
|
||||
* Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
|
||||
@@ -193,6 +193,8 @@ function shallowClearAndCopy(src, dst) {
|
||||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
|
||||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
|
||||
* with `http response` object. See {@link ng.$http $http interceptors}.
|
||||
* - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not.
|
||||
* If not specified only POST, PUT and PATCH requests will have a body.
|
||||
*
|
||||
* @param {Object} options Hash with custom settings that should extend the
|
||||
* default `$resourceProvider` behavior. The supported options are:
|
||||
@@ -237,9 +239,15 @@ function shallowClearAndCopy(src, dst) {
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
* - "class" actions without a body: `Resource.action([parameters], [success], [error])`
|
||||
* - "class" actions with a body: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* When calling instance methods, the instance itself is used as the request body (if the action
|
||||
* should have a body). By default, only actions using `POST`, `PUT` or `PATCH` have request
|
||||
* bodies, but you can use the `hasBody` configuration option to specify whether an action
|
||||
* should have a body or not (regardless of its HTTP method).
|
||||
*
|
||||
*
|
||||
* Success callback is called with (value (Object|Array), responseHeaders (Function),
|
||||
@@ -280,7 +288,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
* the Resource API. This object can be serialized through {@link angular.toJson} safely
|
||||
* without attaching Angular-specific fields. Notice that `JSON.stringify` (and
|
||||
* `angular.toJson`) automatically use this method when serializing a Resource instance
|
||||
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
|
||||
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior)).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -643,7 +651,7 @@ angular.module('ngResource', ['ng']).
|
||||
};
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
|
||||
var hasBody = action.hasBody === true || (action.hasBody !== false && /^(POST|PUT|PATCH)$/i.test(action.method));
|
||||
var numericTimeout = action.timeout;
|
||||
var cancellable = isDefined(action.cancellable) ?
|
||||
action.cancellable : route.defaults.cancellable;
|
||||
|
||||
+110
-58
@@ -2091,17 +2091,47 @@ describe('$compile', function() {
|
||||
));
|
||||
|
||||
|
||||
it('should work when directive is in a repeater', inject(
|
||||
function($compile, $httpBackend, $rootScope) {
|
||||
$httpBackend.expect('GET', 'hello.html').
|
||||
respond('<span>i=<span ng-transclude></span>;</span>');
|
||||
element = jqLite('<div><b class=hello ng-repeat="i in [1,2]">{{i}}</b></div>');
|
||||
$compile(element)($rootScope);
|
||||
describe('when directive is in a repeater', function() {
|
||||
var is;
|
||||
beforeEach(function() {
|
||||
is = [1, 2];
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(element.text()).toEqual('i=1;i=2;');
|
||||
function runTest() {
|
||||
inject(function($compile, $httpBackend, $rootScope) {
|
||||
$httpBackend.expect('GET', 'hello.html').
|
||||
respond('<span>i=<span ng-transclude></span>;</span>');
|
||||
element = jqLite('<div><b class=hello ng-repeat="i in [' + is + ']">{{i}}</b></div>');
|
||||
$compile(element)($rootScope);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect(element.text()).toEqual('i=' + is.join(';i=') + ';');
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
it('should work in jqLite and jQuery with jQuery.cleanData last patched by Angular', runTest);
|
||||
|
||||
it('should work with another library patching jqLite/jQuery.cleanData after Angular', function() {
|
||||
var cleanedCount = 0;
|
||||
var currentCleanData = jqLite.cleanData;
|
||||
jqLite.cleanData = function(elems) {
|
||||
cleanedCount += elems.length;
|
||||
// Don't return the output and explicitly pass only the first parameter
|
||||
// so that we're sure we're not relying on either of them. jQuery UI patch
|
||||
// behaves in this way.
|
||||
currentCleanData(elems);
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
// The initial ng-repeat div is dumped after parsing hence we expect cleanData
|
||||
// count to be one larger than size of the iterated array.
|
||||
expect(cleanedCount).toBe(is.length + 1);
|
||||
|
||||
// Restore the previous cleanData.
|
||||
jqLite.cleanData = currentCleanData;
|
||||
});
|
||||
});
|
||||
|
||||
describe('replace and not exactly one root element', function() {
|
||||
|
||||
@@ -5858,6 +5888,29 @@ describe('$compile', function() {
|
||||
expect(componentScope.owRef).toEqual({name: 'lucas', item: {name: 'martin'}});
|
||||
}));
|
||||
|
||||
// https://github.com/angular/angular.js/issues/15833
|
||||
it('should work with ng-model inputs', function() {
|
||||
var componentScope;
|
||||
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.directive('undi', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
undi: '<'
|
||||
},
|
||||
link: function($scope) { componentScope = $scope; }
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<form name="f" undi="[f.i]"><input name="i" ng-model="a"/></form>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(componentScope.undi).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not complain when the isolated scope changes', inject(function() {
|
||||
compile('<div><span my-component ow-ref="{name: name}">');
|
||||
@@ -8622,65 +8675,64 @@ describe('$compile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
if (jQuery) {
|
||||
describe('cleaning up after a replaced element', function() {
|
||||
var $compile, xs;
|
||||
beforeEach(inject(function(_$compile_) {
|
||||
$compile = _$compile_;
|
||||
xs = [0, 1];
|
||||
}));
|
||||
|
||||
function testCleanup() {
|
||||
var privateData, firstRepeatedElem;
|
||||
describe('cleaning up after a replaced element', function() {
|
||||
var $compile, xs;
|
||||
beforeEach(inject(function(_$compile_) {
|
||||
$compile = _$compile_;
|
||||
xs = [0, 1];
|
||||
}));
|
||||
|
||||
element = $compile('<div><div ng-repeat="x in xs" ng-click="noop()">{{x}}</div></div>')($rootScope);
|
||||
function testCleanup() {
|
||||
var privateData, firstRepeatedElem;
|
||||
|
||||
$rootScope.$apply('xs = [' + xs + ']');
|
||||
firstRepeatedElem = element.children('.ng-scope').eq(0);
|
||||
element = $compile('<div><div ng-repeat="x in xs" ng-click="noop()">{{x}}</div></div>')($rootScope);
|
||||
|
||||
expect(firstRepeatedElem.data('$scope')).toBeDefined();
|
||||
privateData = jQuery._data(firstRepeatedElem[0]);
|
||||
expect(privateData.events).toBeDefined();
|
||||
expect(privateData.events.click).toBeDefined();
|
||||
expect(privateData.events.click[0]).toBeDefined();
|
||||
$rootScope.$apply('xs = [' + xs + ']');
|
||||
firstRepeatedElem = element.children('.ng-scope').eq(0);
|
||||
|
||||
//Ensure the angular $destroy event is still sent
|
||||
var destroyCount = 0;
|
||||
element.find('div').on('$destroy', function() { destroyCount++; });
|
||||
expect(firstRepeatedElem.data('$scope')).toBeDefined();
|
||||
privateData = jqLite._data(firstRepeatedElem[0]);
|
||||
expect(privateData.events).toBeDefined();
|
||||
expect(privateData.events.click).toBeDefined();
|
||||
expect(privateData.events.click[0]).toBeDefined();
|
||||
|
||||
$rootScope.$apply('xs = null');
|
||||
//Ensure the angular $destroy event is still sent
|
||||
var destroyCount = 0;
|
||||
element.find('div').on('$destroy', function() { destroyCount++; });
|
||||
|
||||
expect(destroyCount).toBe(2);
|
||||
expect(firstRepeatedElem.data('$scope')).not.toBeDefined();
|
||||
privateData = jQuery._data(firstRepeatedElem[0]);
|
||||
expect(privateData && privateData.events).not.toBeDefined();
|
||||
}
|
||||
$rootScope.$apply('xs = null');
|
||||
|
||||
it('should work without external libraries (except jQuery)', testCleanup);
|
||||
|
||||
it('should work with another library patching jQuery.cleanData after Angular', function() {
|
||||
var cleanedCount = 0;
|
||||
var currentCleanData = jQuery.cleanData;
|
||||
jQuery.cleanData = function(elems) {
|
||||
cleanedCount += elems.length;
|
||||
// Don't return the output and explicitly pass only the first parameter
|
||||
// so that we're sure we're not relying on either of them. jQuery UI patch
|
||||
// behaves in this way.
|
||||
currentCleanData(elems);
|
||||
};
|
||||
|
||||
testCleanup();
|
||||
|
||||
// The ng-repeat template is removed/cleaned (the +1)
|
||||
// and each clone of the ng-repeat template is also removed (xs.length)
|
||||
expect(cleanedCount).toBe(xs.length + 1);
|
||||
|
||||
// Restore the previous jQuery.cleanData.
|
||||
jQuery.cleanData = currentCleanData;
|
||||
});
|
||||
});
|
||||
expect(destroyCount).toBe(2);
|
||||
expect(firstRepeatedElem.data('$scope')).not.toBeDefined();
|
||||
privateData = jqLite._data(firstRepeatedElem[0]);
|
||||
expect(privateData && privateData.events).not.toBeDefined();
|
||||
}
|
||||
|
||||
it('should work without external libraries (except jQuery)', testCleanup);
|
||||
|
||||
it('should work with another library patching jqLite/jQuery.cleanData after Angular', function() {
|
||||
var cleanedCount = 0;
|
||||
var currentCleanData = jqLite.cleanData;
|
||||
jqLite.cleanData = function(elems) {
|
||||
cleanedCount += elems.length;
|
||||
// Don't return the output and explicitly pass only the first parameter
|
||||
// so that we're sure we're not relying on either of them. jQuery UI patch
|
||||
// behaves in this way.
|
||||
currentCleanData(elems);
|
||||
};
|
||||
|
||||
testCleanup();
|
||||
|
||||
// The ng-repeat template is removed/cleaned (the +1)
|
||||
// and each clone of the ng-repeat template is also removed (xs.length)
|
||||
expect(cleanedCount).toBe(xs.length + 1);
|
||||
|
||||
// Restore the previous cleanData.
|
||||
jqLite.cleanData = currentCleanData;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should add a $$transcluded property onto the transcluded scope', function() {
|
||||
module(function() {
|
||||
|
||||
@@ -504,6 +504,10 @@ describe('filters', function() {
|
||||
expect(date(morning, 'yy/xxx')).toEqual('10/xxx');
|
||||
});
|
||||
|
||||
it('should allow newlines in format', function() {
|
||||
expect(date(midnight, 'EEE\nMMM d\'\n\'yy/xxx\n')).toEqual('Fri\nSep 3\n10/xxx\n');
|
||||
});
|
||||
|
||||
it('should support various iso8061 date strings with timezone as input', function() {
|
||||
var format = 'yyyy-MM-dd ss';
|
||||
|
||||
|
||||
+4
-6
@@ -1369,17 +1369,15 @@ describe('$http', function() {
|
||||
}
|
||||
);
|
||||
|
||||
it('should forward json deserialization errors to the http error handler',
|
||||
function() {
|
||||
it('should return JSON data with error message if JSON is invalid', function() {
|
||||
var errCallback = jasmine.createSpy('error');
|
||||
|
||||
$httpBackend.expect('GET', '/url').respond('abcd', {'Content-Type': 'application/json'});
|
||||
$http({method: 'GET', url: '/url'}).then(callback).catch(errCallback);
|
||||
$httpBackend.expect('GET', '/url').respond('{abcd}', {'Content-Type': 'application/json'});
|
||||
$http.get('/url').then(callback).catch(errCallback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
expect(errCallback).toHaveBeenCalledOnce();
|
||||
expect(errCallback.calls.mostRecent().args[0]).toEqual(jasmine.any(SyntaxError));
|
||||
expect(errCallback.calls.mostRecent().args[0]).toEqualMinErr('$http', 'baddata');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+158
-61
@@ -1867,6 +1867,8 @@ describe('parser', function() {
|
||||
expect(scope.$eval('+\'1\'')).toEqual(+'1');
|
||||
expect(scope.$eval('-\'1\'')).toEqual(-'1');
|
||||
expect(scope.$eval('+undefined')).toEqual(0);
|
||||
|
||||
// Note: don't change toEqual to toBe as toBe collapses 0 & -0.
|
||||
expect(scope.$eval('-undefined')).toEqual(-0);
|
||||
expect(scope.$eval('+null')).toEqual(+null);
|
||||
expect(scope.$eval('-null')).toEqual(-null);
|
||||
@@ -2686,82 +2688,86 @@ describe('parser', function() {
|
||||
expect($parse(':: ').literal).toBe(true);
|
||||
}));
|
||||
|
||||
it('should only become stable when all the properties of an object have defined values', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::{foo: foo, bar: bar}');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, true);
|
||||
[true, false].forEach(function(isDeep) {
|
||||
describe(isDeep ? 'deepWatch' : 'watch', function() {
|
||||
it('should only become stable when all the properties of an object have defined values', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::{foo: foo, bar: bar}');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, isDeep);
|
||||
|
||||
expect(log.empty()).toEqual([]);
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([]);
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([{foo: undefined, bar: undefined}]);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([{foo: undefined, bar: undefined}]);
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([{foo: 'foo', bar: undefined}]);
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([{foo: 'foo', bar: undefined}]);
|
||||
|
||||
$rootScope.foo = 'foobar';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([{foo: 'foobar', bar: 'bar'}]);
|
||||
$rootScope.foo = 'foobar';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([{foo: 'foobar', bar: 'bar'}]);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([]);
|
||||
}));
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should only become stable when all the elements of an array have defined values', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::[foo,bar]');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, true);
|
||||
it('should only become stable when all the elements of an array have defined values', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::[foo,bar]');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, isDeep);
|
||||
|
||||
expect(log.empty()).toEqual([]);
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([]);
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([[undefined, undefined]]);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([[undefined, undefined]]);
|
||||
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([['foo', undefined]]);
|
||||
$rootScope.foo = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([['foo', undefined]]);
|
||||
|
||||
$rootScope.foo = 'foobar';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([['foobar', 'bar']]);
|
||||
$rootScope.foo = 'foobar';
|
||||
$rootScope.bar = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([['foobar', 'bar']]);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([]);
|
||||
}));
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(0);
|
||||
expect(log.empty()).toEqual([]);
|
||||
}));
|
||||
|
||||
it('should only become stable when all the elements of an array have defined values at the end of a $digest', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::[foo]');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, true);
|
||||
$rootScope.$watch('foo', function() { if ($rootScope.foo === 'bar') {$rootScope.foo = undefined; } });
|
||||
it('should only become stable when all the elements of an array have defined values at the end of a $digest', inject(function($parse, $rootScope, log) {
|
||||
var fn = $parse('::[foo]');
|
||||
$rootScope.$watch(fn, function(value) { log(value); }, isDeep);
|
||||
$rootScope.$watch('foo', function() { if ($rootScope.foo === 'bar') {$rootScope.foo = undefined; } });
|
||||
|
||||
$rootScope.foo = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(2);
|
||||
expect(log.empty()).toEqual([['bar'], [undefined]]);
|
||||
$rootScope.foo = 'bar';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(2);
|
||||
expect(log.empty()).toEqual([['bar'], [undefined]]);
|
||||
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([['baz']]);
|
||||
$rootScope.foo = 'baz';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log.empty()).toEqual([['baz']]);
|
||||
|
||||
$rootScope.bar = 'qux';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log).toEqual([]);
|
||||
}));
|
||||
$rootScope.bar = 'qux';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$$watchers.length).toBe(1);
|
||||
expect(log).toEqual([]);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2870,6 +2876,40 @@ describe('parser', function() {
|
||||
expect(called).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not invoke interceptorFns unless the input.valueOf changes even if the instance changes', inject(function($parse) {
|
||||
var called = false;
|
||||
function interceptor(v) {
|
||||
called = true;
|
||||
return v;
|
||||
}
|
||||
scope.$watch($parse('a', interceptor));
|
||||
scope.a = new Date();
|
||||
scope.$digest();
|
||||
expect(called).toBe(true);
|
||||
|
||||
called = false;
|
||||
scope.a = new Date(scope.a.valueOf());
|
||||
scope.$digest();
|
||||
expect(called).toBe(false);
|
||||
}));
|
||||
|
||||
it('should invoke interceptorFns if input.valueOf changes even if the instance does not', inject(function($parse) {
|
||||
var called = false;
|
||||
function interceptor(v) {
|
||||
called = true;
|
||||
return v;
|
||||
}
|
||||
scope.$watch($parse('a', interceptor));
|
||||
scope.a = new Date();
|
||||
scope.$digest();
|
||||
expect(called).toBe(true);
|
||||
|
||||
called = false;
|
||||
scope.a.setTime(scope.a.getTime() + 1);
|
||||
scope.$digest();
|
||||
expect(called).toBe(true);
|
||||
}));
|
||||
|
||||
it('should invoke interceptors when the expression is `undefined`', inject(function($parse) {
|
||||
var called = false;
|
||||
function interceptor(v) {
|
||||
@@ -3038,6 +3078,63 @@ describe('parser', function() {
|
||||
expect(called).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not reevaluate literals with non-primitive input that does support valueOf()',
|
||||
inject(function($parse) {
|
||||
|
||||
var date = scope.date = new Date();
|
||||
|
||||
var parsed = $parse('[date]');
|
||||
var watcherCalls = 0;
|
||||
scope.$watch(parsed, function(input) {
|
||||
expect(input[0]).toBe(date);
|
||||
watcherCalls++;
|
||||
});
|
||||
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(1);
|
||||
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(1);
|
||||
}));
|
||||
|
||||
it('should not reevaluate literals with non-primitive input that does support valueOf()' +
|
||||
' when the instance changes but valueOf() does not', inject(function($parse) {
|
||||
|
||||
scope.date = new Date(1234567890123);
|
||||
|
||||
var parsed = $parse('[date]');
|
||||
var watcherCalls = 0;
|
||||
scope.$watch(parsed, function(input) {
|
||||
watcherCalls++;
|
||||
});
|
||||
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(1);
|
||||
|
||||
scope.date = new Date(1234567890123);
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(1);
|
||||
}));
|
||||
|
||||
it('should reevaluate literals with non-primitive input that does support valueOf()' +
|
||||
' when the instance does not change but valueOf() does', inject(function($parse) {
|
||||
|
||||
scope.date = new Date(1234567890123);
|
||||
|
||||
var parsed = $parse('[date]');
|
||||
var watcherCalls = 0;
|
||||
scope.$watch(parsed, function(input) {
|
||||
watcherCalls++;
|
||||
});
|
||||
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(1);
|
||||
|
||||
scope.date.setTime(scope.date.getTime() + 1);
|
||||
scope.$digest();
|
||||
expect(watcherCalls).toBe(2);
|
||||
}));
|
||||
|
||||
it('should continue with the evaluation of the expression without invoking computed parts',
|
||||
inject(function($parse) {
|
||||
var value = 'foo';
|
||||
|
||||
@@ -97,6 +97,76 @@ describe('basic usage', function() {
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should include a request body when calling custom method with hasBody is true', function() {
|
||||
var instant = {name: 'info.txt'};
|
||||
var condition = {at: '2038-01-19 03:14:08'};
|
||||
|
||||
$httpBackend.expect('CREATE', '/fooresource', instant).respond({fid: 42});
|
||||
$httpBackend.expect('DELETE', '/fooresource', condition).respond({});
|
||||
|
||||
var r = $resource('/fooresource', {}, {
|
||||
create: {method: 'CREATE', hasBody: true},
|
||||
delete: {method: 'DELETE', hasBody: true}
|
||||
});
|
||||
|
||||
var creationResponse = r.create(instant);
|
||||
var deleteResponse = r.delete(condition);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(creationResponse.fid).toBe(42);
|
||||
expect(deleteResponse.$resolved).toBe(true);
|
||||
});
|
||||
|
||||
it('should not include a request body if hasBody is false on POST, PUT and PATCH', function() {
|
||||
function verifyRequest(method, url, data) {
|
||||
expect(data).toBeUndefined();
|
||||
return [200, {id: 42}];
|
||||
}
|
||||
|
||||
$httpBackend.expect('POST', '/foo').respond(verifyRequest);
|
||||
$httpBackend.expect('PUT', '/foo').respond(verifyRequest);
|
||||
$httpBackend.expect('PATCH', '/foo').respond(verifyRequest);
|
||||
|
||||
var R = $resource('/foo', {}, {
|
||||
post: {method: 'POST', hasBody: false},
|
||||
put: {method: 'PUT', hasBody: false},
|
||||
patch: {method: 'PATCH', hasBody: false}
|
||||
});
|
||||
|
||||
var postResponse = R.post();
|
||||
var putResponse = R.put();
|
||||
var patchResponse = R.patch();
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(postResponse.id).toBe(42);
|
||||
expect(putResponse.id).toBe(42);
|
||||
expect(patchResponse.id).toBe(42);
|
||||
});
|
||||
|
||||
it('should expect a body if hasBody is true', function() {
|
||||
var username = 'yathos';
|
||||
var loginRequest = {name: username, password: 'Smile'};
|
||||
var user = {id: 1, name: username};
|
||||
|
||||
$httpBackend.expect('LOGIN', '/user/me', loginRequest).respond(user);
|
||||
|
||||
$httpBackend.expect('LOGOUT', '/user/me', null).respond(null);
|
||||
|
||||
var UserService = $resource('/user/me', {}, {
|
||||
login: {method: 'LOGIN', hasBody: true},
|
||||
logout: {method: 'LOGOUT', hasBody: false}
|
||||
});
|
||||
|
||||
var loginResponse = UserService.login(loginRequest);
|
||||
var logoutResponse = UserService.logout();
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(loginResponse.id).toBe(user.id);
|
||||
expect(logoutResponse.$resolved).toBe(true);
|
||||
});
|
||||
|
||||
it('should build resource', function() {
|
||||
expect(typeof CreditCard).toBe('function');
|
||||
|
||||
@@ -246,7 +246,7 @@ describe('HTML', function() {
|
||||
.toEqual('<p>text1text2</p>');
|
||||
});
|
||||
|
||||
it('should remove clobbered elements', function() {
|
||||
it('should throw on clobbered elements', function() {
|
||||
inject(function($sanitize) {
|
||||
expect(function() {
|
||||
$sanitize('<form><input name="parentNode" /></form>');
|
||||
|
||||
@@ -3491,9 +3491,9 @@ jodid25519@^1.0.0:
|
||||
dependencies:
|
||||
jsbn "~0.1.0"
|
||||
|
||||
jquery@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.1.1.tgz#347c1c21c7e004115e0a4da32cece041fad3c8a3"
|
||||
jquery@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
|
||||
|
||||
js-tokens@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user