diff --git a/.circleci/config.yml b/.circleci/config.yml
index d86c2e93a985..3fcd7a21c36c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -188,6 +188,8 @@ commands:
steps:
- run:
name: Save month to file
+ # Note: Make sure this file is excluded in the `.gitignore` as otherwise the
+ # snapshot stamping would have the `-with-local-changes` suffix.
command: date +%Y-%m > month.txt
# Normally this would be an individual job instead of a command.
diff --git a/.circleci/env.sh b/.circleci/env.sh
index 177ac4cfecf1..2dfcdf0e0fb7 100755
--- a/.circleci/env.sh
+++ b/.circleci/env.sh
@@ -3,7 +3,6 @@
# Variables
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
-readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
# Load helpers and make them available everywhere (through `$BASH_ENV`).
source $envHelpersPath;
diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml
index ca29f1be5ad0..3c161d13dbbb 100644
--- a/.github/workflows/dev-infra.yml
+++ b/.github/workflows/dev-infra.yml
@@ -4,6 +4,10 @@ on:
pull_request_target:
types: [opened, synchronize, reopened]
+# Declare default permissions as read only.
+permissions:
+ contents: read
+
jobs:
labels:
runs-on: ubuntu-latest
diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml
index 601fa01ce081..c7025b56d617 100644
--- a/.github/workflows/feature-requests.yml
+++ b/.github/workflows/feature-requests.yml
@@ -5,6 +5,10 @@ on:
# Run at 14:00 every day
- cron: '0 14 * * *'
+# Declare default permissions as read only.
+permissions:
+ contents: read
+
jobs:
feature_triage:
if: github.repository == 'angular/angular'
diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml
index 3e8f0f977057..e4612769f2b5 100644
--- a/.github/workflows/lock-closed.yml
+++ b/.github/workflows/lock-closed.yml
@@ -5,6 +5,10 @@ on:
# Run at 16:00 every day
- cron: '0 16 * * *'
+# Declare default permissions as read only.
+permissions:
+ contents: read
+
jobs:
lock_closed:
if: github.repository == 'angular/angular'
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index c6bd335524f0..ae9e41a65483 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -8,7 +8,8 @@ on:
workflow_dispatch:
# Declare default permissions as read only.
-permissions: read-all
+permissions:
+ contents: read
jobs:
analysis:
diff --git a/.gitignore b/.gitignore
index 140aed39fc01..e6081cab0bc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,10 @@
*.log
node_modules
+# CircleCI temporary file for cache key computation.
+# See `save_month_to_file` in `.circleci/config.yml`.
+month.txt
+
# Include when developing application packages.
pubspec.lock
.c9
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd0df42c2543..38744f04117a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,33 @@
+
+# 13.2.5 (2022-03-02)
+### animations
+| Commit | Type | Description |
+| -- | -- | -- |
+| [6c61d20476](https://github.com/angular/angular/commit/6c61d20476bf214176994dff4e46d6452bd821cf) | fix | allow animations with unsupported CSS properties ([#45185](https://github.com/angular/angular/pull/45185)) |
+### common
+| Commit | Type | Description |
+| -- | -- | -- |
+| [64da1daa78](https://github.com/angular/angular/commit/64da1daa7827ca99c6d0b3992ac10b1ea0ec5b4a) | fix | canceled JSONP requests won't throw console error with missing callback function ([#36807](https://github.com/angular/angular/pull/36807)) |
+| [56ca7d385b](https://github.com/angular/angular/commit/56ca7d385b263411cf231808fd54ec67ff643b58) | perf | make `NgLocalization` token tree-shakable ([#45118](https://github.com/angular/angular/pull/45118)) ([#45226](https://github.com/angular/angular/pull/45226)) |
+### compiler-cli
+| Commit | Type | Description |
+| -- | -- | -- |
+| [6c906a5bb9](https://github.com/angular/angular/commit/6c906a5bb9f7d7c86122bfc36275d6e6b81a0631) | fix | Support resolve animation name from the DTS ([#45169](https://github.com/angular/angular/pull/45169)) |
+### core
+| Commit | Type | Description |
+| -- | -- | -- |
+| [e8fd452bd2](https://github.com/angular/angular/commit/e8fd452bd24985f23ab6316690bb9431bbbe8ed8) | fix | remove individual commands for updating gold files ([#45198](https://github.com/angular/angular/pull/45198)) |
+| [82d772857c](https://github.com/angular/angular/commit/82d772857ce2d6f07af4c99380676b6bf2cf7912) | perf | make `Compiler`, `ApplicationRef` and `ApplicationInitStatus` tree-shakable ([#45102](https://github.com/angular/angular/pull/45102)) ([#45222](https://github.com/angular/angular/pull/45222)) |
+| [71ff12c1cc](https://github.com/angular/angular/commit/71ff12c1cc009e50977b77f1cba1fe03c2f81946) | perf | make `LOCALE_ID` and other tokens from `ApplicationModule` tree-shakable ([#45102](https://github.com/angular/angular/pull/45102)) ([#45222](https://github.com/angular/angular/pull/45222)) |
+### localize
+| Commit | Type | Description |
+| -- | -- | -- |
+| [d388522745](https://github.com/angular/angular/commit/d388522745835b8e30b66597560254f0e821c040) | fix | avoid imports into `compiler-cli` package ([#45180](https://github.com/angular/angular/pull/45180)) |
+## Special Thanks
+Andrew Kushnir, Andrew Scott, Charles Lyding, Guillaume Bonnet, Jessica Janiuk, JoostK, Martin Sikora, Paul Gschwendtner, Theodore Brown, dario-piotrowicz and ivanwonder
+
+
+
# 13.2.4 (2022-02-23)
### animations
diff --git a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts
index b2b2306c30e1..3462e7f29408 100644
--- a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts
+++ b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts
@@ -11,6 +11,8 @@ import { getLinkById, sleepFor } from './util';
import { getComponentSection, getToggleButton } from './querying.po';
describe('Animation Tests', () => {
+ const routingAnimationDuration = 350;
+
const openCloseHref = getLinkById('open-close');
const statusSliderHref = getLinkById('status');
const toggleHref = getLinkById('toggle');
@@ -20,6 +22,8 @@ describe('Animation Tests', () => {
const heroGroupsHref = getLinkById('hero-groups');
const queryingHref = getLinkById('querying');
+ const newPageSleepFor = (ms = 0) => sleepFor(ms + routingAnimationDuration);
+
beforeAll(() => browser.get(''));
describe('Open/Close Component', () => {
@@ -28,7 +32,7 @@ describe('Animation Tests', () => {
beforeAll(async () => {
await openCloseHref.click();
- await sleepFor();
+ await newPageSleepFor(300);
});
it('should be open', async () => {
@@ -84,7 +88,7 @@ describe('Animation Tests', () => {
beforeAll(async () => {
await statusSliderHref.click();
- await sleepFor(2000);
+ await newPageSleepFor(2000);
});
it('should be inactive with a blue background', async () => {
@@ -125,7 +129,7 @@ describe('Animation Tests', () => {
describe('Toggle Animations Component', () => {
beforeAll(async () => {
await toggleHref.click();
- await sleepFor();
+ await newPageSleepFor();
});
it('should disabled animations on the child element', async () => {
@@ -143,7 +147,7 @@ describe('Animation Tests', () => {
describe('Enter/Leave Component', () => {
beforeAll(async () => {
await enterLeaveHref.click();
- await sleepFor(100);
+ await newPageSleepFor(100);
});
it('should attach a flyInOut trigger to the list of items', async () => {
@@ -169,7 +173,7 @@ describe('Animation Tests', () => {
describe('Auto Calculation Component', () => {
beforeAll(async () => {
await autoHref.click();
- await sleepFor(300);
+ await newPageSleepFor();
});
it('should attach a shrinkOut trigger to the list of items', async () => {
@@ -193,7 +197,7 @@ describe('Animation Tests', () => {
describe('Filter/Stagger Component', () => {
beforeAll(async () => {
await filterHref.click();
- await sleepFor();
+ await newPageSleepFor();
});
it('should attach a filterAnimations trigger to the list container', async () => {
@@ -220,7 +224,7 @@ describe('Animation Tests', () => {
describe('Hero Groups Component', () => {
beforeAll(async () => {
await heroGroupsHref.click();
- await sleepFor(400);
+ await newPageSleepFor(400);
});
it('should attach a flyInOut trigger to the list of items', async () => {
@@ -250,7 +254,7 @@ describe('Animation Tests', () => {
beforeAll(async () => {
await queryingHref.click();
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
});
it('should toggle the section', async () => {
@@ -261,14 +265,14 @@ describe('Animation Tests', () => {
// toggling off
await toggleButton.click();
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
expect(await section.isPresent()).toBe(false);
// toggling on
await toggleButton.click();
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
expect(await section.isPresent()).toBe(true);
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
});
it(`should disable the button for the animation's duration`, async () => {
@@ -278,13 +282,13 @@ describe('Animation Tests', () => {
// toggling off
await toggleButton.click();
expect(await toggleButton.isEnabled()).toBe(false);
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
expect(await toggleButton.isEnabled()).toBe(true);
// toggling on
await toggleButton.click();
expect(await toggleButton.isEnabled()).toBe(false);
- await sleepFor(queryingAnimationDuration);
+ await newPageSleepFor(queryingAnimationDuration);
expect(await toggleButton.isEnabled()).toBe(true);
});
diff --git a/aio/content/examples/animations/src/app/app.component.html b/aio/content/examples/animations/src/app/app.component.html
index 50195d3f5639..3082ece03ed8 100644
--- a/aio/content/examples/animations/src/app/app.component.html
+++ b/aio/content/examples/animations/src/app/app.component.html
@@ -21,7 +21,7 @@
Animations
-
-
+
+
diff --git a/aio/content/examples/animations/src/app/app.component.ts b/aio/content/examples/animations/src/app/app.component.ts
index 2c79714b5d74..39b2c72e6b24 100644
--- a/aio/content/examples/animations/src/app/app.component.ts
+++ b/aio/content/examples/animations/src/app/app.component.ts
@@ -11,7 +11,7 @@ import {
} from '@angular/animations';
// #enddocregion imports
-import { RouterOutlet } from '@angular/router';
+import { ChildrenOutletContexts, RouterOutlet } from '@angular/router';
import { slideInAnimation } from './animations';
// #docregion decorator, toggle-app-animations, define
@@ -34,12 +34,13 @@ export class AppComponent {
public animationsDisabled = false;
// #enddocregion toggle-app-animations
-// #docregion prepare-router-outlet
- prepareRoute(outlet: RouterOutlet) {
- return outlet?.activatedRouteData?.['animation'];
- }
+// #docregion get-route-animations-data
+ constructor(private contexts: ChildrenOutletContexts) {}
-// #enddocregion prepare-router-outlet
+ getRouteAnimationData() {
+ return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation'];
+ }
+// #enddocregion get-route-animations-data
toggleAnimations() {
this.animationsDisabled = !this.animationsDisabled;
diff --git a/aio/content/examples/http/src/app/app.component.css b/aio/content/examples/http/src/app/app.component.css
new file mode 100644
index 000000000000..ea5fdfb1cdac
--- /dev/null
+++ b/aio/content/examples/http/src/app/app.component.css
@@ -0,0 +1,16 @@
+.checkboxes {
+ display: flex;
+ flex-wrap: wrap;
+ margin-bottom: 2rem;
+}
+
+.checkboxes > label {
+ flex: 0 0 max-content;
+ margin: 0.5rem;
+ cursor: pointer;
+}
+
+section:not(:last-of-type) {
+ padding-bottom: 2rem;
+ border-bottom: 1px solid black;
+}
diff --git a/aio/content/examples/http/src/app/app.component.html b/aio/content/examples/http/src/app/app.component.html
index dd5ae8926e3c..1a16ba0ed062 100644
--- a/aio/content/examples/http/src/app/app.component.html
+++ b/aio/content/examples/http/src/app/app.component.html
@@ -1,29 +1,46 @@
HttpClient Example
-
-
-
Heroes
+
+
+
+ Heroes
+
-
- Config
+
+
+ Config
+
-
- Downloader
+
+
+ Downloader
+
-
- Uploader
+
+
+ Uploader
+
-
- Search
+
+
+ Search
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/aio/content/examples/http/src/app/app.component.ts b/aio/content/examples/http/src/app/app.component.ts
index 655b55027610..09cd2020eee4 100644
--- a/aio/content/examples/http/src/app/app.component.ts
+++ b/aio/content/examples/http/src/app/app.component.ts
@@ -2,7 +2,8 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
- templateUrl: './app.component.html'
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.css']
})
export class AppComponent {
showHeroes = true;
@@ -16,4 +17,4 @@ export class AppComponent {
toggleDownloader() { this.showDownloader = !this.showDownloader; }
toggleUploader() { this.showUploader = !this.showUploader; }
toggleSearch() { this.showSearch = !this.showSearch; }
- }
+}
diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.css b/aio/content/examples/http/src/app/heroes/heroes.component.css
index d5a402a03974..ec5cb40d5655 100644
--- a/aio/content/examples/http/src/app/heroes/heroes.component.css
+++ b/aio/content/examples/http/src/app/heroes/heroes.component.css
@@ -9,57 +9,71 @@
}
.heroes li {
- display: flex;
- width: 100%;
+ display: grid;
+ grid-template-columns: min-content max-content 1fr max-content max-content;
+ grid-template-areas: "badge name . edit delete";
+ margin: 1rem 0;
+ align-items: center;
+ background-color: #eee;
+ border-radius: 4px;
}
-.heroes li:hover {
- left: .1em;
+.heroes li .badge {
+ grid-area: badge;
}
-.heroes button.delete {
- color: black;
- display: block;
- font-size: 1.2rem;
- background-color: #eee;
- border-radius: 0 4px 4px 0;
+.heroes li .name {
+ grid-area: name;
+}
+
+.heroes li button {
+ margin: 0.3rem;
+ padding: 0.3rem 0.8rem;
+}
+
+.heroes li button.edit {
+ grid-area: edit;
+}
+
+.heroes li button.delete {
+ grid-area: delete;
}
-.heroes a:hover {
- color: #2c3a41;
- background-color: #e6e6e6;
+.heroes li button:hover, .heros li button:focus {
+ color: #eee;
+ background-color: #000;
}
.heroes .badge {
- padding: .5em .6em;
+ padding: 0.5rem 0.6rem;
color: white;
background-color: #435B60;
- min-width: 16px;
- margin-right: .8em;
+ margin-right: 0.8rem;
border-radius: 4px 0 0 4px;
-}
-
-.heroes button.edit {
- flex: 1;
- text-align: left;
- padding: 0;
- margin-right: 0;
+ align-self: stretch;
display: flex;
align-items: center;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-button.delete {
- right: -8px;
- top: 5px;
- background-color: gray;
- color: white;
- padding: 5px 8px;
- width: 2em;
}
.heroes input {
max-width: 12rem;
padding: .25rem;
}
+
+@media (max-width: 550px) {
+ .heroes li {
+ grid-template-columns: min-content 1fr 1fr;
+ grid-template-rows: 2rem max-content;
+ grid-template-areas:
+ "badge name name"
+ "badge edit delete";
+ }
+
+ .heroes li .name {
+ margin-top: 0.3rem;
+ }
+
+ .heroes li button {
+ justify-self: center;
+ }
+}
\ No newline at end of file
diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.html b/aio/content/examples/http/src/app/heroes/heroes.component.html
index 3e9fb41478cb..75b3f81363fb 100644
--- a/aio/content/examples/http/src/app/heroes/heroes.component.html
+++ b/aio/content/examples/http/src/app/heroes/heroes.component.html
@@ -1,29 +1,29 @@
Heroes
Hero name:
-
+
-
-
- Add hero
-
-
+ Add hero
+
Search
diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.ts b/aio/content/examples/http/src/app/heroes/heroes.component.ts
index ea0a0f263c3f..79124cfeebbd 100644
--- a/aio/content/examples/http/src/app/heroes/heroes.component.ts
+++ b/aio/content/examples/http/src/app/heroes/heroes.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Hero } from './hero';
import { HeroesService } from './heroes.service';
@@ -12,9 +12,17 @@ import { HeroesService } from './heroes.service';
export class HeroesComponent implements OnInit {
heroes: Hero[] = [];
editHero: Hero | undefined; // the hero currently being edited
+ heroName = '';
constructor(private heroesService: HeroesService) {}
+ @ViewChild('heroEditInput')
+ set heroEditInput(element: ElementRef
) {
+ if (element) {
+ element.nativeElement.focus();
+ }
+ }
+
ngOnInit() {
this.getHeroes();
}
@@ -55,8 +63,9 @@ export class HeroesComponent implements OnInit {
*/
}
- edit(hero: Hero) {
- this.editHero = hero;
+ edit(heroName: string) {
+ this.update(heroName);
+ this.editHero = undefined;
}
search(searchTerm: string) {
@@ -65,13 +74,15 @@ export class HeroesComponent implements OnInit {
this.heroesService
.searchHeroes(searchTerm)
.subscribe(heroes => (this.heroes = heroes));
+ } else {
+ this.getHeroes();
}
}
- update() {
- if (this.editHero) {
+ update(heroName: string) {
+ if (heroName && this.editHero && this.editHero.name !== heroName) {
this.heroesService
- .updateHero(this.editHero)
+ .updateHero({...this.editHero, name: heroName})
.subscribe(hero => {
// replace the hero in the heroes list with update from server
const ix = hero ? this.heroes.findIndex(h => h.id === hero.id) : -1;
diff --git a/aio/content/examples/http/src/app/messages/messages.component.css b/aio/content/examples/http/src/app/messages/messages.component.css
new file mode 100644
index 000000000000..9c0f3c715633
--- /dev/null
+++ b/aio/content/examples/http/src/app/messages/messages.component.css
@@ -0,0 +1,9 @@
+:host {
+ display: block;
+ padding: 1rem;
+ background-color: rgb(255, 245, 187);
+}
+
+h2 {
+ margin: 0;
+}
diff --git a/aio/content/examples/http/src/app/messages/messages.component.html b/aio/content/examples/http/src/app/messages/messages.component.html
index 92dafa8cd7c2..5d05ac003275 100644
--- a/aio/content/examples/http/src/app/messages/messages.component.html
+++ b/aio/content/examples/http/src/app/messages/messages.component.html
@@ -1,8 +1,8 @@
-
-
Messages
+
Messages
+
clear
{{message}}
-
+
diff --git a/aio/content/examples/http/src/app/messages/messages.component.ts b/aio/content/examples/http/src/app/messages/messages.component.ts
index d54a1216651c..034c0ac0afc6 100644
--- a/aio/content/examples/http/src/app/messages/messages.component.ts
+++ b/aio/content/examples/http/src/app/messages/messages.component.ts
@@ -3,7 +3,8 @@ import { MessageService } from '../message.service';
@Component({
selector: 'app-messages',
- templateUrl: './messages.component.html'
+ templateUrl: './messages.component.html',
+ styleUrls: ['./messages.component.css']
})
export class MessagesComponent {
constructor(public messageService: MessageService) {}
diff --git a/aio/content/examples/i18n/angular.json b/aio/content/examples/i18n/angular.json
index cbcc13ff4244..b708244e8380 100644
--- a/aio/content/examples/i18n/angular.json
+++ b/aio/content/examples/i18n/angular.json
@@ -44,13 +44,8 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
- "assets": [
- "src/favicon.ico",
- "src/assets"
- ],
- "styles": [
- "src/styles.css"
- ],
+ "assets": ["src/favicon.ico", "src/assets"],
+ "styles": ["src/styles.css"],
"scripts": [],
// #docregion missing-translation-error
"i18nMissingTranslation": "error"
@@ -82,6 +77,7 @@
"outputHashing": "all"
},
"development": {
+ "localize": false,
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
@@ -91,9 +87,7 @@
},
// #docregion build-single-locale, build-production-french
"fr": {
- "localize": [
- "fr"
- ]
+ "localize": ["fr"]
}
},
// #enddocregion build-single-locale, build-production-french
@@ -113,7 +107,7 @@
},
// #docregion build-single-locale, build-production-french
"fr": {
- "browserTarget": "angular.io-example:build:fr"
+ "browserTarget": "angular.io-example:build:development,fr"
}
},
// #enddocregion build-single-locale, build-production-french
@@ -134,13 +128,8 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
- "assets": [
- "src/favicon.ico",
- "src/assets"
- ],
- "styles": [
- "src/styles.css"
- ],
+ "assets": ["src/favicon.ico", "src/assets"],
+ "styles": ["src/styles.css"],
"scripts": []
}
},
diff --git a/aio/content/examples/router/src/app/app.component.2.html b/aio/content/examples/router/src/app/app.component.2.html
index 51e62b5bd178..a361c1d7e14f 100644
--- a/aio/content/examples/router/src/app/app.component.2.html
+++ b/aio/content/examples/router/src/app/app.component.2.html
@@ -4,6 +4,6 @@ Angular Router
Crisis Center
Heroes
-
-
+
+
\ No newline at end of file
diff --git a/aio/content/examples/router/src/app/app.component.2.ts b/aio/content/examples/router/src/app/app.component.2.ts
index 533022096e94..8e0a63362d99 100644
--- a/aio/content/examples/router/src/app/app.component.2.ts
+++ b/aio/content/examples/router/src/app/app.component.2.ts
@@ -2,7 +2,7 @@
// #docregion
import { Component } from '@angular/core';
// #docregion animation-imports
-import { RouterOutlet } from '@angular/router';
+import { ChildrenOutletContexts } from '@angular/router';
import { slideInAnimation } from './animations';
@Component({
@@ -14,8 +14,10 @@ import { slideInAnimation } from './animations';
// #enddocregion animation-imports
// #docregion function-binding
export class AppComponent {
- getAnimationData(outlet: RouterOutlet) {
- return outlet?.activatedRouteData?.['animation'];
+ constructor(private contexts: ChildrenOutletContexts) {}
+
+ getAnimationData() {
+ return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation'];
}
}
// #enddocregion function-binding
diff --git a/aio/content/examples/router/src/app/app.component.4.html b/aio/content/examples/router/src/app/app.component.4.html
index 4a32aedb34c2..1db98d81a9e9 100644
--- a/aio/content/examples/router/src/app/app.component.4.html
+++ b/aio/content/examples/router/src/app/app.component.4.html
@@ -8,8 +8,8 @@
Angular Router
-
-
+
+
\ No newline at end of file
diff --git a/aio/content/examples/router/src/app/app.component.5.html b/aio/content/examples/router/src/app/app.component.5.html
index 4cc8c91ffc7c..a5c392a60257 100644
--- a/aio/content/examples/router/src/app/app.component.5.html
+++ b/aio/content/examples/router/src/app/app.component.5.html
@@ -6,7 +6,7 @@
Angular Router
Admin
Contact
-
-
+
+
\ No newline at end of file
diff --git a/aio/content/examples/router/src/app/app.component.6.html b/aio/content/examples/router/src/app/app.component.6.html
index b082558ecb34..48cc9ba0bbc3 100644
--- a/aio/content/examples/router/src/app/app.component.6.html
+++ b/aio/content/examples/router/src/app/app.component.6.html
@@ -7,7 +7,7 @@
Angular Router
Login
Contact
-
-
+
+
\ No newline at end of file
diff --git a/aio/content/examples/router/src/app/app.component.html b/aio/content/examples/router/src/app/app.component.html
index eff3c6d5d259..2cbe855b524b 100644
--- a/aio/content/examples/router/src/app/app.component.html
+++ b/aio/content/examples/router/src/app/app.component.html
@@ -8,8 +8,8 @@
Angular Router
Login
Contact
-
diff --git a/aio/content/examples/router/src/app/app.component.ts b/aio/content/examples/router/src/app/app.component.ts
index b3dd99b684ea..58f6c90c6642 100644
--- a/aio/content/examples/router/src/app/app.component.ts
+++ b/aio/content/examples/router/src/app/app.component.ts
@@ -1,7 +1,7 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
-import { RouterOutlet } from '@angular/router';
+import { ChildrenOutletContexts } from '@angular/router';
import { slideInAnimation } from './animations';
@Component({
@@ -11,7 +11,9 @@ import { slideInAnimation } from './animations';
animations: [ slideInAnimation ]
})
export class AppComponent {
- getAnimationData(outlet: RouterOutlet) {
- return outlet?.activatedRouteData?.['animation'];
+ constructor(private contexts: ChildrenOutletContexts) {}
+
+ getRouteAnimationData() {
+ return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation'];
}
}
diff --git a/aio/content/guide/example-apps-list.md b/aio/content/guide/example-apps-list.md
index 10456e327994..048a2af15f26 100644
--- a/aio/content/guide/example-apps-list.md
+++ b/aio/content/guide/example-apps-list.md
@@ -430,6 +430,7 @@ Demonstrates Angular's fundamental routing techniques.
For more information, see [Using Angular routes in a single-page application](guide/router-tutorial).
+## Documentation
### Style guide for Documentation contributions
diff --git a/aio/content/guide/route-animations.md b/aio/content/guide/route-animations.md
index b41cbab7fe5d..2c99bc73325c 100644
--- a/aio/content/guide/route-animations.md
+++ b/aio/content/guide/route-animations.md
@@ -59,15 +59,15 @@ In addition to `path` and `component`, the `data` property of each route defines
After configuring the routes, add a `
` inside the root `AppComponent` template. The `` directive tells the Angular router where to render the views when matched with a route.
-The `` directive holds the custom data set for the currently active route which can be accessed via the directive's `activatedRouteData` property, we can use such data to animate our routing transitions.
+The `ChildrenOutletContexts` holds information about outlets and activated routes. We can use the `data` property of each `Route` to animate our routing transitions.
`AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens.
-
+
-Here, the `prepareRoute()` method takes the value of the outlet directive (established through `#outlet="outlet"`) and returns a string value representing the state of the animation based on the custom data of the current active route. Use this data to control which transition to execute for each route.
+Here, the `getRouteAnimationData()` method takes the value of the outlet and returns a string which represents the state of the animation based on the custom data of the current active route. Use this data to control which transition to execute for each route.
## Animation definition
diff --git a/aio/content/guide/router-tutorial-toh.md b/aio/content/guide/router-tutorial-toh.md
index a2cacc9d1500..332feddee411 100644
--- a/aio/content/guide/router-tutorial-toh.md
+++ b/aio/content/guide/router-tutorial-toh.md
@@ -1225,8 +1225,8 @@ This example uses a variable of `routerOutlet`.
-The `@routeAnimation` property is bound to the `getAnimationData()` with the provided `routerOutlet` reference, so the next step is to define that function in the `AppComponent`.
-The `getAnimationData()` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
+The `@routeAnimation` property is bound to the `getAnimationData()` which returns the animation property from the `data` provided by the primary route. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
+
diff --git a/aio/tools/examples/shared/boilerplate/i18n/package.json b/aio/tools/examples/shared/boilerplate/i18n/package.json
index ba65cc42c7de..804074a5947f 100644
--- a/aio/tools/examples/shared/boilerplate/i18n/package.json
+++ b/aio/tools/examples/shared/boilerplate/i18n/package.json
@@ -8,7 +8,6 @@
"start": "ng serve",
"start:fr": "ng serve --configuration=fr",
"build": "ng build",
- "build:fr": "ng build --configuration=production-fr",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"e2e": "ng e2e",
diff --git a/goldens/public-api/common/common.md b/goldens/public-api/common/common.md
index 878431650b5f..78a90f2c9dbd 100644
--- a/goldens/public-api/common/common.md
+++ b/goldens/public-api/common/common.md
@@ -503,6 +503,10 @@ export class NgLocaleLocalization extends NgLocalization {
export abstract class NgLocalization {
// (undocumented)
abstract getPluralCategory(value: any, locale?: string): string;
+ // (undocumented)
+ static ɵfac: i0.ɵɵFactoryDeclaration;
+ // (undocumented)
+ static ɵprov: i0.ɵɵInjectableDeclaration;
}
// @public
diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json
index 96cfbaaf1021..8d03c28c7ac4 100755
--- a/goldens/size-tracking/aio-payloads.json
+++ b/goldens/size-tracking/aio-payloads.json
@@ -15,7 +15,7 @@
"master": {
"uncompressed": {
"runtime": 4343,
- "main": 451244,
+ "main": 450239,
"polyfills": 37297,
"styles": 70379,
"light-theme": 77582,
diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json
index e4c2eee5906c..a8ccddce36dc 100644
--- a/goldens/size-tracking/integration-payloads.json
+++ b/goldens/size-tracking/integration-payloads.json
@@ -3,7 +3,7 @@
"master": {
"uncompressed": {
"runtime": 1083,
- "main": 127944,
+ "main": 124282,
"polyfills": 37226
}
}
@@ -24,7 +24,7 @@
"master": {
"uncompressed": {
"runtime": 1105,
- "main": 133608,
+ "main": 131270,
"polyfills": 37248
}
}
@@ -33,7 +33,7 @@
"master": {
"uncompressed": {
"runtime": 929,
- "main": 126275,
+ "main": 123958,
"polyfills": 37933
}
}
@@ -42,7 +42,7 @@
"master": {
"uncompressed": {
"runtime": 2835,
- "main": 231989,
+ "main": 230780,
"polyfills": 37244,
"src_app_lazy_lazy_module_ts": 795
}
@@ -52,7 +52,7 @@
"master": {
"uncompressed": {
"runtime": 1063,
- "main": 159637,
+ "main": 155607,
"polyfills": 36975
}
}
@@ -61,7 +61,7 @@
"master": {
"uncompressed": {
"runtime": 1070,
- "main": 159755,
+ "main": 155563,
"polyfills": 37242
}
}
diff --git a/integration/typings_test_ts44/include-all.ts b/integration/typings_test_ts44/include-all.ts
index 86b57f2789d8..925e0ef07853 100644
--- a/integration/typings_test_ts44/include-all.ts
+++ b/integration/typings_test_ts44/include-all.ts
@@ -21,6 +21,7 @@ import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as elements from '@angular/elements';
import * as forms from '@angular/forms';
+import * as localize from '@angular/localize';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing';
@@ -51,6 +52,7 @@ export default {
coreTesting,
elements,
forms,
+ localize,
platformBrowser,
platformBrowserTesting,
platformBrowserDynamic,
diff --git a/integration/typings_test_ts44/package.json b/integration/typings_test_ts44/package.json
index 11b24fd1a827..9a4bcb938a03 100644
--- a/integration/typings_test_ts44/package.json
+++ b/integration/typings_test_ts44/package.json
@@ -11,6 +11,7 @@
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/elements": "file:../../dist/packages-dist/elements",
"@angular/forms": "file:../../dist/packages-dist/forms",
+ "@angular/localize": "file:../../dist/packages-dist/localize",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
diff --git a/integration/typings_test_ts45/include-all.ts b/integration/typings_test_ts45/include-all.ts
index 86b57f2789d8..925e0ef07853 100644
--- a/integration/typings_test_ts45/include-all.ts
+++ b/integration/typings_test_ts45/include-all.ts
@@ -21,6 +21,7 @@ import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as elements from '@angular/elements';
import * as forms from '@angular/forms';
+import * as localize from '@angular/localize';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing';
@@ -51,6 +52,7 @@ export default {
coreTesting,
elements,
forms,
+ localize,
platformBrowser,
platformBrowserTesting,
platformBrowserDynamic,
diff --git a/integration/typings_test_ts45/package.json b/integration/typings_test_ts45/package.json
index ee8b39d77c3d..4f8eed6b8573 100644
--- a/integration/typings_test_ts45/package.json
+++ b/integration/typings_test_ts45/package.json
@@ -11,6 +11,7 @@
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/elements": "file:../../dist/packages-dist/elements",
"@angular/forms": "file:../../dist/packages-dist/forms",
+ "@angular/localize": "file:../../dist/packages-dist/localize",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
diff --git a/package.json b/package.json
index ec6a4845c1a4..3c4971377bb4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "angular-srcs",
- "version": "13.2.4",
+ "version": "13.2.5",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
@@ -51,13 +51,14 @@
"@angular/platform-browser-dynamic-12": "npm:@angular/platform-browser-dynamic@12.2.13",
"@angular/platform-server-12": "npm:@angular/platform-server@12.2.13",
"@angular/router-12": "npm:@angular/router@12.2.13",
- "@babel/cli": "7.17.0",
- "@babel/core": "7.8.6",
- "@babel/generator": "7.8.6",
- "@babel/parser": "7.9.4",
- "@babel/preset-env": "7.10.2",
- "@babel/template": "7.8.6",
- "@babel/traverse": "7.8.6",
+ "@babel/cli": "7.17.3",
+ "@babel/core": "7.17.5",
+ "@babel/generator": "7.17.3",
+ "@babel/parser": "7.17.3",
+ "@babel/preset-env": "7.16.11",
+ "@babel/template": "7.16.7",
+ "@babel/traverse": "7.17.3",
+ "@babel/types": "7.17.0",
"@bazel/concatjs": "4.6.0",
"@bazel/esbuild": "4.6.0",
"@bazel/jasmine": "4.6.0",
@@ -72,10 +73,10 @@
"@rollup/plugin-node-resolve": "^13.0.4",
"@schematics/angular": "13.2.3",
"@types/angular": "^1.6.47",
- "@types/babel__core": "7.1.6",
- "@types/babel__generator": "7.6.1",
- "@types/babel__template": "7.0.2",
- "@types/babel__traverse": "7.0.9",
+ "@types/babel__core": "7.1.18",
+ "@types/babel__generator": "7.6.4",
+ "@types/babel__template": "7.4.1",
+ "@types/babel__traverse": "7.14.2",
"@types/base64-js": "1.3.0",
"@types/bluebird": "^3.5.27",
"@types/convert-source-map": "^1.5.1",
@@ -192,19 +193,11 @@
},
"// 4": "Overwrite graceful-fs to a version that does not rely on the 'natives' package. This fixes gulp for >= 10.13, more information: #28213",
"// 5": "Ensure a single version of webdriver-manager so it is hoisted as the integration tests depend on it being found at ../../node_modules/webdriver-manager",
- "// 6": "Ensure that `@babel/*` packages match the below versions to avoid conflicts with `types/babel__*`",
- "// 7": "Ensure that transitive dependencies on `https-proxy-agent` are at minimum v5 as older versions patch NodeJS directly, breaking tools like webdriver which is used by the karma-sauce-launcher as an example.",
- "// 8": "Ensure that a single instance of the `saucelabs` package is used. Protractor and the Karma sauce launcher pull this package as dependency. A single instance allows for e.g. easier patching in the Karma config.",
+ "// 6": "Ensure that transitive dependencies on `https-proxy-agent` are at minimum v5 as older versions patch NodeJS directly, breaking tools like webdriver which is used by the karma-sauce-launcher as an example.",
+ "// 7": "Ensure that a single instance of the `saucelabs` package is used. Protractor and the Karma sauce launcher pull this package as dependency. A single instance allows for e.g. easier patching in the Karma config.",
"resolutions": {
"**/graceful-fs": "4.2.9",
"**/webdriver-manager": "12.1.8",
- "@babel/core": "7.8.6",
- "@babel/generator": "7.8.6",
- "@babel/parser": "7.9.4",
- "@babel/preset-env": "7.10.2",
- "@babel/template": "7.8.6",
- "@babel/traverse": "7.8.6",
- "@babel/types": "7.8.6",
"**/https-proxy-agent": "5.0.0",
"**/saucelabs": "7.1.3"
}
diff --git a/packages/animations/browser/src/dsl/animation.ts b/packages/animations/browser/src/dsl/animation.ts
index f861efe91203..ba51897ac6da 100644
--- a/packages/animations/browser/src/dsl/animation.ts
+++ b/packages/animations/browser/src/dsl/animation.ts
@@ -10,6 +10,7 @@ import {AnimationMetadata, AnimationMetadataType, AnimationOptions, ɵStyleData}
import {buildingFailed, validationFailed} from '../error_helpers';
import {AnimationDriver} from '../render/animation_driver';
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, normalizeStyles} from '../util';
+import {warnValidation} from '../warning_helpers';
import {Ast} from './animation_ast';
import {buildAnimationAst} from './animation_ast_builder';
@@ -21,10 +22,14 @@ export class Animation {
private _animationAst: Ast;
constructor(private _driver: AnimationDriver, input: AnimationMetadata|AnimationMetadata[]) {
const errors: Error[] = [];
- const ast = buildAnimationAst(_driver, input, errors);
+ const warnings: string[] = [];
+ const ast = buildAnimationAst(_driver, input, errors, warnings);
if (errors.length) {
throw validationFailed(errors);
}
+ if (warnings.length) {
+ warnValidation(warnings);
+ }
this._animationAst = ast;
}
diff --git a/packages/animations/browser/src/dsl/animation_ast_builder.ts b/packages/animations/browser/src/dsl/animation_ast_builder.ts
index fb49c29ea325..7b708533fbe2 100644
--- a/packages/animations/browser/src/dsl/animation_ast_builder.ts
+++ b/packages/animations/browser/src/dsl/animation_ast_builder.ts
@@ -11,6 +11,7 @@ import {invalidDefinition, invalidKeyframes, invalidOffset, invalidParallelAnima
import {AnimationDriver} from '../render/animation_driver';
import {getOrSetAsInMap} from '../render/shared';
import {copyObj, extractStyleParams, iteratorToArray, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, normalizeAnimationEntry, resolveTiming, SUBSTITUTION_EXPR_START, validateStyleParams, visitDslNode} from '../util';
+import {pushUnrecognizedPropertiesWarning} from '../warning_helpers';
import {AnimateAst, AnimateChildAst, AnimateRefAst, Ast, DynamicTimingAst, GroupAst, KeyframesAst, QueryAst, ReferenceAst, SequenceAst, StaggerAst, StateAst, StyleAst, TimingAst, TransitionAst, TriggerAst} from './animation_ast';
import {AnimationDslVisitor} from './animation_dsl_visitor';
@@ -56,9 +57,9 @@ const SELF_TOKEN_REGEX = new RegExp(`\s*${SELF_TOKEN}\s*,?`, 'g');
* Otherwise an error will be thrown.
*/
export function buildAnimationAst(
- driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[],
- errors: Error[]): Ast {
- return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
+ driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[], errors: Error[],
+ warnings: string[]): Ast {
+ return new AnimationAstBuilderVisitor(driver).build(metadata, errors, warnings);
}
const ROOT_SELECTOR = '';
@@ -66,12 +67,20 @@ const ROOT_SELECTOR = '';
export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
constructor(private _driver: AnimationDriver) {}
- build(metadata: AnimationMetadata|AnimationMetadata[], errors: Error[]):
+ build(metadata: AnimationMetadata|AnimationMetadata[], errors: Error[], warnings: string[]):
Ast {
const context = new AnimationAstBuilderContext(errors);
this._resetContextStyleTimingState(context);
- return >visitDslNode(
- this, normalizeAnimationEntry(metadata), context);
+ const ast =
+ >visitDslNode(this, normalizeAnimationEntry(metadata), context);
+
+ if (context.unsupportedCSSPropertiesFound.size) {
+ pushUnrecognizedPropertiesWarning(
+ warnings,
+ [...context.unsupportedCSSPropertiesFound.keys()],
+ );
+ }
+ return ast;
}
private _resetContextStyleTimingState(context: AnimationAstBuilderContext) {
@@ -303,7 +312,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
Object.keys(tuple).forEach(prop => {
if (!this._driver.validateStyleProperty(prop)) {
- context.errors.push(invalidProperty(prop));
+ delete tuple[prop];
+ context.unsupportedCSSPropertiesFound.add(prop);
return;
}
@@ -507,6 +517,7 @@ export class AnimationAstBuilderContext {
public currentTime: number = 0;
public collectedStyles: {[selectorName: string]: {[propName: string]: StyleTimeTuple}} = {};
public options: AnimationOptions|null = null;
+ public unsupportedCSSPropertiesFound: Set = new Set();
constructor(public errors: Error[]) {}
}
diff --git a/packages/animations/browser/src/render/animation_engine_next.ts b/packages/animations/browser/src/render/animation_engine_next.ts
index 2202743e87fa..f34f17273d4a 100644
--- a/packages/animations/browser/src/render/animation_engine_next.ts
+++ b/packages/animations/browser/src/render/animation_engine_next.ts
@@ -12,6 +12,7 @@ import {buildAnimationAst} from '../dsl/animation_ast_builder';
import {AnimationTrigger, buildTrigger} from '../dsl/animation_trigger';
import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer';
import {triggerBuildFailed} from '../error_helpers';
+import {warnTriggerBuild} from '../warning_helpers';
import {AnimationDriver} from './animation_driver';
import {parseTimelineCommand} from './shared';
@@ -44,11 +45,15 @@ export class AnimationEngine {
let trigger = this._triggerCache[cacheKey];
if (!trigger) {
const errors: Error[] = [];
- const ast =
- buildAnimationAst(this._driver, metadata as AnimationMetadata, errors) as TriggerAst;
+ const warnings: string[] = [];
+ const ast = buildAnimationAst(
+ this._driver, metadata as AnimationMetadata, errors, warnings) as TriggerAst;
if (errors.length) {
throw triggerBuildFailed(name, errors);
}
+ if (warnings.length) {
+ warnTriggerBuild(name, warnings);
+ }
trigger = buildTrigger(name, ast, this._normalizer);
this._triggerCache[cacheKey] = trigger;
}
diff --git a/packages/animations/browser/src/render/timeline_animation_engine.ts b/packages/animations/browser/src/render/timeline_animation_engine.ts
index 77469cfebdb0..2aa999c68d7a 100644
--- a/packages/animations/browser/src/render/timeline_animation_engine.ts
+++ b/packages/animations/browser/src/render/timeline_animation_engine.ts
@@ -15,6 +15,7 @@ import {ElementInstructionMap} from '../dsl/element_instruction_map';
import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer';
import {createAnimationFailed, missingOrDestroyedAnimation, missingPlayer, registerFailed} from '../error_helpers';
import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '../util';
+import {warnRegister} from '../warning_helpers';
import {AnimationDriver} from './animation_driver';
import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
@@ -32,10 +33,14 @@ export class TimelineAnimationEngine {
register(id: string, metadata: AnimationMetadata|AnimationMetadata[]) {
const errors: Error[] = [];
- const ast = buildAnimationAst(this._driver, metadata, errors);
+ const warnings: string[] = [];
+ const ast = buildAnimationAst(this._driver, metadata, errors, warnings);
if (errors.length) {
throw registerFailed(errors);
} else {
+ if (warnings.length) {
+ warnRegister(warnings);
+ }
this._animations[id] = ast;
}
}
diff --git a/packages/animations/browser/src/warning_helpers.ts b/packages/animations/browser/src/warning_helpers.ts
new file mode 100644
index 000000000000..d93ca15dd7ef
--- /dev/null
+++ b/packages/animations/browser/src/warning_helpers.ts
@@ -0,0 +1,43 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
+
+function createListOfWarnings(warnings: string[]): string {
+ const LINE_START = '\n - ';
+ return `${LINE_START}${warnings.filter(Boolean).map(warning => warning).join(LINE_START)}`;
+}
+
+export function warnValidation(warnings: string[]): void {
+ NG_DEV_MODE && console.warn(`animation validation warnings:${createListOfWarnings(warnings)}`);
+}
+
+export function warnTriggerBuild(name: string, warnings: string[]): void {
+ NG_DEV_MODE &&
+ console.warn(`The animation trigger "${name}" has built with the following warnings:${
+ createListOfWarnings(warnings)}`);
+}
+
+export function warnRegister(warnings: string[]): void {
+ NG_DEV_MODE &&
+ console.warn(`Animation built with the following warnings:${createListOfWarnings(warnings)}`);
+}
+
+export function triggerParsingWarnings(name: string, warnings: string[]): void {
+ NG_DEV_MODE &&
+ console.warn(`Animation parsing for the ${name} trigger presents the following warnings:${
+ createListOfWarnings(warnings)}`);
+}
+
+export function pushUnrecognizedPropertiesWarning(warnings: string[], props: string[]): void {
+ if (ngDevMode && props.length) {
+ warnings.push(
+ `The provided CSS properties are not recognized properties supported for animations: ${
+ props.join(', ')}`);
+ }
+}
diff --git a/packages/animations/browser/test/dsl/animation_spec.ts b/packages/animations/browser/test/dsl/animation_spec.ts
index 330d03ecbcba..c76b4afdaa65 100644
--- a/packages/animations/browser/test/dsl/animation_spec.ts
+++ b/packages/animations/browser/test/dsl/animation_spec.ts
@@ -218,16 +218,28 @@ function createDiv() {
/state\("panfinal", ...\) must define default values for all the following style substitutions: greyColor/);
});
- it('should throw an error if an invalid CSS property is used in the animation', () => {
+ it('should provide a warning if an invalid CSS property is used in the animation', () => {
const steps = [animate(1000, style({abc: '500px'}))];
- expect(() => {
- validateAndThrowAnimationSequence(steps);
- })
- .toThrowError(
- /The provided animation property "abc" is not a supported CSS property for animations/);
+ expect(getValidationWarningsForAnimationSequence(steps)).toEqual([
+ 'The provided CSS properties are not recognized properties supported for animations: abc'
+ ]);
});
+ it('should provide a warning if multiple invalid CSS properties are used in the animation',
+ () => {
+ const steps = [
+ state('state', style({
+ '123': '100px',
+ })),
+ style({abc: '200px'}), animate(1000, style({xyz: '300px'}))
+ ];
+
+ expect(getValidationWarningsForAnimationSequence(steps)).toEqual([
+ 'The provided CSS properties are not recognized properties supported for animations: 123, abc, xyz'
+ ]);
+ });
+
it('should allow a vendor-prefixed property to be used in an animation sequence without throwing an error',
() => {
const steps = [
@@ -1070,12 +1082,20 @@ function invokeAnimationSequence(
function validateAndThrowAnimationSequence(steps: AnimationMetadata|AnimationMetadata[]) {
const driver = new MockAnimationDriver();
const errors: Error[] = [];
- const ast = buildAnimationAst(driver, steps, errors);
+ const ast = buildAnimationAst(driver, steps, errors, []);
if (errors.length) {
throw new Error(errors.join('\n'));
}
}
+function getValidationWarningsForAnimationSequence(steps: AnimationMetadata|
+ AnimationMetadata[]): string[] {
+ const driver = new MockAnimationDriver();
+ const warnings: string[] = [];
+ buildAnimationAst(driver, steps, [], warnings);
+ return warnings;
+}
+
function buildParams(params: {[name: string]: any}): AnimationOptions {
return {params};
}
diff --git a/packages/animations/browser/test/render/transition_animation_engine_spec.ts b/packages/animations/browser/test/render/transition_animation_engine_spec.ts
index 1a041e1fd3d4..5134bd39eacb 100644
--- a/packages/animations/browser/test/render/transition_animation_engine_spec.ts
+++ b/packages/animations/browser/test/render/transition_animation_engine_spec.ts
@@ -732,9 +732,11 @@ function registerTrigger(
element: any, engine: TransitionAnimationEngine, metadata: AnimationTriggerMetadata,
id: string = DEFAULT_NAMESPACE_ID) {
const errors: Error[] = [];
+ const warnings: string[] = [];
const driver = new MockAnimationDriver();
const name = metadata.name;
- const ast = buildAnimationAst(driver, metadata as AnimationMetadata, errors) as TriggerAst;
+ const ast =
+ buildAnimationAst(driver, metadata as AnimationMetadata, errors, warnings) as TriggerAst;
if (errors.length) {
}
const trigger = buildTrigger(name, ast, new NoopAnimationStyleNormalizer());
diff --git a/packages/animations/browser/test/shared.ts b/packages/animations/browser/test/shared.ts
index bcbd55a66486..e699c243dbcc 100644
--- a/packages/animations/browser/test/shared.ts
+++ b/packages/animations/browser/test/shared.ts
@@ -13,16 +13,21 @@ import {buildAnimationAst} from '../src/dsl/animation_ast_builder';
import {AnimationTrigger, buildTrigger} from '../src/dsl/animation_trigger';
import {NoopAnimationStyleNormalizer} from '../src/dsl/style_normalization/animation_style_normalizer';
import {triggerParsingFailed} from '../src/error_helpers';
+import {triggerParsingWarnings} from '../src/warning_helpers';
import {MockAnimationDriver} from '../testing/src/mock_animation_driver';
export function makeTrigger(
name: string, steps: any, skipErrors: boolean = false): AnimationTrigger {
const driver = new MockAnimationDriver();
const errors: Error[] = [];
+ const warnings: string[] = [];
const triggerData = trigger(name, steps);
- const triggerAst = buildAnimationAst(driver, triggerData, errors) as TriggerAst;
+ const triggerAst = buildAnimationAst(driver, triggerData, errors, warnings) as TriggerAst;
if (!skipErrors && errors.length) {
throw triggerParsingFailed(name, errors);
}
+ if (warnings.length) {
+ triggerParsingWarnings(name, warnings);
+ }
return buildTrigger(name, triggerAst, new NoopAnimationStyleNormalizer());
}
diff --git a/packages/common/http/src/jsonp.ts b/packages/common/http/src/jsonp.ts
index e5be9634973f..318c4c8b1fc2 100644
--- a/packages/common/http/src/jsonp.ts
+++ b/packages/common/http/src/jsonp.ts
@@ -21,6 +21,12 @@ import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse, HttpStatusCod
// is shared among all applications on the page.
let nextRequestId: number = 0;
+/**
+ * When a pending