Compare commits

...

75 Commits

Author SHA1 Message Date
Andrew Kushnir 174997150f release: cut the v15.2.1 release 2023-03-01 10:53:10 -08:00
Stephanie Tuerk 439b4d3a89 docs: clarify ActivatedRouter injection location (#49270)
The example in the code snippet below this line of text shows `ActivatedRouter` being injected into a component's constructor. When I read instruction to inject A`ActivatedRouter` into **application's** constructor, I assumed this meant the constructor for `app.component.ts`. 

Editing to clarify/match code example below.
PR Close #49270
2023-02-28 17:13:37 -08:00
Joey Perrott 377d0c9c1e build: update cross-repo angular dependencies (#49268)
See associated pull request for more information.

PR Close #49268
2023-02-28 12:43:42 -08:00
Alan Agius d60ea6ab5a fix(core): update zone.js peerDependencies ranges (#49244)
This ensures that the latest version of zone.js is supported.

PR Close #49244
2023-02-28 11:55:54 -08:00
Matthieu Riegler dafb765926 refactor(core): Drop Symbol.iterator shim (#49207)
We are targeting evergreen browsers, we can drop the shim.

PR Close #49207
2023-02-28 10:05:42 -08:00
Kristiyan Kostadinov 44d095a61c fix(migrations): avoid migrating the same class multiple times in standalone migration (#49245)
If a class is declared in multiple modules, the standalone migration may end up generating invalid code. While declaring a class in multiple modules is an error, it can happen with modules in tests. These changes avoid the issue by using a `Set` to track the classes being migrated.

PR Close #49245
2023-02-28 10:04:55 -08:00
Virginia Dooley 600fd12fda docs: New doc extract from original HTTP doc to be retired. (#48915)
PR Close #48915
2023-02-28 09:32:30 -08:00
Virginia Dooley d100ddfd6e docs: New doc extract from original HTTP doc to be retired. (#48912)
PR Close #48912
2023-02-28 09:31:31 -08:00
Virginia Dooley 1aadabb508 docs: New doc extract from original HTTP doc to be retired. (#48908)
PR Close #48908
2023-02-28 09:30:14 -08:00
Virginia Dooley d252187c45 docs: New document extracted from the original Communicating with backend services using HTTP document, which is to be retired. (#47971)
PR Close #47971
2023-02-28 09:29:13 -08:00
Matthieu Riegler 6d6b76c59f docs(router): Improving linking (#49203)
Add links to @see elements and suggest a more explicit alternative for the depreciation of `CanActivate`.

PR Close #49203
2023-02-28 08:24:11 -08:00
Matthieu Riegler 5f2822ce1f refactor(platform-server): handle #24571 todos (#49233)
This commit removes the remaining TODO(issue/24571) in platform-server code base.

PR Close #49233
2023-02-28 08:19:58 -08:00
Matthieu Riegler acc87a41ce docs: remove TODO from doc example (#49233)
This commit removes a TODO(issue/24571) that leaking into the docs examples

PR Close #49233
2023-02-28 08:19:57 -08:00
Matthieu Riegler 7eec66c449 refactor(elements): handle #24571 todos (#49233)
This commit removes the remaining TODO(issue/24571) in elements code base.

PR Close #49233
2023-02-28 08:19:57 -08:00
Angular Robot 8fc02f56d0 build: update eslint dependencies to v5.54.0 (#49243)
See associated pull request for more information.

PR Close #49243
2023-02-28 08:19:07 -08:00
Angular Robot 313760b851 build: update github/codeql-action action to v2.2.5 (#49242)
See associated pull request for more information.

PR Close #49242
2023-02-28 08:18:22 -08:00
Matthieu Riegler 87252dcbbc docs: improve section title in TOH-pt4 (#49208)
fixes #49165

PR Close #49208
2023-02-28 08:16:39 -08:00
Alan Agius 785ab81a7b ci: add several dependencies to renovate ignore list (#49258)
Add ignored dependencies in Renovate config. These have been collected from the dependency dashboard (https://github.com/angular/angular/issues/46728)

The reason for this change is that Renovate is re-opened dependency updates for ignored PRs.

Example:
https://github.com/angular/angular/pull/49256
https://github.com/angular/angular/pull/47852

PR Close #49258
2023-02-28 08:15:05 -08:00
Virginia Dooley 19a4cdec7d docs: New document extracted from the original Communicating with backend services using HTTP document, which is to be retired. (#47937)
PR Close #47937
2023-02-27 15:24:41 -08:00
Iván Navarro 3062442728 fix(router): add error message when using loadComponent with a NgModule (#49164)
Add a more specific error message when defining a lazy-loaded route using
`loadComponent` and passing it a NgModule instead of a standalone component,
when the user should actually be using `loadChildren`.

PR Close #49164
2023-02-27 10:09:55 -08:00
Doug Parker 138b4936b7 release: bump DevTools version (#49121)
PR Close #49121
2023-02-27 10:07:54 -08:00
Matthieu Riegler af31adb8dd refactor(common): Use isPromise from @angular/core (#49210)
This commit removes `isPromise()` from Common as it's a duplicate function that can be imported from `@angular/core`.

PR Close #49210
2023-02-27 10:03:21 -08:00
Vincent f0e926074d fix(common): make Location.normalize() return the correct path when the base path contains characters that interfere with regex syntax. (#49181)
Fix the function stripping the base path from the URL, as the current implementation uses the base path as part of a regex, which wrongly makes paths fails that contain characters such as a parenthesis (example: C:/Users/MyUser(Test)/project).

Fixes #49179

PR Close #49181
2023-02-27 10:02:33 -08:00
Andrew Kushnir 7ec76dea5f refactor(core): add previous sibling node reference to TNode (#49223)
This commit updates the `TNode` to include a reference to the previous sibling node. Currently, TNode has references to the next sibling and parent nodes, but in followup changes we'd need to have access to previous TNodes (to determine position of the current node).

PR Close #49223
2023-02-27 10:00:26 -08:00
Paul Gschwendtner a852862ff9 build: cleanup unused dependencies (#49227)
Cleans up a few more unused dependencies.

Related: https://github.com/angular/angular/pull/49226

PR Close #49227
2023-02-27 08:06:00 -08:00
Aristeidis Bampakos 6107d2ebbc docs: change link for Angular Projects book (#49198)
PR Close #49198
2023-02-27 08:05:21 -08:00
Aristeidis Bampakos 56f7d1f61a docs: add new edition of Learning Angular book (#49199)
PR Close #49199
2023-02-27 08:04:35 -08:00
Matthieu Riegler 6d912960b9 docs: bump first-app to 15.2 (#49217)
The stackblitz demo of the quick-start guide was broken with incompatible dependencies.
This commit fixes this.

Fixes #49213

PR Close #49217
2023-02-27 08:03:47 -08:00
Andrew Kushnir 8542593c80 refactor(platform-browser): move TransferState init logic into its constructor (#49191)
This commit updates the TransferState class to move its init logic from the `useFactory` function to its constructor. The change is needed to make the init behavior consistent across different injection scenarios and tolerate the issue described in https://github.com/angular/angular/issues/49190.

PR Close #49191
2023-02-24 10:41:17 -08:00
Andrew Kushnir d0fa598fe4 refactor(core): avoid creating DOM nodes before creating a TNode (#49172)
This commit updates the `elementStart` instruction to avoid creating DOM nodes before creating a corresponding TNode. This refactoring is needed to make sure this internal logic is consistent across all instructions.

PR Close #49172
2023-02-24 08:29:06 -08:00
Paul Gschwendtner 04d8b6c61a fix(compiler-cli): do not persist component analysis if template/styles are missing (#49184)
Consider the following scenario:

1. A TS file with a component and templateUrl exists
2. The template file does not exist.
3. First build: ngtsc will properly report the error, via a FatalDiagnosticError
4. The template file is now created
5. Second build: ngtsc still reports the same errror.

ngtsc persists the analysis data of the component and never invalidates
it when the template/style file becomes available later.

This breaks incremental builds and potentially common workflows
where resource files are added later after the TS file is created. This
did surface as an issue in the Angular CLI yet because Webpack requires
users to re-start the process when a new file is added. With ESBuild
this will change and this also breaks incremental builds with
Bazel/Blaze workers.

To fix this, we have a few options:

* Invalidate the analysis when e.g. the template file is missing. Never
  caching it means that it will be re-analyzed on every build iteration.
* Add the resource dependency to ngtsc's incremental file graph. ngtsc
  will then know via `host.getModifiedResources` when the file becomes
  available- and fresh analysis of component would occur.

The first approach is straightforward to implement and was chosen here.
The second approach would allow ngtsc to re-use more of the analysis
when we know that e.g. the template file still not there, but it
increases complexity unnecessarily because there is no **single**
obvious resource path for e.g. a `templateUrl`. The URL is attempted
to be resolved using multiple strategies, such as TS program root dirs,
or there is support for a custom resolution through
`host.resourceNameToFileName`.

It would be possible to determine some candidate paths and add them to
the dependency tracker, but it seems incomplete given possible external
resolvers like `resourceNameToFileName` and also would likely not have
a sufficient-enough impact given that a broken component decorator is
not expected to remain for too long between N incremental build
iterations.

PR Close #49184
2023-02-24 08:24:08 -08:00
Kristiyan Kostadinov 92b0bda9e4 fix(migrations): delete barrel exports in standalone migration (#49176)
Adds some logic to automatically delete `export * from './foo'` style imports. Previously they weren't being picked up, because finding all the references using the language service doesn't include barrel exports.

PR Close #49176
2023-02-24 08:23:02 -08:00
Michael Ziluck d7835e4f1a docs(router): fix code block closing ticks (#49185)
PR Close #49185
2023-02-23 10:37:25 -08:00
Craig Spence e384c69b83 docs(core): Improved README.md (#49171)
PR Close #49171
2023-02-23 10:35:42 -08:00
Angular Robot aa10532458 build: update io_bazel_rules_sass digest to 1f27790 (#49132)
See associated pull request for more information.

PR Close #49132
2023-02-23 10:34:43 -08:00
Alex Rickabaugh 7677963bd9 release: cut the v15.2.0 release 2023-02-22 13:04:10 -08:00
cexbrayat 03fcb36cfd fix(migrations): migrate HttpClientModule to provideHttpClient() (#48949)
The `standalone-bootstrap` migration now migrates `HttpClientModule` imports to `provideHttpClient(withInterceptorsFromDi())` instead of `importProvidersFrom(HttpClientModule)`.

The `withInterceptorsFromDi()` feature is added to make sure class-based interceptors still works if there are any in the application.

Fixes #48948

PR Close #48949
2023-02-22 11:30:01 -08:00
Matthieu Riegler 2f0856070b docs: escape script tag to prevent rendering problems (#49152)
un-commented tags are responsible for rendering problem of markdown document. This commit fixes the http make jsonp request guide.

fixes #49151

PR Close #49152
2023-02-22 11:29:33 -08:00
Angular Robot 7b4d3c036d build: update actions/cache digest to 69d9d44 (#49148)
See associated pull request for more information.

PR Close #49148
2023-02-21 13:36:41 -08:00
Kristiyan Kostadinov c98c6a8452 fix(migrations): don't copy animations modules into the imports of test components (#49147)
Since we have less information about how to copy test components, we copy all the `imports` from the `configureTestingModule` call into the component's `imports`. It fixes some tests, but it can cause issues with animations modules, because they throw errors if they're imported multiple times.

These changes add an exception for animations modules imported in testing modules.

PR Close #49147
2023-02-21 13:35:43 -08:00
Angular Robot 630193543f build: update eslint dependencies to v5.53.0 (#49146)
See associated pull request for more information.

PR Close #49146
2023-02-21 13:24:16 -08:00
Walid Bouguima cff84d00cf docs(router): canMatch route guard method signature update (#49140)
Very cosmetic, but it might be necessary : 

I updated the ```canAccess``` method second signature name and type in the provided sample. As it stands, it triggers `TS(2345):  Argument of type 'Route' is not assignable to parameter of type 'string'`.
PR Close #49140
2023-02-21 13:23:08 -08:00
Kristiyan Kostadinov ba7a757cc5 fix(migrations): return correct alias when conflicting import exists (#49139)
Fixes that the `ImportManager` was returning the `propertyName` instead of the `name` when there's an import with a conflicting identifier.

PR Close #49139
2023-02-21 13:17:21 -08:00
Kristiyan Kostadinov ecb309152f build: update AIO to TypeScript 4.9 (#49135)
Updates AIO to the latest stable version of TypeScript as a prerequisite to drop support for 4.8.

PR Close #49135
2023-02-21 13:16:36 -08:00
Kalbarczyk 2fd81940d3 docs: add NG Poland 2023 and JS Poland 2023 to events (#48972)
docs: add NG Poland 2023 and JS Poland 2023 to events
PR Close #48972
2023-02-21 13:16:06 -08:00
Paul Gschwendtner 0950ef6a49 ci: attempt to fix node windows job flakiness (#49142)
Ocasionally Node is randomly not installed in the Windows job. We've
been trying to debug this for a while. With the additonal debug
information it looks like in some situations the NodeJS folder from
NVM is not even present.

One idea is to use `nvm on` to ensure NVM is actually enabled. It's
unclear why it would be `off` at VM start, but it's worth giving a try.

Trying `nvm off` via SSH yields similar issues as we saw in the flaky
workflows:
https://app.circleci.com/pipelines/github/angular/angular/56160/workflows/2f1b80b3-29c7-4274-804e-cb994c20aff0/jobs/1287228

PR Close #49142
2023-02-20 15:16:54 +00:00
Paul Gschwendtner e69ba26c55 ci: fix rbe initialization failing in windows job (#49142)
The RBE init script from dev-infra is using ES2020 but
the image NodeJS version does not support e.g. nullish coalescing.

PR Close #49142
2023-02-20 15:16:54 +00:00
Joey Perrott 61878883ed build: update to latest remote build execution setup (#49133)
Update to the latest RBE setup after key rotation.

PR Close #49133
2023-02-18 02:42:28 +00:00
Andrew Kushnir 8ed653b93f ci: update AIO payload size tracking (#49130)
This commit updates a golden file to decrease an expected payload size.

PR Close #49130
2023-02-17 11:55:02 -08:00
cexbrayat b76b176733 refactor(http): simplify HttpClientModule XSRF configuration (#48957)
The current configuration of `HttpClientModule` explicitely enables the XSRF configuration,
but this XSRF configuration is the one used by default by `provideHttpClient`.

See https://github.com/angular/angular/blob/main/packages/common/http/src/provider.ts#L50-L98

PR Close #48957
2023-02-17 11:17:55 -08:00
Angular Robot f8a932afbb build: update io_bazel_rules_sass digest to a2efcd6 (#49122)
See associated pull request for more information.

PR Close #49122
2023-02-17 11:11:01 -08:00
Alan Agius d784986cd3 Revert "fix(platform-server): insert transfer state script before other script tags (#48868)" (#49112)
This reverts commit 2fc5b70fce as this change is no longer needed since `type=module` script are deferred by default. Which causes the transfer data to be queried after the browser has finished parsing the DOM.

PR Close #49112
2023-02-17 11:09:22 -08:00
Matthieu Riegler 31a71a0f89 refactor: remove unnecessary file (#49042)
view had a single export from a file located in the same package, no need to keep it.

PR Close #49042
2023-02-17 11:08:34 -08:00
Alan Agius 3d8efece03 test: remove cli-hello-world-ivy-compat integration test (#49111)
This now is the same as `cli-hello-world` since this test does not use View Engine

PR Close #49111
2023-02-17 11:07:28 -08:00
Virginia Dooley 345817f427 docs: New doc HTTP client - Pass metadata to interceptors. (#48918)
PR Close #48918
2023-02-17 11:06:58 -08:00
Virginia Dooley 24b6eea0a9 docs: New doc HTTP client - Test requests (#48917)
PR Close #48917
2023-02-17 11:06:04 -08:00
Virginia Dooley d457ffc408 docs: New doc HTTP client - Security: Cross-Site Request Forgery (XSRF) protection (#48916)
PR Close #48916
2023-02-17 11:05:27 -08:00
Virginia Dooley a378267117 docs: New doc extract from original HTTP doc to be retired. (#48913)
PR Close #48913
2023-02-17 11:04:51 -08:00
Virginia Dooley 11f8f8d8e4 docs: new doc extract from original HTTP doc to be retired. (#48911)
PR Close #48911
2023-02-17 11:04:22 -08:00
Virginia Dooley 4811850607 docs: new doc extract from original HTTP doc which is to be retired. (#48905)
PR Close #48905
2023-02-17 11:03:49 -08:00
Virginia Dooley 5d6440947a docs: New document extracted from the original Communicating with backend services using HTTP document, which is to be retired. (#47965)
PR Close #47965
2023-02-17 11:03:06 -08:00
Andrew Kushnir 25e220a23a fix(platform-server): avoid duplicate TransferState info after renderApplication call (#49094)
This commit updates the `renderApplication` function to avoid duplicate serialization of the data from the `TransferState` class. The `renderApplication` function imports providers from the `ServerModule`, which already include the `TransferState` serialization providers, but the same providers were also included inside the `renderApplication` as well, which led to the duplication.

PR Close #49094
2023-02-16 16:00:30 -08:00
Matthieu Riegler c95766ed12 refactor: restore tree shaking for a warning message (#49064)
`getInheritedInjectableDef` logs a depreciation warning that can be tree shaked in prod. Let's do it.

PR Close #49064
2023-02-16 15:55:44 -08:00
Matthieu Riegler 43e9301783 docs: fixing typo in angular compiler options (#49085)
The strictness flags that this "option" instead of "open".

Fixes #49078

PR Close #49085
2023-02-16 15:54:15 -08:00
Andrew Scott e63fb8031a refactor(router): Remove warning for an invalid ActivatedRoute (#49117)
This warning was put in place to surface an error when developers use an ActivatedRoute
with an invalid structure in tests. However, there isn't a clear path to fixing it and
the tests continue to pass (ideally - that's what the recovery code is there for).
Rather than surfacing an error that developers would be confused by and likely ignore,
it would be better to not warn at all unless there is a clearer action to take to
resolve the issue.

resolves #49116

PR Close #49117
2023-02-16 15:53:29 -08:00
John Manners 25bed17c5f docs: fix typo in toh-pt5.md (#49108)
Line 254 - change 'liberate' to 'liberates'.
PR Close #49108
2023-02-16 15:52:33 -08:00
John Manners 117af7d8b5 docs: improve sentence in toh-pt5.md (#49109)
Line 302 - improve clarity.
PR Close #49109
2023-02-16 15:51:27 -08:00
Ikko Eltociear Ashimine ee7a9248a8 docs: fix typo in router-tutorial-toh.md (#49095)
non-existant -> non-existent
PR Close #49095
2023-02-16 15:50:56 -08:00
Ilyass e1607cdbd4 docs(core): typo adding missing comma (#49088)
PR Close #49088
2023-02-16 15:49:09 -08:00
Kristiyan Kostadinov b06a1d0349 docs: add guide for standalone migration (#49044)
Adds a guide describing how to use the standalone migration schematic. The new guide is grouped together with the existing standalone guide.

PR Close #49044
2023-02-16 15:33:18 -08:00
mgechev a814ed1382 docs: close the developer survey (#49092)
PR Close #49092
2023-02-16 15:32:45 -08:00
Paul Gschwendtner 1a7444f99f build: avoid unnecessary re-evaluation of starlark code (#49106)
The experimental allow tags propagation flag is a `BuildLanguage` option
and causes all Starlark code to be re-invoked. This causes a slow-down
when switching between bazel query/ bazel build because the option is not set for `bazel query`.

We fix it by applying the option to all commands, using `common`.

PR Close #49106
2023-02-16 15:31:56 -08:00
Alan Agius bf4ad38117 fix(platform-browser): remove styles from DOM of destroyed components (#48298)
Currently style of components using `encapsulation`, `None` or `Emulated` will not be removed from the DOM once the component get destroyed.

This change addresses this by keeping track of the number of times a component is rendered, when the component is destroyed the counter is decreased and once this reaches zero the style element is removed from the DOM.

Currently, this new behaviour is on opt-in bases, but it will be changed in the next major version.

To opt-in, set the `REMOVE_STYLES_ON_COMPONENT_DESTROY` DI token to `true`.

Example
```ts
@NgModule({
 declarations: [
   AppComponent,
 ],
 imports: [
   BrowserModule
 ],
 providers: [
   { provide: REMOVE_STYLES_ON_COMPONENT_DESTROY, useValue: true }
 ],
 bootstrap: [AppComponent]
})
export class AppModule { }
```

Closes #16670

PR Close #48298
2023-02-16 15:31:24 -08:00
Kristiyan Kostadinov 86d4016d23 refactor(compiler): remove remaining usage of getMutableClone (#49070)
Uses an alternate approach of preserving default imports that doesn't involve the `getMutableClone` function that is being removed in TypeScript 5.0.

The alternate approach was already used in the downlevel transform and it works by patching the EmitResolver of the current transformation context to tell TypeScript to preserve the import.

PR Close #49070
2023-02-16 15:30:53 -08:00
Pawel Kozlowski 440b51b7af release: cut the v15.2.0-rc.0 release 2023-02-15 18:45:14 +01:00
151 changed files with 6218 additions and 4849 deletions
+3 -2
View File
@@ -134,9 +134,10 @@ build:remote --project_id=internal-200822
build:remote --remote_cache=remotebuildexecution.googleapis.com
build:remote --remote_executor=remotebuildexecution.googleapis.com
# Ensure that tags like "no-remote-exec" get propagated to actions created by rules.
# Ensure that tags like "no-remote-exec" get propagated to actions created by rules,
# even if the rule implementation does not explicitly pass them to the execution requirements.
# https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation
build --experimental_allow_tags_propagation
common --experimental_allow_tags_propagation
# Disable network access in the sandbox by default. To enable network access
# for a particular target, use:
+12 -10
View File
@@ -74,7 +74,7 @@ var_9: &only_release_branches
# https://circleci.com/developer/orbs.
orbs:
node: circleci/node@5.0.1
devinfra: angular/dev-infra@1.0.6
devinfra: angular/dev-infra@1.0.8
# Executor Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
@@ -595,22 +595,16 @@ jobs:
executor: windows-executor
steps:
- checkout
- save_month_to_file
- capture_patches_for_cache_key
- init_environment
# Setup the proper NodeJS version. The image comes pre-installed with `nvm-windows`.
# Note: This happens immediately after checkout because environment initialization logic
# might rely on Node and shouldn't use the older image-provided NodeJS version.
- run:
name: Install NodeJS version from `.nvmrc`.
command: |
nvm on
nvm install $(cat .nvmrc)
nvm use $(cat .nvmrc)
- restore_cache:
keys:
- *cache_key_win
- *cache_key_win_fallback
- run:
name: Debug information for flakiness of Windows job
command: |
@@ -621,6 +615,14 @@ jobs:
echo "Path: $PATH"
yarn node -v
- save_month_to_file
- capture_patches_for_cache_key
- init_environment
- restore_cache:
keys:
- *cache_key_win
- *cache_key_win_fallback
# Install project dependencies, and install Bazelisk globally. This is necessary as
# Windows might error when `bazel` is invoked from the project node modules. The Bazel
# invocation might modify the symlinked project `node_modules` again, causing failures.
+1 -1
View File
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
runs:
using: 'composite'
steps:
- uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3
- uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3
with:
path: |
./node_modules/
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: ./.github/actions/yarn-install
- uses: angular/dev-infra/github-actions/setup-bazel-remote-exec@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/setup-bazel-remote-exec@82bf866dd936abf5e6826a282d8da6e720dd1aa2
with:
bazelrc: ./.bazelrc.user
@@ -34,7 +34,7 @@ jobs:
# the number of concurrent actions is determined based on the host resources.
- run: bazel build //aio:build --jobs=32 --announce_rc --verbose_failures
- uses: angular/dev-infra/github-actions/deploy-previews/pack-and-upload-artifact@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/deploy-previews/pack-and-upload-artifact@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
workflow-artifact-name: 'aio'
pull-number: '${{github.event.pull_request.number}}'
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
npx -y firebase-tools@latest target:clear --project ${{env.PREVIEW_PROJECT}} hosting aio
npx -y firebase-tools@latest target:apply --project ${{env.PREVIEW_PROJECT}} hosting aio ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/deploy-previews/upload-artifacts-to-firebase@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/deploy-previews/upload-artifacts-to-firebase@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'aio'
@@ -16,6 +16,6 @@ jobs:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/branch-manager@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: angular/dev-infra/github-actions/commit-message-based-labels@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/commit-message-based-labels@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: angular/dev-infra/github-actions/post-approval-changes@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/post-approval-changes@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -14,6 +14,6 @@ jobs:
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/feature-request@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/feature-request@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- uses: angular/dev-infra/github-actions/google-internal-tests@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/google-internal-tests@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
run-tests-guide-url: http://go/angular/g3sync
github-token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -14,6 +14,6 @@ jobs:
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/lock-closed@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/lock-closed@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@accc3c96df0d40ff639070132acb0699a58bfcf8
- uses: angular/dev-infra/github-actions/unified-status-check@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
uses: github/codeql-action/upload-sarif@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5
with:
sarif_file: results.sarif
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
env:
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
- name: Create a PR (if necessary)
uses: angular/dev-infra/github-actions/create-pr-for-changes@accc3c96df0d40ff639070132acb0699a58bfcf8
uses: angular/dev-infra/github-actions/create-pr-for-changes@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
branch-prefix: update-cli-help
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
- name: Generate `events.json`
run: node aio/scripts/generate-events/index.mjs --ignore-invalid-dates
- name: Create a PR (if necessary)
uses: angular/dev-infra/github-actions/create-pr-for-changes@accc3c96df0d40ff639070132acb0699a58bfcf8
uses: angular/dev-infra/github-actions/create-pr-for-changes@c12a0ca820b97029279dbc3c71c25da5de5bab26
with:
branch-prefix: docs-update-events
pr-title: 'docs: update events'
+2
View File
@@ -350,6 +350,7 @@ groups:
'aio/content/guide/singleton-services.md',
'aio/content/guide/sharing-ngmodules.md',
'aio/content/guide/standalone-components.md',
'aio/content/guide/standalone-migration.md',
'aio/content/guide/structural-directives.md',
'aio/content/examples/structural-directives/**/{*,.*}',
'aio/content/guide/svg-in-templates.md',
@@ -416,6 +417,7 @@ groups:
'aio/content/guide/http-request-data-from-server.md',
'aio/content/guide/http-security-xsrf-protection.md',
'aio/content/guide/http-send-data-to-server.md',
'aio/content/guide/http-server-communication.md',
'aio/content/guide/http-setup-server-communication.md',
'aio/content/guide/http-test-requests.md',
'aio/content/guide/http-track-show-request-progress.md',
+82 -116
View File
@@ -1,16 +1,42 @@
<a name="15.1.5"></a>
# 15.1.5 (2023-02-15)
### forms
<a name="15.2.1"></a>
# 15.2.1 (2023-03-01)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [5f2a3edcf2](https://github.com/angular/angular/commit/5f2a3edcf27cfe6ffc6ae4d67ca2b47953f26336) | fix | Make radio buttons respect `[attr.disabled]` ([#48864](https://github.com/angular/angular/pull/48864)) |
| [f0e926074d](https://github.com/angular/angular/commit/f0e926074df189b3e3ca361a6a3bcd852c05e010) | fix | make Location.normalize() return the correct path when the base path contains characters that interfere with regex syntax. ([#49181](https://github.com/angular/angular/pull/49181)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [04d8b6c61a](https://github.com/angular/angular/commit/04d8b6c61a0d0a2d61b9202d09774f3ab347e82f) | fix | do not persist component analysis if template/styles are missing ([#49184](https://github.com/angular/angular/pull/49184)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [d60ea6ab5a](https://github.com/angular/angular/commit/d60ea6ab5a22cb4f3677e34d0d7f6be0c3fe23fe) | fix | update zone.js peerDependencies ranges ([#49244](https://github.com/angular/angular/pull/49244)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [44d095a61c](https://github.com/angular/angular/commit/44d095a61cb340ea1f5e0a19370ea839378b02c3) | fix | avoid migrating the same class multiple times in standalone migration ([#49245](https://github.com/angular/angular/pull/49245)) |
| [92b0bda9e4](https://github.com/angular/angular/commit/92b0bda9e4e7117552f929bf86acfc0ae65779a1) | fix | delete barrel exports in standalone migration ([#49176](https://github.com/angular/angular/pull/49176)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [3062442728](https://github.com/angular/angular/commit/30624427289ad65bdbabd865d028146753c3a97a) | fix | add error message when using loadComponent with a NgModule ([#49164](https://github.com/angular/angular/pull/49164)) |
## Special Thanks
AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler, PaloMiklo and Paul Gschwendtner
Alan Agius, Andrew Kushnir, Aristeidis Bampakos, Craig Spence, Doug Parker, Iván Navarro, Joey Perrott, Kristiyan Kostadinov, Matthieu Riegler, Michael Ziluck, Paul Gschwendtner, Stephanie Tuerk, Vincent and Virginia Dooley
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.2.0-next.4"></a>
# 15.2.0-next.4 (2023-02-08)
<a name="15.2.0"></a>
# 15.2.0 (2023-02-22)
## Deprecations
###
- Class and `InjectionToken` guards and resolvers are
deprecated. Instead, write guards as plain JavaScript functions and
inject dependencies with `inject` from `@angular/core`.
###
| Commit | Type | Description |
| -- | -- | -- |
| [926c35f4ac](https://github.com/angular/angular/commit/926c35f4ac70f5e4d142e545d6d056dd67aac97b) | docs | Deprecate class and InjectionToken and resolvers ([#47924](https://github.com/angular/angular/pull/47924)) |
### common
| Commit | Type | Description |
| -- | -- | -- |
@@ -19,27 +45,75 @@ AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler,
| Commit | Type | Description |
| -- | -- | -- |
| [0cf11167f1](https://github.com/angular/angular/commit/0cf11167f13108992ec781e88ab2a7d1fc7f5a0d) | fix | incorrectly detecting forward refs when symbol already exists in file ([#48988](https://github.com/angular/angular/pull/48988)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [a154db8a81](https://github.com/angular/angular/commit/a154db8a81cbdfed8c3d0db1e2a5bf43aa3e0bbf) | feat | add ng generate schematic to convert declarations to standalone ([#48790](https://github.com/angular/angular/pull/48790)) |
| [345e737daa](https://github.com/angular/angular/commit/345e737daa7b9f635a4c2923358e5e765f716434) | feat | add ng generate schematic to convert to standalone bootstrapping APIs ([#48848](https://github.com/angular/angular/pull/48848)) |
| [e7318fc758](https://github.com/angular/angular/commit/e7318fc758d9e64d1a7f60a2c7071a769b73e7d8) | feat | add ng generate schematic to remove unnecessary modules ([#48832](https://github.com/angular/angular/pull/48832)) |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [4ae384fd61](https://github.com/angular/angular/commit/4ae384fd619a13eaadf737d08a97f07e1f6b273c) | feat | Allow auto-imports of a pipe via quick fix when its selector is used, both directly and via reexports. ([#48354](https://github.com/angular/angular/pull/48354)) |
| [141333411e](https://github.com/angular/angular/commit/141333411e67769d752c7162e4cb03376022f5e1) | feat | Introduce a new NgModuleIndex, and use it to suggest re-exports. ([#48354](https://github.com/angular/angular/pull/48354)) |
| [d0145033bd](https://github.com/angular/angular/commit/d0145033bd11eccd16fa8b61ba9170037d0c62b3) | fix | generate forwardRef for same file imports ([#48898](https://github.com/angular/angular/pull/48898)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [2796230e95](https://github.com/angular/angular/commit/2796230e953eb8c29d6227a1a3858f5f08a8f200) | fix | add `enum` in `mode` option in `standalone` schema ([#48851](https://github.com/angular/angular/pull/48851)) |
| [816e76a578](https://github.com/angular/angular/commit/816e76a5789b041fee78ddd278c0e0d19b9a617a) | fix | automatically prune root module after bootstrap step ([#49030](https://github.com/angular/angular/pull/49030)) |
| [bdbf21d04b](https://github.com/angular/angular/commit/bdbf21d04ba74a6f73469242076d6ce697c57edf) | fix | avoid generating imports with forward slashes ([#48993](https://github.com/angular/angular/pull/48993)) |
| [32cf4e5cb9](https://github.com/angular/angular/commit/32cf4e5cb989f365296d519dddf72fb38ca47c40) | fix | avoid internal modules when generating imports ([#48958](https://github.com/angular/angular/pull/48958)) |
| [521ccfbe6c](https://github.com/angular/angular/commit/521ccfbe6ce9af1a7ddd6ab5e70151b7198f82ef) | fix | avoid interrupting the migration if language service lookup fails ([#49010](https://github.com/angular/angular/pull/49010)) |
| [a40cd47aa7](https://github.com/angular/angular/commit/a40cd47aa7ebccfbeeb26e397e03f1372aa10a55) | fix | avoid modifying testing modules without declarations ([#48921](https://github.com/angular/angular/pull/48921)) |
| [1afa6ed322](https://github.com/angular/angular/commit/1afa6ed3227e784e3fe2b4b31443961589cb6332) | fix | don't add ModuleWithProviders to standalone test components ([#48987](https://github.com/angular/angular/pull/48987)) |
| [c98c6a8452](https://github.com/angular/angular/commit/c98c6a845286b9b89daf275a9c4a2bdbc7ad77a7) | fix | don't copy animations modules into the imports of test components ([#49147](https://github.com/angular/angular/pull/49147)) |
| [8389557848](https://github.com/angular/angular/commit/83895578488bd35c7e47609f092907eb0f53f435) | fix | don't copy unmigrated declarations into imports array ([#48882](https://github.com/angular/angular/pull/48882)) |
| [f82bdc4b01](https://github.com/angular/angular/commit/f82bdc4b01f93a7103870449d37da61cc4c4f179) | fix | don't delete classes that may provide dependencies transitively ([#48866](https://github.com/angular/angular/pull/48866)) |
| [759db12e0b](https://github.com/angular/angular/commit/759db12e0b618fcb51f4cb141adeb49bfa495a60) | fix | duplicated comments on migrated classes ([#48966](https://github.com/angular/angular/pull/48966)) |
| [ba38178d19](https://github.com/angular/angular/commit/ba38178d1918d413f9c2260c40eb6542eadfddba) | fix | generate forwardRef for same file imports ([#48898](https://github.com/angular/angular/pull/48898)) |
| [03fcb36cfd](https://github.com/angular/angular/commit/03fcb36cfd36731028bf288f156e16cb8ac4c758) | fix | migrate HttpClientModule to provideHttpClient() ([#48949](https://github.com/angular/angular/pull/48949)) |
| [2de6dae16d](https://github.com/angular/angular/commit/2de6dae16d4b0b83f0517a3033cda44ba44154ed) | fix | migrate RouterModule.forRoot with a config object to use features ([#48935](https://github.com/angular/angular/pull/48935)) |
| [770191cf1f](https://github.com/angular/angular/commit/770191cf1f1254546625dfa7a882b716c3f0aab3) | fix | migrate tests when switching to standalone bootstrap API ([#48987](https://github.com/angular/angular/pull/48987)) |
| [c7926b5773](https://github.com/angular/angular/commit/c7926b57730c23f765a00d3dd9f92079c95e87e0) | fix | move standalone migrations into imports ([#48987](https://github.com/angular/angular/pull/48987)) |
| [65c74ed93e](https://github.com/angular/angular/commit/65c74ed93e04cb560c27838d440c6aa7a9859a4e) | fix | normalize paths to posix ([#48850](https://github.com/angular/angular/pull/48850)) |
| [6377487b1a](https://github.com/angular/angular/commit/6377487b1ab7679cef9a44f88440fe5e8eb97480) | fix | only exclude bootstrapped declarations from initial standalone migration ([#48987](https://github.com/angular/angular/pull/48987)) |
| [e9e4449a43](https://github.com/angular/angular/commit/e9e4449a43430e026e61b0f05ebd32dd830fa916) | fix | preserve tsconfig in standalone migration ([#48987](https://github.com/angular/angular/pull/48987)) |
| [ffad1b49d9](https://github.com/angular/angular/commit/ffad1b49d95ab90637e7184f92cb5136d490d865) | fix | reduce number of files that need to be checked ([#48987](https://github.com/angular/angular/pull/48987)) |
| [ba7a757cc5](https://github.com/angular/angular/commit/ba7a757cc5a2f3f942adcbabdcd5b7aef33ea493) | fix | return correct alias when conflicting import exists ([#49139](https://github.com/angular/angular/pull/49139)) |
| [49a7c9f94a](https://github.com/angular/angular/commit/49a7c9f94ae8f89907da8b3620242e62f87ec5a4) | fix | standalone migration incorrectly throwing path error for multi app projects ([#48958](https://github.com/angular/angular/pull/48958)) |
| [584976e6c8](https://github.com/angular/angular/commit/584976e6c8a783d40578ab191132673300394a52) | fix | support --defaults in standalone migration ([#48921](https://github.com/angular/angular/pull/48921)) |
| [03f47ac901](https://github.com/angular/angular/commit/03f47ac9019eddbcb373b50c41bc6f523293ece1) | fix | use consistent quotes in generated imports ([#48876](https://github.com/angular/angular/pull/48876)) |
| [ebae506d89](https://github.com/angular/angular/commit/ebae506d894a90c38e0f2dd1e948acabdb0fdf2e) | fix | use import remapper in root component ([#49046](https://github.com/angular/angular/pull/49046)) |
| [40c976c909](https://github.com/angular/angular/commit/40c976c90975878852a87b7722076eb78944098b) | fix | use NgForOf instead of NgFor ([#49022](https://github.com/angular/angular/pull/49022)) |
| [4ac25b2aff](https://github.com/angular/angular/commit/4ac25b2affab4f959ad8c111f1e429a05b435422) | perf | avoid re-traversing nodes when resolving bootstrap call dependencies ([#49010](https://github.com/angular/angular/pull/49010)) |
| [26cb7ab2e6](https://github.com/angular/angular/commit/26cb7ab2e6ac9b61904361a8a544467b69eef3f3) | perf | speed up language service lookups ([#49010](https://github.com/angular/angular/pull/49010)) |
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [bf4ad38117](https://github.com/angular/angular/commit/bf4ad3811762d9ba43d18c3360d014a9ceb06b4d) | fix | remove styles from DOM of destroyed components ([#48298](https://github.com/angular/angular/pull/48298)) |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [25e220a23a](https://github.com/angular/angular/commit/25e220a23ab90520efc65f05cd9b7a22db582b87) | fix | avoid duplicate TransferState info after renderApplication call ([#49094](https://github.com/angular/angular/pull/49094)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [31b94c762f](https://github.com/angular/angular/commit/31b94c762fc91ab6cabe08ea6812780fdcf92a64) | feat | Add a withNavigationErrorHandler feature to provideRouter ([#48551](https://github.com/angular/angular/pull/48551)) |
| [dedac8d3f7](https://github.com/angular/angular/commit/dedac8d3f73ebf4f05b773454e2a22ab5fa4bf7c) | feat | Add test helper for trigger navigations in tests ([#48552](https://github.com/angular/angular/pull/48552)) |
## Special Thanks
Alex Castle, Andrew Scott, Dylan Hunn, Jessica Janiuk, Kian Yang Lee, Kristiyan Kostadinov, Matthieu Riegler, Pawel Kozlowski, Redouane Bekkouche, Simona Cotin and Walid Bouguima
Alan Agius, Alex Castle, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Dylan Hunn, Ikko Eltociear Ashimine, Ilyass, Jessica Janiuk, Joey Perrott, John Manners, Kalbarczyk, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Pawel Kozlowski, Virginia Dooley, Walid Bouguima, cexbrayat and mgechev
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.1.5"></a>
# 15.1.5 (2023-02-15)
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [5f2a3edcf2](https://github.com/angular/angular/commit/5f2a3edcf27cfe6ffc6ae4d67ca2b47953f26336) | fix | Make radio buttons respect `[attr.disabled]` ([#48864](https://github.com/angular/angular/pull/48864)) |
## Special Thanks
AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler, PaloMiklo and Paul Gschwendtner
<!-- CHANGELOG SPLIT MARKER -->
@@ -82,73 +156,6 @@ Alan Agius, Besim Gürbüz, Brecht Billiet, Dario Piotrowicz, Dylan Hunn, Iván
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.2.0-next.3"></a>
# 15.2.0-next.3 (2023-02-02)
### animations
| Commit | Type | Description |
| -- | -- | -- |
| [b4187548ca](https://github.com/angular/angular/commit/b4187548cae42f25624bf7ab7ad0cca121242de7) | fix | fix non-animatable warnings for easing ([#48583](https://github.com/angular/angular/pull/48583)) |
### common
| Commit | Type | Description |
| -- | -- | -- |
| [a055196c55](https://github.com/angular/angular/commit/a055196c55c57de281ff7167cd4dfb3df783c337) | fix | warn if using ngSrcset without a configured image loader ([#48804](https://github.com/angular/angular/pull/48804)) |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [06e161f2dd](https://github.com/angular/angular/commit/06e161f2dd78b42ad1938578f4d730a41404a24f) | fix | incorrect code when non-null assertion is used after a safe access ([#48801](https://github.com/angular/angular/pull/48801)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [345e737daa](https://github.com/angular/angular/commit/345e737daa7b9f635a4c2923358e5e765f716434) | feat | add ng generate schematic to convert to standalone bootstrapping APIs ([#48848](https://github.com/angular/angular/pull/48848)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [2796230e95](https://github.com/angular/angular/commit/2796230e953eb8c29d6227a1a3858f5f08a8f200) | fix | add `enum` in `mode` option in `standalone` schema ([#48851](https://github.com/angular/angular/pull/48851)) |
| [a40cd47aa7](https://github.com/angular/angular/commit/a40cd47aa7ebccfbeeb26e397e03f1372aa10a55) | fix | avoid modifying testing modules without declarations ([#48921](https://github.com/angular/angular/pull/48921)) |
| [8389557848](https://github.com/angular/angular/commit/83895578488bd35c7e47609f092907eb0f53f435) | fix | don't copy unmigrated declarations into imports array ([#48882](https://github.com/angular/angular/pull/48882)) |
| [f82bdc4b01](https://github.com/angular/angular/commit/f82bdc4b01f93a7103870449d37da61cc4c4f179) | fix | don't delete classes that may provide dependencies transitively ([#48866](https://github.com/angular/angular/pull/48866)) |
| [04e0ac3d7c](https://github.com/angular/angular/commit/04e0ac3d7ca89bf38d4d2a38b7ca120d9b7b96ae) | fix | migration host incorrectly reading empty files ([#48849](https://github.com/angular/angular/pull/48849)) |
| [65c74ed93e](https://github.com/angular/angular/commit/65c74ed93e04cb560c27838d440c6aa7a9859a4e) | fix | normalize paths to posix ([#48850](https://github.com/angular/angular/pull/48850)) |
| [584976e6c8](https://github.com/angular/angular/commit/584976e6c8a783d40578ab191132673300394a52) | fix | support --defaults in standalone migration ([#48921](https://github.com/angular/angular/pull/48921)) |
| [03f47ac901](https://github.com/angular/angular/commit/03f47ac9019eddbcb373b50c41bc6f523293ece1) | fix | use consistent quotes in generated imports ([#48876](https://github.com/angular/angular/pull/48876)) |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [2fc5b70fce](https://github.com/angular/angular/commit/2fc5b70fcedb8ac35b825b245c0ae394dc125244) | fix | insert transfer state `script` before other `script` tags ([#48868](https://github.com/angular/angular/pull/48868)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [dedac8d3f7](https://github.com/angular/angular/commit/dedac8d3f73ebf4f05b773454e2a22ab5fa4bf7c) | feat | Add test helper for trigger navigations in tests ([#48552](https://github.com/angular/angular/pull/48552)) |
| [16ef770db8](https://github.com/angular/angular/commit/16ef770db803ce4037a90c72477da412642dfb33) | fix | Handle routerLink directive on svg anchors. ([#48857](https://github.com/angular/angular/pull/48857)) |
## Special Thanks
Alan Agius, Andrew Scott, Besim Gürbüz, Brecht Billiet, Dario Piotrowicz, Dylan Hunn, Iván Navarro, Kristiyan Kostadinov, Matthieu Riegler, Onkar Ruikar, Payam Valadkhan, Santosh Yadav, Virginia Dooley and Walid Bouguima
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.2.0-next.2"></a>
# 15.2.0-next.2 (2023-01-25)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [bc8cfa2552](https://github.com/angular/angular/commit/bc8cfa25520c3b8b0a506c69a269e42a63f097d6) | fix | handle css selectors with space after an escaped character. ([#48558](https://github.com/angular/angular/pull/48558)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [4da1f2948c](https://github.com/angular/angular/commit/4da1f2948cc67b68527a91579ee14bb1a22b0c58) | fix | resolve deprecation warning ([#48812](https://github.com/angular/angular/pull/48812)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [a154db8a81](https://github.com/angular/angular/commit/a154db8a81cbdfed8c3d0db1e2a5bf43aa3e0bbf) | feat | add ng generate schematic to convert declarations to standalone ([#48790](https://github.com/angular/angular/pull/48790)) |
| [e7318fc758](https://github.com/angular/angular/commit/e7318fc758d9e64d1a7f60a2c7071a769b73e7d8) | feat | add ng generate schematic to remove unnecessary modules ([#48832](https://github.com/angular/angular/pull/48832)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [4dcab333ae](https://github.com/angular/angular/commit/4dcab333ae009f923cf3864e20ee47e43d32dfe6) | fix | 'createUrlTreeFromSnapshot' with empty paths and named outlets ([#48734](https://github.com/angular/angular/pull/48734)) |
## Special Thanks
Alan Agius, AleksanderBodurri, Andrew Kushnir, Andrew Scott, Charles Lyding, Dylan Hunn, Jessica Janiuk, JoostK, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Payam Valadkhan, Virginia Dooley, Yann Thomas LE MOIGNE and dario-piotrowicz
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.1.2"></a>
# 15.1.2 (2023-01-25)
### compiler
@@ -168,36 +175,6 @@ Alan Agius, AleksanderBodurri, Andrew Kushnir, Andrew Scott, Charles Lyding, Dyl
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.2.0-next.1"></a>
# 15.2.0-next.1 (2023-01-18)
## Deprecations
###
- Class and `InjectionToken` guards and resolvers are
deprecated. Instead, write guards as plain JavaScript functions and
inject dependencies with `inject` from `@angular/core`.
###
| Commit | Type | Description |
| -- | -- | -- |
| [926c35f4ac](https://github.com/angular/angular/commit/926c35f4ac70f5e4d142e545d6d056dd67aac97b) | docs | Deprecate class and InjectionToken and resolvers ([#47924](https://github.com/angular/angular/pull/47924)) |
### common
| Commit | Type | Description |
| -- | -- | -- |
| [8802b4aab9](https://github.com/angular/angular/commit/8802b4aab9600870f3e09f198c993845587e9622) | fix | Update `Location` to get a normalized URL valid in case a represented URL starts with the substring equals `APP_BASE_HREF` ([#48489](https://github.com/angular/angular/pull/48489)) |
| [5f21c6d627](https://github.com/angular/angular/commit/5f21c6d627892dfad942b55f13861c213f9c0afb) | perf | avoid excessive DOM mutation in NgClass ([#48433](https://github.com/angular/angular/pull/48433)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [f00bf71411](https://github.com/angular/angular/commit/f00bf714110100549111bd27345943ab8830128c) | fix | makeEnvironmentProviders should accept EnvironmentProviders ([#48720](https://github.com/angular/angular/pull/48720)) |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [4ae384fd61](https://github.com/angular/angular/commit/4ae384fd619a13eaadf737d08a97f07e1f6b273c) | feat | Allow auto-imports of a pipe via quick fix when its selector is used, both directly and via reexports. ([#48354](https://github.com/angular/angular/pull/48354)) |
| [141333411e](https://github.com/angular/angular/commit/141333411e67769d752c7162e4cb03376022f5e1) | feat | Introduce a new NgModuleIndex, and use it to suggest re-exports. ([#48354](https://github.com/angular/angular/pull/48354)) |
## Special Thanks
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos, Bob Watson, Dylan Hunn, Jens, Konstantin Kharitonov, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Pawel Kozlowski, Vladyslav Slipchenko, ced, dario-piotrowicz, mgechev and ノウラ
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.1.1"></a>
# 15.1.1 (2023-01-18)
### common
@@ -214,17 +191,6 @@ Alan Agius, Alex Rickabaugh, Andrew Scott, Aristeidis Bampakos, Bob Watson, Jens
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.2.0-next.0"></a>
# 15.2.0-next.0 (2023-01-11)
### router
| Commit | Type | Description |
| -- | -- | -- |
| [31b94c762f](https://github.com/angular/angular/commit/31b94c762fc91ab6cabe08ea6812780fdcf92a64) | feat | Add a withNavigationErrorHandler feature to provideRouter ([#48551](https://github.com/angular/angular/pull/48551)) |
## Special Thanks
Alan Agius, Alex Rickabaugh, Andrew Scott, Paul Gschwendtner and angular-robot[bot]
<!-- CHANGELOG SPLIT MARKER -->
<a name="15.1.0"></a>
# 15.1.0 (2023-01-10)
## Deprecations
+1 -1
View File
@@ -4,7 +4,7 @@
<img src="aio/src/assets/images/logos/angular/angular.png" alt="angular-logo" width="120px" height="120px"/>
<br>
<i>Angular is a development platform for building mobile and desktop web applications
<br> using Typescript/JavaScript and other languages.</i>
<br> using TypeScript/JavaScript and other languages.</i>
<br>
</p>
+3 -3
View File
@@ -192,10 +192,10 @@ cldr_xml_data_repository(
# sass rules
http_archive(
name = "io_bazel_rules_sass",
sha256 = "38442f09a6f6f4e932e86ed29b86d5026b41d9ebbe9d017fe8a5684e1826b821",
strip_prefix = "rules_sass-b603e6f0daf73d5aa9dca768b45dde30e5b288bc",
sha256 = "78efa335c53253ed5c2b731bce8e654922c92f9f49337f9ee63b275c61989b33",
strip_prefix = "rules_sass-1f277900101a3d057392df55b2d6f332df34087a",
urls = [
"https://github.com/bazelbuild/rules_sass/archive/b603e6f0daf73d5aa9dca768b45dde30e5b288bc.zip",
"https://github.com/bazelbuild/rules_sass/archive/1f277900101a3d057392df55b2d6f332df34087a.zip",
],
)
+11 -11
View File
@@ -3,12 +3,12 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@angular/animations": "15.1.0-next.2",
"@angular/common": "15.1.0-next.2",
"@angular/compiler": "15.1.0-next.2",
"@angular/core": "15.1.0-next.2",
"@angular/platform-browser": "15.1.0-next.2",
"@angular/platform-browser-dynamic": "15.1.0-next.2",
"@angular/animations": "~15.2.0",
"@angular/common": "~15.2.0",
"@angular/compiler": "~15.2.0",
"@angular/core": "~15.2.0",
"@angular/platform-browser": "~15.2.0",
"@angular/platform-browser-dynamic": "~15.2.0",
"rxjs": "^7.5.2",
"tslib": "^2.3.1",
"zone.js": "^0.12.0"
@@ -22,9 +22,9 @@
"e2e": "ng e2e"
},
"devDependencies": {
"@angular-devkit/build-angular": "~15.0.1",
"@angular/cli": "~15.0.1",
"@angular/compiler-cli": "~15.0.1",
"@angular-devkit/build-angular": "~15.2.0",
"@angular/cli": "~15.2.0",
"@angular/compiler-cli": "~15.2.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
@@ -38,6 +38,6 @@
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"tslint": "~6.1.0",
"typescript": "~4.8.4"
"typescript": "~4.9.3"
}
}
}
File diff suppressed because it is too large Load Diff
@@ -220,7 +220,7 @@ When you use the Angular CLI command `ng new --strict`, it is set to `true` in t
When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode).
The strictness flags that this open enables allow you to turn on and off specific types of strict template type checking.
The strictness flags that this option enables allow you to turn on and off specific types of strict template type checking.
See [troubleshooting template errors](guide/template-typecheck#troubleshooting-template-errors).
When you use the Angular CLI command `ng new --strict`, it is set to `true` in the new project's configuration.
@@ -1,3 +1,37 @@
# Configure HTTP URL parameters
# HTTP - Configure URL parameters
@reviewed 2022-10-06
Use the `HttpParams` class with the `params` request option to add URL query strings in your `HttpRequest`.
## Create URL parameter using the search method
The following example, the `searchHeroes()` method queries for heroes whose names contain the search term.
Start by importing `HttpParams` class.
<code-example hideCopy language="typescript">
import {HttpParams} from "&commat;angular/common/http";
</code-example>
<code-example linenums="false" path="http/src/app/heroes/heroes.service.ts" region="searchHeroes"></code-example>
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter.
If the term is "cat", for example, the GET request URL would be `api/heroes?name=cat`.
The `HttpParams` object is immutable.
If you need to update the options, save the returned value of the `.set()` method.
## Create URL parameters from a query
You can also create HTTP parameters directly from a query string by using the `fromString` variable:
<code-example hideCopy language="typescript">
const params = new HttpParams({fromString: 'name=foo'});
</code-example>
<a id="intercepting-requests-and-responses"></a>
@reviewed 2022-11-08
@@ -1,3 +1,60 @@
# Handle request errors
# HTTP client - Handle request errors
@reviewed 2022-10-06
If the request fails on the server, `HttpClient` returns an *error* object instead of a successful response.
The same service that performs your server transactions should also perform error inspection, interpretation, and resolution.
When an error occurs, you can obtain details of what failed to inform your user.
In some cases, you might also automatically [retry the request](#retry).
<a id="error-details"></a>
## Getting error details
An app should give the user useful feedback when data access fails.
A raw error object is not particularly useful as feedback.
In addition to detecting that an error has occurred, you need to get error details and use those details to compose a user-friendly response.
Two types of errors can occur.
* The server backend might reject the request, returning an HTTP response with a status code such as 404 or 500.
These are error *responses*.
* Something could go wrong on the client-side such as a network error that prevents the request from completing successfully or an exception thrown in an RxJS operator.
These errors have `status` set to `0` and the `error` property contains a `ProgressEvent` object, whose `type` might provide further information.
`HttpClient` captures both kinds of errors in its `HttpErrorResponse`.
Inspect that response to identify the error's cause.
The following example defines an error handler in the previously defined ConfigService.
<code-example header="app/config/config.service.ts (handleError)" path="http/src/app/config/config.service.ts" region="handleError"></code-example>
The handler returns an RxJS `ErrorObservable` with a user-friendly error message.
The following code updates the `getConfig()` method, using a [pipe](guide/pipes "Pipes guide") to send all observables returned by the `HttpClient.get()` call to the error handler.
<code-example header="app/config/config.service.ts (getConfig v.3 with error handler)" path="http/src/app/config/config.service.ts" region="getConfig_3"></code-example>
<a id="retry"></a>
## Retrying a failed request
Sometimes the error is transient and goes away automatically if you try again.
For example, network interruptions are common in mobile scenarios, and trying again can produce a successful result.
The [RxJS library](guide/rx-library) offers several *retry* operators.
For example, the `retry()` operator automatically re-subscribes to a failed `Observable` a specified number of times.
*Re-subscribing* to the result of an `HttpClient` method call has the effect of reissuing the HTTP request.
The following example shows how to pipe a failed request to the `retry()` operator before passing it to the error handler.
<code-example header="app/config/config.service.ts (getConfig with retry)" path="http/src/app/config/config.service.ts" region="getConfig"></code-example>
## Sending data to a server
In addition to fetching data from a server, `HttpClient` supports other HTTP methods such as PUT, POST, and DELETE, which you can use to modify the remote data.
The sample app for this guide includes an abridged version of the "Tour of Heroes" example that fetches heroes and enables users to add, delete, and update them.
The following sections show examples of the data-update methods from the sample's `HeroesService`.
@reviewed 2023-02-27
@@ -1,3 +1,189 @@
# Intercept requests and responses
# HTTP - Intercept requests and responses
@reviewed 2022-10-06
With interception, you declare *interceptors* that inspect and transform HTTP requests from your application to a server.
The same interceptors can also inspect and transform a server's responses on their way back to the application.
Multiple interceptors form a *forward-and-backward* chain of request/response handlers.
Interceptors can perform a variety of *implicit* tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Without interception, developers would have to implement these tasks *explicitly* for each `HttpClient` method call.
## Write an interceptor
To implement an interceptor, declare a class that implements the `intercept()` method of the `HttpInterceptor` interface.
Here is a do-nothing `noop` interceptor that passes the request through without touching it:
<code-example header="app/http-interceptors/noop-interceptor.ts" path="http/src/app/http-interceptors/noop-interceptor.ts"></code-example>
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
In this sense, each interceptor is fully capable of handling the request entirely by itself.
Most interceptors inspect the request on the way in and forward the potentially altered request to the `handle()` method of the `next` object which implements the [`HttpHandler`](api/common/http/HttpHandler) interface.
<code-example format="javascript" language="javascript">
export abstract class HttpHandler {
abstract handle(req: HttpRequest&lt;any&gt;): Observable&lt;HttpEvent&lt;any&gt;&gt;;
}
</code-example>
Like `intercept()`, the `handle()` method transforms an HTTP request into an `Observable` of [`HttpEvents`](#interceptor-events) which ultimately include the server's response.
The `intercept()` method could inspect that observable and alter it before returning it to the caller.
This `no-op` interceptor calls `next.handle()` with the original request and returns the observable without doing a thing.
## The `next` object
The `next` object represents the next interceptor in the chain of interceptors.
The final `next` in the chain is the `HttpClient` backend handler that sends the request to the server and receives the server's response.
Most interceptors call `next.handle()` so that the request flows through to the next interceptor and, eventually, the backend handler.
An interceptor *could* skip calling `next.handle()`, short-circuit the chain, and return its own `Observable` with an artificial server response.
This is a common middleware pattern found in frameworks such as Express.js.
## Provide the interceptor
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
Like other services, you must provide the interceptor class before the app can use it.
Because interceptors are optional dependencies of the `HttpClient` service, you must provide them in the same injector or a parent of the injector that provides `HttpClient`.
Interceptors provided *after* DI creates the `HttpClient` are ignored.
This app provides `HttpClient` in the app's root injector, as a side-effect of importing the `HttpClientModule` in `AppModule`.
You should provide interceptors in `AppModule` as well.
After importing the `HTTP_INTERCEPTORS` injection token from `@angular/common/http`, write the `NoopInterceptor` provider like this:
<code-example path="http/src/app/http-interceptors/index.ts" region="noop-provider"></code-example>
Notice the `multi: true` option.
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a *multiprovider* that injects an array of values, rather than a single value.
You *could* add this provider directly to the providers array of the `AppModule`.
However, it's rather verbose and there's a good chance that you'll create more interceptors and provide them in the same way.
You must also pay [close attention to the order](#interceptor-order) in which you provide these interceptors.
Consider creating a "barrel" file that gathers all the interceptor providers into an `httpInterceptorProviders` array, starting with this first one, the `NoopInterceptor`.
<code-example header="app/http-interceptors/index.ts" path="http/src/app/http-interceptors/index.ts" region="interceptor-providers"></code-example>
Then import and add it to the `AppModule` `providers array` like this:
<code-example header="app/app.module.ts (interceptor providers)" path="http/src/app/app.module.ts" region="interceptor-providers"></code-example>
As you create new interceptors, add them to the `httpInterceptorProviders` array and you won't have to revisit the `AppModule`.
<div class="alert is-helpful">
There are many more interceptors in the complete sample code.
</div>
## Interceptor order
Angular applies interceptors in the order that you provide them.
For example, consider a situation in which you want to handle the authentication of your HTTP requests and log them before sending them to a server.
To accomplish this task, you could provide an `AuthInterceptor` service and then a `LoggingInterceptor` service.
Outgoing requests would flow from the `AuthInterceptor` to the `LoggingInterceptor`.
Responses from these requests would flow in the other direction, from `LoggingInterceptor` back to `AuthInterceptor`.
The following is a visual representation of the process:
<div class="lightbox">
<img alt="Interceptor in order of HttpClient, AuthInterceptor, AuthInterceptor, HttpBackend, Server, and back in opposite order to show the two-way flow" src="generated/images/guide/http/interceptor-order.svg">
</div>
<div class="alert is-helpful">
The last interceptor in the process is always the `HttpBackend` that handles communication with the server.
</div>
You cannot change the order or remove interceptors later.
If you need to enable and disable an interceptor dynamically, you'll have to build that capability into the interceptor itself.
<a id="interceptor-events"></a>
## Handle interceptor events
Most `HttpClient` methods return observables of `HttpResponse<any>`.
The `HttpResponse` class itself is actually an event, whose type is `HttpEventType.Response`.
A single HTTP request can, however, generate multiple events of other types, including upload and download progress events.
The methods `HttpInterceptor.intercept()` and `HttpHandler.handle()` return observables of `HttpEvent<any>`.
Many interceptors are only concerned with the outgoing request and return the event stream from `next.handle()` without modifying it.
Some interceptors, however, need to examine and modify the response from `next.handle()`; these operations can see all of these events in the stream.
<a id="immutability"></a>
Although interceptors are capable of modifying requests and responses, the `HttpRequest` and `HttpResponse` instance properties are `readonly`, rendering them largely immutable.
They are immutable for a good reason:
An app might retry a request several times before it succeeds, which means that the interceptor chain can re-process the same request multiple times.
If an interceptor could modify the original request object, the re-tried operation would start from the modified request rather than the original.
Immutability ensures that interceptors see the same request for each try.
<div class="alert is-helpful">
Your interceptor should return every event without modification unless it has a compelling reason to do otherwise.
</div>
TypeScript prevents you from setting `HttpRequest` read-only properties.
<code-example format="javascript" language="javascript">
// Typescript disallows the following assignment because req.url is readonly
req.url = req.url.replace('http://', 'https://');
</code-example>
If you must alter a request, clone it first and modify the clone before passing it to `next.handle()`.
You can clone and modify the request in a single step, as shown in the following example.
<code-example header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)" path="http/src/app/http-interceptors/ensure-https-interceptor.ts" region="excerpt"></code-example>
The `clone()` method's hash argument lets you mutate specific properties of the request while copying the others.
### Modify a request body
The `readonly` assignment guard can't prevent deep updates and, in particular, it can't prevent you from modifying a property of a request body object.
<code-example format="javascript" language="javascript">
req.body.name = req.body.name.trim(); // bad idea!
</code-example>
If you must modify the request body, follow these steps.
1. Copy the body and make your change in the copy.
1. Clone the request object, using its `clone()` method.
1. Replace the clone's body with the modified copy.
<code-example header="app/http-interceptors/trim-name-interceptor.ts (excerpt)" path="http/src/app/http-interceptors/trim-name-interceptor.ts" region="excerpt"></code-example>
### Clear the request body in a clone
Sometimes you need to clear the request body rather than replace it.
To do this, set the cloned request body to `null`.
<div class="alert is-helpful">
**TIP**: <br />
If you set the cloned request body to `undefined`, Angular assumes you intend to leave the body as is.
</div>
<code-example format="javascript" language="javascript">
newReq = req.clone({ &hellip; }); // body not mentioned =&gt; preserve original body
newReq = req.clone({ body: undefined }); // preserve original body
newReq = req.clone({ body: null }); // clear the body
</code-example>
@reviewed 2023-02-27
+126 -2
View File
@@ -1,3 +1,127 @@
# HTTP interceptor use-cases
# HTTP - interceptor use-cases
@reviewed 2022-10-06
Following are a number of common uses for interceptors.
## Set default headers
Apps often use an interceptor to set default headers on outgoing requests.
The sample app has an `AuthService` that produces an authorization token.
Here is its `AuthInterceptor` that injects that service to get the token and adds an authorization header with that token to every outgoing request:
<code-example header="app/http-interceptors/auth-interceptor.ts" path="http/src/app/http-interceptors/auth-interceptor.ts"></code-example>
The practice of cloning a request to set new headers is so common that there's a `setHeaders` shortcut for it:
<code-example path="http/src/app/http-interceptors/auth-interceptor.ts" region="set-header-shortcut"></code-example>
An interceptor that alters headers can be used for a number of different operations, including:
* Authentication/authorization
* Caching behavior; for example, `If-Modified-Since`
* XSRF protection
## Log request and response pairs
Because interceptors can process the request and response *together*, they can perform tasks such as timing and logging an entire HTTP operation.
Consider the following `LoggingInterceptor`, which captures the time of the request,
the time of the response, and logs the outcome with the elapsed time
with the injected `MessageService`.
<code-example header="app/http-interceptors/logging-interceptor.ts)" path="http/src/app/http-interceptors/logging-interceptor.ts" region="excerpt"></code-example>
The RxJS `tap` operator captures whether the request succeeded or failed.
The RxJS `finalize` operator is called when the response observable either returns an error or completes and reports the outcome to the `MessageService`.
Neither `tap` nor `finalize` touch the values of the observable stream returned to the caller.
<a id="custom-json-parser"></a>
## Custom JSON parsing
Interceptors can be used to replace the built-in JSON parsing with a custom implementation.
The `CustomJsonInterceptor` in the following example demonstrates how to achieve this.
If the intercepted request expects a `'json'` response, the `responseType` is changed to `'text'` to disable the built-in JSON parsing.
Then the response is parsed via the injected `JsonParser`.
<code-example header="app/http-interceptors/custom-json-interceptor.ts" path="http/src/app/http-interceptors/custom-json-interceptor.ts" region="custom-json-interceptor"></code-example>
You can then implement your own custom `JsonParser`.
Here is a custom JsonParser that has a special date reviver.
<code-example header="app/http-interceptors/custom-json-interceptor.ts" path="http/src/app/http-interceptors/custom-json-interceptor.ts" region="custom-json-parser"></code-example>
You provide the `CustomParser` along with the `CustomJsonInterceptor`.
<code-example header="app/http-interceptors/index.ts" path="http/src/app/http-interceptors/index.ts" region="custom-json-interceptor"></code-example>
<a id="caching"></a>
## Cache requests
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
For example, you might decide to cache certain requests and responses to improve performance.
You can delegate caching to an interceptor without disturbing your existing data services.
The `CachingInterceptor` in the following example demonstrates this approach.
<code-example header="app/http-interceptors/caching-interceptor.ts)" path="http/src/app/http-interceptors/caching-interceptor.ts" region="v1"></code-example>
* The `isCacheable()` function determines if the request is cacheable.
In this sample, only GET requests to the package search API are cacheable.
* If the request is not cacheable, the interceptor forwards the request to the next handler in the chain
* If a cacheable request is found in the cache, the interceptor returns an `of()` *observable* with the cached response, by-passing the `next` handler and all other interceptors downstream
* If a cacheable request is not in cache, the code calls `sendRequest()`.
This function forwards the request to `next.handle()` which ultimately calls the server and returns the server's response.
<a id="send-request"></a>
<code-example path="http/src/app/http-interceptors/caching-interceptor.ts" region="send-request"></code-example>
<div class="alert is-helpful">
Notice how `sendRequest()` intercepts the response on its way back to the application.
This method pipes the response through the `tap()` operator, whose callback adds the response to the cache.
The original response continues untouched back up through the chain of interceptors to the application caller.
Data services, such as `PackageSearchService`, are unaware that some of their `HttpClient` requests actually return cached responses.
</div>
<a id="cache-refresh"></a>
## Use interceptors to request multiple values
The `HttpClient.get()` method normally returns an observable that emits a single value, either the data or an error.
An interceptor can change this to an observable that emits [multiple values](guide/observables).
The following revised version of the `CachingInterceptor` optionally returns an observable that immediately emits the cached response, sends the request on to the package search API, and emits again later with the updated search results.
<code-example path="http/src/app/http-interceptors/caching-interceptor.ts" region="intercept-refresh"></code-example>
<div class="alert is-helpful">
The *cache-then-refresh* option is triggered by the presence of a custom `x-refresh` header.
A checkbox on the `PackageSearchComponent` toggles a `withRefresh` flag, which is one of the arguments to `PackageSearchService.search()`.
That `search()` method creates the custom `x-refresh` header and adds it to the request before calling `HttpClient.get()`.
</div>
The revised `CachingInterceptor` sets up a server request whether there's a cached value or not, using the same `sendRequest()` method described [above](#send-request).
The `results$` observable makes the request when subscribed.
* If there's no cached value, the interceptor returns `results$`.
* If there is a cached value, the code *pipes* the cached response onto `results$`. This produces a recomposed observable that emits two responses, so subscribers will see a sequence of these two responses:
* The cached response that's emitted immediately
* The response from the server, that's emitted later
<a id="report-progress"></a>
@reviewed 2022-11-08
+46 -2
View File
@@ -1,3 +1,47 @@
# Make a JSON with Padding request
# HTTP: Make a JSONP request
@reviewed 2022-10-06
“JSON with Padding” (JSONP) is a method to deceive a web browser into carrying out requests with a `<script>` tag that uses the SRC attribute to make a special API request.
Apps can use the `HttpClient` to make [JSONP](https://en.wikipedia.org/wiki/JSONP) requests across domains when a server doesn't support [CORS protocol](https://developer.mozilla.org/docs/Web/HTTP/CORS).
Angular JSONP requests return an `Observable`.
Follow the pattern for subscribing to observables and use the RxJS `map` operator to transform the response before using the [async pipe](api/common/AsyncPipe) to manage the results.
In Angular, use JSONP by including `HttpClientJsonpModule` in the `NgModule` imports.
In the following example, the `searchHeroes()` method uses a JSONP request to query for heroes whose names contain the search term.
<code-example format="typescript" language="typescript">
/* GET heroes whose name contains search term */
searchHeroes(term: string): Observable {
term = term.trim();
const heroesURL = `&dollar;{this.heroesURL}?&dollar;{term}`;
return this.http.jsonp(heroesUrl, 'callback').pipe(
catchError(this.handleError('searchHeroes', [])) // then handle the error
);
}
</code-example>
This request passes the `heroesURL` as the first parameter and the callback function name as the second parameter.
The response is wrapped in the callback function, which takes the observables returned by the JSONP method and pipes them through to the error handler.
## Request non-JSON data
Not all APIs return JSON data.
In this next example, a `DownloaderService` method reads a text file from the server and logs the file contents, before returning those contents to the caller as an `Observable<string>`.
<code-example header="app/downloader/downloader.service.ts (getTextFile)" linenums="false" path="http/src/app/downloader/downloader.service.ts" region="getTextFile"></code-example>
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
The RxJS `tap` operator lets the code inspect both success and error values passing through the observable without disturbing them.
A `download()` method in the `DownloaderComponent` initiates the request by subscribing to the service method.
<code-example header="app/downloader/downloader.component.ts (download)" linenums="false" path="http/src/app/downloader/downloader.component.ts" region="download"></code-example>
<a id="error-handling"></a>
@reviewed 2022-11-03
@@ -1,3 +1,59 @@
# Optimize server interaction with debouncing
# HTTP - Optimize server interaction with debouncing
@reviewed 2022-10-06
If you need to make an HTTP request in response to user input, it's not efficient to send a request for every keystroke. It's better to wait until the user stops typing and then send a request. This technique is known as debouncing.
## Implement debouncing
Consider the following template, which lets a user enter a search term to find a package by name. When the user enters a name in a search-box, the `PackageSearchComponent` sends a search request for a package with that name to the package search API.
<code-example header="app/package-search/package-search.component.html (search)" path="http/src/app/package-search/package-search.component.html" region="search"></code-example>
Here, the `keyup` event binding sends every keystroke to the component's `search()` method.
<div class="alert is-helpful">
The type of `$event.target` is only `EventTarget` in the template.
In the `getValue()` method, the target is cast to an `HTMLInputElement` to let type-safe have access to its `value` property.
<code-example path="http/src/app/package-search/package-search.component.ts" region="getValue"></code-example>
</div>
The following snippet implements debouncing for this input using RxJS operators.
<code-example header="app/package-search/package-search.component.ts (excerpt)" path="http/src/app/package-search/package-search.component.ts" region="debounce"></code-example>
The `searchText$` is the sequence of search-box values coming from the user.
It's defined as an RxJS `Subject`, which means it is a multicasting `Observable` that can also emit values for itself by calling `next(value)`, as happens in the `search()` method.
Rather than forward every `searchText` value directly to the injected `PackageSearchService`, the code in `ngOnInit()` pipes search values through three operators, so that a search value reaches the service only if it's a new value and the user stopped typing.
| RxJS operators | Details |
|:--- |:--- |
| `debounceTime(500)`⁠ | Wait for the user to stop typing, which is 1/2 second in this case. |
| `distinctUntilChanged()` | Wait until the search text changes. |
| `switchMap()`⁠ | Send the search request to the service. |
The code sets `packages$` to this re-composed `Observable` of search results.
The template subscribes to `packages$` with the [AsyncPipe](api/common/AsyncPipe) and displays search results as they arrive.
## Using the `switchMap()` operator
The `switchMap()` operator takes a function argument that returns an `Observable`.
In the example, `PackageSearchService.search` returns an `Observable`, as other data service methods do.
If a previous search request is still in-flight, such as when the network connection is poor, the operator cancels that request and sends a new one.
<div class="alert is-helpful">
**NOTE**: <br />
`switchMap()` returns service responses in their original request order, even if the server returns them out of order.
</div>
<div class="alert is-helpful">
If you think you'll reuse this debouncing logic, consider moving it to a utility function or into the `PackageSearchService` itself.
</div>
@reviewed 2023-02-27
@@ -1,3 +1,49 @@
# Pass metadata to interceptors
# HTTP - Pass metadata to interceptors
@reviewed 2022-10-06
Many interceptors require or benefit from configuration.
Consider an interceptor that retries failed requests.
By default, the interceptor might retry a request three times, but you might want to override this retry count for particularly error-prone or sensitive requests.
`HttpClient` requests contain a *context* that can carry metadata about the request.
This context is available for interceptors to read or modify, though it is not transmitted to the backend server when the request is sent.
This lets applications or other interceptors tag requests with configuration parameters, such as how many times to retry a request.
## Create a context token
Angular stores and retrieves a value in the context using an `HttpContextToken`.
You can create a context token using the `new` operator, as in the following example:
<code-example header="creating a context token" path="http/src/app/http-interceptors/retry-interceptor.ts" region="context-token"></code-example>
The lambda function `() => 3` passed during the creation of the `HttpContextToken` serves two purposes:
1. It lets TypeScript infer the type of this token:
`HttpContextToken<number>`
The request context is type-safe &mdash;reading a token from a request's context returns a value of the appropriate type.
1. It sets the default value for the token.
This is the value that the request context returns if no other value was set for this token.
Using a default value avoids the need to check if a particular value is set.
## Set context values when making a request
When making a request, you can provide an `HttpContext` instance, in which you have already set the context values.
<code-example header="setting context values" path="http/src/app/http-interceptors/retry-interceptor.ts" region="set-context"></code-example>
## Read context values in an interceptor
Within an interceptor, you can read the value of a token in a given request's context with `HttpContext.get()`.
If you have not explicitly set a value for the token, Angular returns the default value specified in the token.
<code-example header="reading context values in an interceptor" path="http/src/app/http-interceptors/retry-interceptor.ts" region="reading-context"></code-example>
## Contexts are mutable
Unlike most other aspects of `HttpRequest` instances, the request context is mutable and persists across other immutable transformations of the request.
This lets interceptors coordinate operations through the context.
For instance, the `RetryInterceptor` example could use a second context token to track how many errors occur during the execution of a given request:
<code-example header="coordinating operations through the context" path="http/src/app/http-interceptors/retry-interceptor.ts" region="mutable-context"></code-example>
@reviewed 2022-11-15
@@ -1,3 +1,221 @@
# Request data from a server
# HTTP: Request data from a server
@reviewed 2022-10-06
Use the [`HttpClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
The asynchronous method sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
The return type varies based on the `observe` and `responseType` values that you pass to the call.
The `get()` method takes two arguments; the endpoint URL from which to fetch, and an *options* object that is used to configure the request.
<code-example format="typescript" language="typescript">
options: {
headers?: HttpHeaders &verbar; {[header: string]: string &verbar; string[]},
observe?: 'body' &verbar; 'events' &verbar; 'response',
params?: HttpParams&verbar;{[param: string]: string &verbar; number &verbar; boolean &verbar; ReadonlyArray&lt;string &verbar; number &verbar; boolean&gt;},
reportProgress?: boolean,
responseType?: 'arraybuffer'&verbar;'blob'&verbar;'json'&verbar;'text',
withCredentials?: boolean,
}
</code-example>
Important options include the *observe* and *responseType* properties.
* The *observe* option specifies how much of the response to return
* The *responseType* option specifies the format in which to return data
<div class="alert is-helpful">
Use the `options` object to configure various other aspects of an outgoing request.
In adding headers, for example, the service set the default headers using the `headers` option property.
Use the `params` property to configure a request with HTTP URL parameters, and the `reportProgress` option to listen for progress events when transferring large amounts of data.
</div>
Applications often request JSON data from a server.
In the `ConfigService` example, the app needs a configuration file on the server, `config.json`, that specifies resource URLs.
<code-example header="assets/config.json" path="http/src/assets/config.json"></code-example>
To fetch this kind of data, the `get()` call needs the following options: `{observe: 'body', responseType: 'json'}`.
These are the default values for those options, so the following examples do not pass the options object.
Later sections show some of the additional option possibilities.
<a id="config-service"></a>
The example conforms to the best practices for creating scalable solutions by defining a re-usable [injectable service](guide/glossary#service "service definition") to perform the data-handling functionality.
In addition to fetching data, the service can post-process the data, add error handling, and add retry logic.
The `ConfigService` fetches this file using the `HttpClient.get()` method.
<code-example header="app/config/config.service.ts (getConfig v.1)" path="http/src/app/config/config.service.ts" region="getConfig_1"></code-example>
The `ConfigComponent` injects the `ConfigService` and calls the `getConfig` service method.
Because the service method returns an `Observable` of configuration data, the component *subscribes* to the method's return value.
The subscription callback performs minimal post-processing.
It copies the data fields into the component's `config` object, which is data-bound in the component template for display.
<code-example header="app/config/config.component.ts (showConfig v.1)" path="http/src/app/config/config.component.ts" region="v1"></code-example>
<a id="always-subscribe"></a>
## Starting the request
For all `HttpClient` methods, the method doesn't begin its HTTP request until you call `subscribe()` on the observable the method returns.
This is true for *all* `HttpClient` *methods*.
<div class="alert is-helpful">
You should always unsubscribe from an observable when a component is destroyed.
</div>
All observables returned from `HttpClient` methods are *cold* by design.
Execution of the HTTP request is *deferred*, letting you extend the observable with additional operations such as `tap` and `catchError` before anything actually happens.
Calling `subscribe()` triggers execution of the observable and causes `HttpClient` to compose and send the HTTP request to the server.
Think of these observables as *blueprints* for actual HTTP requests.
<div class="alert is-helpful">
In fact, each `subscribe()` initiates a separate, independent execution of the observable.
Subscribing twice results in two HTTP requests.
<code-example format="javascript" language="javascript">
const req = http.get&lt;Heroes&gt;('/api/heroes');
// 0 requests made - .subscribe() not called.
req.subscribe();
// 1 request made.
req.subscribe();
// 2 requests made.
</code-example>
</div>
<a id="typed-response"></a>
## Requesting a typed response
Structure your `HttpClient` request to declare the type of the response object, to make consuming the output easier and more obvious.
Specifying the response type acts as a type assertion at compile time.
<div class="alert is-important">
Specifying the response type is a declaration to TypeScript that it should treat your response as being of the given type.
This is a build-time check and doesn't guarantee that the server actually responds with an object of this type.
It is up to the server to ensure that the type specified by the server API is returned.
</div>
To specify the response object type, first define an interface with the required properties.
Use an interface rather than a class, because the response is a plain object that cannot be automatically converted to an instance of a class.
<code-example path="http/src/app/config/config.service.ts" region="config-interface"></code-example>
Next, specify that interface as the `HttpClient.get()` call's type parameter in the service.
<code-example header="app/config/config.service.ts (getConfig v.2)" path="http/src/app/config/config.service.ts" region="getConfig_2"></code-example>
<div class="alert is-helpful">
When you pass an interface as a type parameter to the `HttpClient.get()` method, use the [RxJS `map` operator](guide/rx-library#operators) to transform the response data as needed by the UI.
You can then pass the transformed data to the [async pipe](api/common/AsyncPipe).
</div>
The callback in the updated component method receives a typed data object, which is easier and safer to consume:
<code-example header="app/config/config.component.ts (showConfig v.2)" path="http/src/app/config/config.component.ts" region="v2"></code-example>
To access properties that are defined in an interface, you must explicitly convert the plain object you get from the JSON to the required response type.
For example, the following `subscribe` callback receives `data` as an Object, and then type-casts it in order to access the properties.
<code-example format="typescript" language="typescript">
.subscribe(data =&gt; this.config = {
heroesUrl: (data as any).heroesUrl,
textfile: (data as any).textfile,
});
</code-example>
<a id="string-union-types"></a>
<div class="callout is-important">
<header><code>observe</code> and <code>response</code> types</header>
The types of the `observe` and `response` options are *string unions*, rather than plain strings.
<code-example format="typescript" language="typescript">
options: {
&hellip;
observe?: 'body' &verbar; 'events' &verbar; 'response',
&hellip;
responseType?: 'arraybuffer'&verbar;'blob'&verbar;'json'&verbar;'text',
&hellip;
}
</code-example>
This can cause confusion.
For example:
<code-example format="typescript" language="typescript">
// this works
client.get('/foo', {responseType: 'text'})
// but this does NOT work
const options = {
responseType: 'text',
};
client.get('/foo', options)
</code-example>
In the second case, TypeScript infers the type of `options` to be `{responseType: string}`.
The type is too wide to pass to `HttpClient.get` which is expecting the type of `responseType` to be one of the *specific* strings.
`HttpClient` is typed explicitly this way so that the compiler can report the correct return type based on the options you provided.
Use `as const` to let TypeScript know that you really do mean to use a constant string type:
<code-example format="typescript" language="typescript">
const options = {
responseType: 'text' as const,
};
client.get('/foo', options);
</code-example>
</div>
## Reading the full response
In the previous example, the call to `HttpClient.get()` did not specify any options.
By default, it returned the JSON data contained in the response body.
You might need more information about the transaction than is contained in the response body.
Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
Tell `HttpClient` that you want the full response with the `observe` option of the `get()` method:
<code-example path="http/src/app/config/config.service.ts" region="getConfigResponse"></code-example>
Now `HttpClient.get()` returns an `Observable` of type `HttpResponse` rather than just the JSON data contained in the body.
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
<code-example header="app/config/config.component.ts (showConfigResponse)" path="http/src/app/config/config.component.ts" region="showConfigResponse"></code-example>
As you can see, the response object has a `body` property of the correct type.
@reviewed 2023-02-27
@@ -1,3 +1,35 @@
# Security: Cross-site request forgery (XSRF) protection
# HTTP client - Security: Cross-Site Request Forgery (XSRF) protection
@reviewed 2022-10-06
[Cross-Site Request Forgery (XSRF or CSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website.
`HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token) used to prevent XSRF attacks.
When performing HTTP requests, an interceptor reads a token from a cookie, by default `XSRF-TOKEN`, and sets it as an HTTP header, `X-XSRF-TOKEN`.
Because only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
By default, an interceptor sends this header on all mutating requests \(such as POST\)
to relative URLs, but not on GET/HEAD requests or on requests with an absolute URL.
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called `XSRF-TOKEN` on either the page load or the first GET request.
On subsequent requests the server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be sure that only code running on your domain could have sent the request.
The token must be unique for each user and must be verifiable by the server; this prevents the client from making up its own tokens.
Set the token to a digest of your site's authentication cookie with a salt for added security.
To prevent collisions in environments where multiple Angular apps share the same domain or subdomain, give each application a unique cookie name.
<div class="alert is-important">
*`HttpClient` supports only the client half of the XSRF protection scheme.*
Your backend service must be configured to set the cookie for your page, and to verify that the header is present on all eligible requests.
Failing to do so renders Angular's default protection ineffective.
</div>
## Configure custom cookie/header names
If your backend service uses different names for the XSRF token cookie or header, use `HttpClientXsrfModule.withOptions()` to override the defaults.
<code-example path="http/src/app/app.module.ts" region="xsrf"></code-example>
<a id="testing-requests"></a>
@reviewed 2022-11-14
@@ -0,0 +1,254 @@
# HTTP Server communication
Most front-end applications need to communicate with a server over the HTTP protocol, to download or upload data and access other back-end services.
## Setup for server communication
Before you can use `HttpClient`, you need to import the Angular `HttpClientModule`.
Most apps do so in the root `AppModule`.
<code-example header="app/app.module.ts (excerpt)" path="http/src/app/app.module.ts" region="sketch"></code-example>
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
The `HttpClient` service makes use of [observables](guide/glossary#observable "Observable definition") for all transactions.
You must import the RxJS observable and operator symbols that appear in the example snippets.
These `ConfigService` imports are typical.
<code-example header="app/config/config.service.ts (RxJS imports)" path="http/src/app/config/config.service.ts" region="rxjs-imports"></code-example>
<div class="alert is-helpful">
You can run the <live-example></live-example> that accompanies this guide.
The sample app does not require a data server.
It relies on the [Angular *in-memory-web-api*](https://github.com/angular/angular/tree/main/packages/misc/angular-in-memory-web-api), which replaces the *HttpClient* module's `HttpBackend`.
The replacement service simulates the behavior of a REST-like backend.
Look at the `AppModule` *imports* to see how it is configured.
</div>
## Requesting data from a server
Use the [`HttpClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
The asynchronous method sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
The return type varies based on the `observe` and `responseType` values that you pass to the call.
The `get()` method takes two arguments; the endpoint URL from which to fetch, and an *options* object that is used to configure the request.
<code-example format="typescript" language="typescript">
options: {
headers?: HttpHeaders &verbar; {[header: string]: string &verbar; string[]},
observe?: 'body' &verbar; 'events' &verbar; 'response',
params?: HttpParams&verbar;{[param: string]: string &verbar; number &verbar; boolean &verbar; ReadonlyArray&lt;string &verbar; number &verbar; boolean&gt;},
reportProgress?: boolean,
responseType?: 'arraybuffer'&verbar;'blob'&verbar;'json'&verbar;'text',
withCredentials?: boolean,
}
</code-example>
Important options include the *observe* and *responseType* properties.
* The *observe* option specifies how much of the response to return
* The *responseType* option specifies the format in which to return data
<div class="alert is-helpful">
Use the `options` object to configure various other aspects of an outgoing request.
In adding headers, for example, the service set the default headers using the `headers` option property.
Use the `params` property to configure a request with [TTP URL parameters, and the `reportProgress` option to listen for progress events when transferring large amounts of data.
</div>
Applications often request JSON data from a server.
In the `ConfigService` example, the app needs a configuration file on the server, `config.json`, that specifies resource URLs.
<code-example header="assets/config.json" path="http/src/assets/config.json"></code-example>
To fetch this kind of data, the `get()` call needs the following options: `{observe: 'body', responseType: 'json'}`.
These are the default values for those options, so the following examples do not pass the options object.
Later sections show some of the additional option possibilities.
<a id="config-service"></a>
The example conforms to the best practices for creating scalable solutions by defining a re-usable [injectable service](guide/glossary#service "service definition") to perform the data-handling functionality.
In addition to fetching data, the service can post-process the data, add error handling, and add retry logic.
The `ConfigService` fetches this file using the `HttpClient.get()` method.
<code-example header="app/config/config.service.ts (getConfig v.1)" path="http/src/app/config/config.service.ts" region="getConfig_1"></code-example>
The `ConfigComponent` injects the `ConfigService` and calls the `getConfig` service method.
Because the service method returns an `Observable` of configuration data, the component *subscribes* to the method's return value.
The subscription callback performs minimal post-processing.
It copies the data fields into the component's `config` object, which is data-bound in the component template for display.
<code-example header="app/config/config.component.ts (showConfig v.1)" path="http/src/app/config/config.component.ts" region="v1"></code-example>
<a id="always-subscribe"></a>
### Starting the request
For all `HttpClient` methods, the method doesn't begin its HTTP request until you call `subscribe()` on the observable the method returns.
This is true for *all* `HttpClient` *methods*.
<div class="alert is-helpful">
You should always unsubscribe from an observable when a component is destroyed.
</div>
All observables returned from `HttpClient` methods are *cold* by design.
Execution of the HTTP request is *deferred*, letting you extend the observable with additional operations such as `tap` and `catchError` before anything actually happens.
Calling `subscribe()` triggers execution of the observable and causes `HttpClient` to compose and send the HTTP request to the server.
Think of these observables as *blueprints* for actual HTTP requests.
<div class="alert is-helpful">
In fact, each `subscribe()` initiates a separate, independent execution of the observable.
Subscribing twice results in two HTTP requests.
<code-example format="javascript" language="javascript">
const req = http.get&lt;Heroes&gt;('/api/heroes');
// 0 requests made - .subscribe() not called.
req.subscribe();
// 1 request made.
req.subscribe();
// 2 requests made.
</code-example>
</div>
<a id="typed-response"></a>
### Requesting a typed response
Structure your `HttpClient` request to declare the type of the response object, to make consuming the output easier and more obvious.
Specifying the response type acts as a type assertion at compile time.
<div class="alert is-important">
Specifying the response type is a declaration to TypeScript that it should treat your response as being of the given type.
This is a build-time check and doesn't guarantee that the server actually responds with an object of this type.
It is up to the server to ensure that the type specified by the server API is returned.
</div>
To specify the response object type, first define an interface with the required properties.
Use an interface rather than a class, because the response is a plain object that cannot be automatically converted to an instance of a class.
<code-example path="http/src/app/config/config.service.ts" region="config-interface"></code-example>
Next, specify that interface as the `HttpClient.get()` call's type parameter in the service.
<code-example header="app/config/config.service.ts (getConfig v.2)" path="http/src/app/config/config.service.ts" region="getConfig_2"></code-example>
<div class="alert is-helpful">
When you pass an interface as a type parameter to the `HttpClient.get()` method, use the [RxJS `map` operator](guide/rx-library#operators) to transform the response data as needed by the UI.
You can then pass the transformed data to the [async pipe](api/common/AsyncPipe).
</div>
The callback in the updated component method receives a typed data object, which is easier and safer to consume:
<code-example header="app/config/config.component.ts (showConfig v.2)" path="http/src/app/config/config.component.ts" region="v2"></code-example>
To access properties that are defined in an interface, you must explicitly convert the plain object you get from the JSON to the required response type.
For example, the following `subscribe` callback receives `data` as an Object, and then type-casts it in order to access the properties.
<code-example format="typescript" language="typescript">
.subscribe(data =&gt; this.config = {
heroesUrl: (data as any).heroesUrl,
textfile: (data as any).textfile,
});
</code-example>
<a id="string-union-types"></a>
<div class="callout is-important">
<header><code>observe</code> and <code>response</code> types</header>
The types of the `observe` and `response` options are *string unions*, rather than plain strings.
<code-example format="typescript" language="typescript">
options: {
&hellip;
observe?: 'body' &verbar; 'events' &verbar; 'response',
&hellip;
responseType?: 'arraybuffer'&verbar;'blob'&verbar;'json'&verbar;'text',
&hellip;
}
</code-example>
This can cause confusion.
For example:
<code-example format="typescript" language="typescript">
// this works
client.get('/foo', {responseType: 'text'})
// but this does NOT work
const options = {
responseType: 'text',
};
client.get('/foo', options)
</code-example>
In the second case, TypeScript infers the type of `options` to be `{responseType: string}`.
The type is too wide to pass to `HttpClient.get` which is expecting the type of `responseType` to be one of the *specific* strings.
`HttpClient` is typed explicitly this way so that the compiler can report the correct return type based on the options you provided.
Use `as const` to let TypeScript know that you really do mean to use a constant string type:
<code-example format="typescript" language="typescript">
const options = {
responseType: 'text' as const,
};
client.get('/foo', options);
</code-example>
</div>
### Reading the full response
In the previous example, the call to `HttpClient.get()` did not specify any options.
By default, it returned the JSON data contained in the response body.
You might need more information about the transaction than is contained in the response body.
Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
Tell `HttpClient` that you want the full response with the `observe` option of the `get()` method:
<code-example path="http/src/app/config/config.service.ts" region="getConfigResponse"></code-example>
Now `HttpClient.get()` returns an `Observable` of type `HttpResponse` rather than just the JSON data contained in the body.
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
<code-example header="app/config/config.component.ts (showConfigResponse)" path="http/src/app/config/config.component.ts" region="showConfigResponse"></code-example>
As you can see, the response object has a `body` property of the correct type.
@reviewed 2023-02-27
@@ -1,3 +1,30 @@
# Setup for server communication
# HTTP: Setup for server communication
@reviewed 2022-10-06
Before you can use `HttpClient`, you need to import the Angular `HttpClientModule`.
Most apps do so in the root `AppModule`.
<code-example header="app/app.module.ts (excerpt)" path="http/src/app/app.module.ts" region="sketch"></code-example>
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
The `HttpClient` service makes use of [observables](guide/glossary#observable "Observable definition") for all transactions.
You must import the RxJS observable and operator symbols that appear in the example snippets.
These `ConfigService` imports are typical.
<code-example header="app/config/config.service.ts (RxJS imports)" path="http/src/app/config/config.service.ts" region="rxjs-imports"></code-example>
<div class="alert is-helpful">
You can run the <live-example></live-example> that accompanies this guide.
The sample app does not require a data server.
It relies on the [Angular *in-memory-web-api*](https://github.com/angular/angular/tree/main/packages/misc/angular-in-memory-web-api), which replaces the *HttpClient* module's `HttpBackend`.
The replacement service simulates the behavior of a REST-like backend.
Look at the `AppModule` *imports* to see how it is configured.
</div>
@reviewed 2022-11-03
+74 -2
View File
@@ -1,3 +1,75 @@
# Test HTTP requests
# HTTP client - Test requests
@reviewed 2022-10-06
As for any external dependency, you must mock the HTTP backend so your tests can simulate interaction with a remote server.
The `@angular/common/http/testing` library makes it straightforward to set up such mocking.
## HTTP testing library
Angular's HTTP testing library is designed for a pattern of testing in which the app executes code and makes requests first.
The test then expects that certain requests have or have not been made, performs assertions against those requests, and finally provides responses by "flushing" each expected request.
At the end, tests can verify that the app made no unexpected requests.
<div class="alert is-helpful">
You can run <live-example stackblitz="specs">these sample tests</live-example> in a live coding environment.
The tests described in this guide are in `src/testing/http-client.spec.ts`.
There are also tests of an application data service that call `HttpClient` in `src/app/heroes/heroes.service.spec.ts`.
</div>
## Setup for testing
To begin testing calls to `HttpClient`, import the `HttpClientTestingModule` and the mocking controller, `HttpTestingController`, along with the other symbols your tests require.
<code-example header="app/testing/http-client.spec.ts (imports)" path="http/src/testing/http-client.spec.ts" region="imports"></code-example>
Then add the `HttpClientTestingModule` to the `TestBed` and continue with the setup of the *service-under-test*.
<code-example header="app/testing/http-client.spec.ts(setup)" path="http/src/testing/http-client.spec.ts" region="setup"></code-example>
Now requests made in the course of your tests hit the testing backend instead of the normal backend.
This setup also calls `TestBed.inject()` to inject the `HttpClient` service and the mocking controller so they can be referenced during the tests.
## Expect and answer requests
Now you can write a test that expects a GET Request to occur and provides a mock response.
<code-example header="app/testing/http-client.spec.ts (HttpClient.get)" path="http/src/testing/http-client.spec.ts" region="get-test"></code-example>
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
<code-example path="http/src/testing/http-client.spec.ts" region="afterEach"></code-example>
### Custom request expectations
If matching by URL isn't sufficient, it's possible to implement your own matching function.
For example, you could look for an outgoing request that has an authorization header:
<code-example path="http/src/testing/http-client.spec.ts" region="predicate"></code-example>
As with the previous `expectOne()`, the test fails if 0 or 2+ requests satisfy this predicate.
### Handle more than one request
If you need to respond to duplicate requests in your test, use the `match()` API instead of `expectOne()`.
It takes the same arguments but returns an array of matching requests.
Once returned, these requests are removed from future matching and you are responsible for flushing and verifying them.
<code-example path="http/src/testing/http-client.spec.ts" region="multi-request"></code-example>
## Test for errors
You should test the app's defenses against HTTP requests that fail.
Call `request.flush()` with an error message, as seen in the following example.
<code-example path="http/src/testing/http-client.spec.ts" region="404"></code-example>
Alternatively, call `request.error()` with a `ProgressEvent`.
<code-example path="http/src/testing/http-client.spec.ts" region="network-error"></code-example>
@reviewed 2022-11-14
+1 -1
View File
@@ -257,7 +257,7 @@ Because a wildcard route is the least specific route, place it last in the route
</div>
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to a non-existant route called `"/sidekicks"`.
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to a non-existent route called `"/sidekicks"`.
<code-example header="src/app/hero-list/hero-list.component.html (excerpt)" path="router/src/app/hero-list/hero-list.component.1.html"></code-example>
+1 -1
View File
@@ -134,7 +134,7 @@ To get information from a route:
* [`ActivatedRoute`](api/router/ActivatedRoute)
* [`ParamMap`](api/router/ParamMap)
1. Inject an instance of `ActivatedRoute` by adding it to your application's constructor:
1. Inject an instance of `ActivatedRoute` by adding it to your component's constructor:
<code-example header="In the component class (excerpt)" path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="activated-route"></code-example>
+204
View File
@@ -0,0 +1,204 @@
# Migrate an existing Angular project to standalone
As of version 15.2.0, Angular offers a [schematic](guide/schematics) to help project authors convert existing projects to [the new standalone APIs](guide/standalone-components). The schematic aims to transform as much code as possible automatically, but it may require some manual fixes by the project author. Run the schematic with the following command:
<code-example format="shell" language="shell">
ng generate @angular/core:standalone
</code-example>
## Prerequisites
Before using the schematic, please ensure that the project:
1. Is using Angular 15.2.0 or later.
2. Builds without any compilation errors.
3. Is on a clean Git branch and all work is saved.
## Schematic options
| Option | Details |
|:--- |:--- |
| `mode` | The transformation to perform. See [Migration modes](#migration-modes) below for details on the available options. |
| `path` | The path to migrate, relative to the project root. You can use this option to migrate sections of your project incrementally. |
## Migrations steps
The migration process is composed of three steps. You'll have to run it multiple times and check manually that the project builds and behaves as expected.
<div class="callout is-helpful">
<header>Note</header>
While the schematic can automatically update most code, some edge cases require developer intervention.
You should plan to apply manual fixes after each step of the migration. Additionally, the new code generated by the schematic may not match your code's formatting rules.
</div>
Run the migration in the order listed below, verifying that your code builds and runs between each step:
1. Run `ng g @angular/core:standalone` and select "Convert all components, directives and pipes to standalone"
2. Run `ng g @angular/core:standalone` and select "Remove unnecessary NgModule classes"
3. Run `ng g @angular/core:standalone` and select "Bootstrap the project using standalone APIs"
4. Run any linting and formatting checks, fix any failures, and commit the result
## After the migration
Congratulations, your application has been converted to standalone 🎉. These are some optional follow-up steps you may want to take now:
* Find and remove any remaining `NgModule` declarations: since the ["Remove unnecessary NgModules" step](#remove-unnecessary-ngmodules) cannot remove all modules automatically, you may have to remove the remaining declarations manually.
* Run the project's unit tests and fix any failures.
* Run any code formatters, if the project uses automatic formatting.
* Run any linters in your project and fix new warnings. Some linters support a `--fix` flag that may resolve so warnings automatically.
## Migration modes
The migration has the following modes:
1. Convert declarations to standalone.
2. Remove unnecessary NgModules.
3. Switch to standalone bootstrapping API.
You should run these migrations in the order given.
### Convert declarations to standalone
In this mode, the migration converts all components, directives and pipes to standalone by setting `standalone: true` and adding dependencies to their `imports` array.
<div class="callout is-helpful">
The schematic ignores NgModules which bootstrap a component during this step because they are likely root modules used by `bootstrapModule` rather than the standalone-compatible `bootstrapApplication`. The schematic converts these declarations automatically as a part of the ["Switch to standalone bootstrapping API"](#switch-to-standalone-bootstrapping-api) step.
</div>
**Before:**
```typescript
// shared.module.ts
@NgModule({
imports: [CommonModule],
declarations: [GreeterComponent],
exports: [GreeterComponent]
})
export class SharedModule {}
```
```typescript
// greeter.component.ts
@Component({
selector: 'greeter',
template: '<div *ngIf="showGreeting">Hello</div>',
})
export class GreeterComponent {
showGreeting = true;
}
```
**After:**
```typescript
// shared.module.ts
@NgModule({
imports: [CommonModule, GreeterComponent],
exports: [GreeterComponent]
})
export class SharedModule {}
```
```typescript
// greeter.component.ts
@Component({
selector: 'greeter',
template: '<div *ngIf="showGreeting">Hello</div>',
standalone: true,
imports: [NgIf]
})
export class GreeterComponent {
showGreeting = true;
}
```
### Remove unnecessary NgModules
After converting all declarations to standalone, many NgModules can be safely removed. This step deletes such module declarations and as many corresponding references as possible. If the migration cannot delete a reference automatically, it leaves the following TODO comment so that you can delete the NgModule manually:
```typescript
/* TODO(standalone-migration): clean up removed NgModule reference manually */
```
The migration considers a module safe to remove if that module:
* Has no `declarations`.
* Has no `providers`.
* Has no `bootstrap` components.
* Has no `imports` that reference a `ModuleWithProviders` symbol or a module that can't be removed.
* Has no class members. Empty constructors are ignored.
**Before:**
```typescript
// importer.module.ts
@NgModule({
imports: [FooComponent, BarPipe],
exports: [FooComponent, BarPipe]
})
export class ImporterModule {}
```
**After:**
```typescript
// importer.module.ts
// Does not exist!
```
### Switch to standalone bootstrapping API
This step converts any usages of `bootstrapModule` to the new, standalone-based `bootstrapApplication`. It also switches the root component to `standalone: true` and deletes the root NgModule. If the root module has any `providers` or `imports`, the migration attempts to copy as much of this configuration as possible into the new bootstrap call.
**Before:**
```typescript
// ./app/app.module.ts
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
```
```typescript
// ./app/app.component.ts
@Component({ selector: 'app', template: 'hello' })
export class AppComponent {}
```
```typescript
// ./main.ts
import { platformBrowser } from '@angular/platform-browser';
import { AppModule } from './app/app.module';
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
```
**After:**
```typescript
// ./app/app.module.ts
// Does not exist!
```
```typescript
// ./app/app.component.ts
@Component({ selector: 'app', template: 'hello', standalone: true })
export class AppComponent {}
```
```typescript
// ./main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent).catch(e => console.error(e));
```
## Common problems
Some common problems that may prevent the schematic from working correctly include:
* Compilation errors - if the project has compilation errors, Angular cannot analyze and migrate it correctly.
* Files not included in a tsconfig - the schematic determines which files to migrate by analyzing your project's `tsconfig.json` files. The schematic excludes any files not captured by a tsconfig.
* Code that cannot be statically analyzed - the schematic uses static analysis to understand your code and determine where to make changes. The migration may skip any classes with metadata that cannot be statically analyzed at build time.
## Limitations
Due to the size and complexity of the migration, there are some cases that the schematic cannot handle:
* Because unit tests are not ahead-of-time (AoT) compiled, `imports` added to components in unit tests might not be entirely correct.
* The schematic relies on direct calls to Angular APIs. The schematic cannot recognize custom wrappers around Angular APIs. For example, if there you define a custom `customConfigureTestModule` function that wraps `TestBed.configureTestingModule`, components it declares may not be recognized.
@reviewed 2023-02-15
+15 -1
View File
@@ -1132,5 +1132,19 @@
"start": "2022-12-06"
},
"name": "Angular Contributor Days"
},
{
"date": {
"start": "2023-11-07"
},
"linkUrl": "http://ng-poland.pl/",
"name": "NG Poland"
},
{
"date": {
"start": "2023-11-08"
},
"linkUrl": "http://js-poland.pl/",
"name": "JS Poland"
}
]
]
+3 -3
View File
@@ -587,8 +587,8 @@
"resources": {
"-KLIzGEp8Mh5W-FkiQnL": {
"desc": "A no-nonsense beginner's guide to building web applications with Angular and TypeScript",
"title": "Learning Angular - Third Edition",
"url": "https://www.packtpub.com/product/learning-angular-third-edition/9781839210662"
"title": "Learning Angular - Fourth Edition",
"url": "https://www.amazon.com/dp/1803240601"
},
"3ab": {
"desc": "More than 15 books from O'Reilly about Angular",
@@ -674,7 +674,7 @@
"angular-projects": {
"title": "Angular Projects - Second Edition",
"desc": "Build modern web apps by exploring Angular with 10 different projects and cutting-edge technologies",
"url": "https://www.packtpub.com/product/angular-projects-second-edition/9781800205260"
"url": "https://www.amazon.com/dp/1800205260"
},
"rangle-angular-training-book": {
"title": "Rangle’s Angular Training Book",
+14 -3
View File
@@ -312,9 +312,20 @@
"tooltip": "A list of developer guides for building Angular applications."
},
{
"url": "guide/standalone-components",
"title": "Standalone components",
"tooltip": "Standalone components, directives, and pipes"
"title": "Standalone",
"tooltip": "Learn about Angular's standalone APIs and how to convert your application to them.",
"children": [
{
"url": "guide/standalone-components",
"title": "Standalone components",
"tooltip": "Standalone components, directives, and pipes"
},
{
"url": "guide/standalone-migration",
"title": "Migrating to standalone",
"tooltip": "Migrate your application to Angular's standalone APIs"
}
]
},
{
"title": "Change detection",
@@ -329,7 +329,7 @@ This template binds directly to the component's `messageService`.
The messages look better after you add the private CSS styles to `messages.component.css` as listed in one of the ["final code review"](#final-code-review) tabs below.
## Add messages to hero service
## Add MessageService to HeroesComponent
The following example shows how to display a history of each time the user clicks on a hero.
This helps when you get to the next section on [Routing](tutorial/tour-of-heroes/toh-pt5).
@@ -251,7 +251,7 @@ The user should be able to get to these details in three ways.
1. By clicking a hero in the heroes list.
1. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.
This section enables navigation to the `HeroDetailComponent` and liberate it from the `HeroesComponent`.
This section enables navigation to the `HeroDetailComponent` and liberates it from the `HeroesComponent`.
### Delete *hero details* from `HeroesComponent`
@@ -299,7 +299,7 @@ The hero items in the `HeroesComponent` are `<li>` elements whose click events a
<code-example header="src/app/heroes/heroes.component.html (list with onSelect)" path="toh-pt4/src/app/heroes/heroes.component.html" region="list"></code-example>
Remove the `<li>` back to just its `*ngFor`.
Remove the inner HTML of `<li>`.
Wrap the badge and name in an anchor `<a>` element.
Add a `routerLink` attribute to the anchor that's the same as in the dashboard template.
+6 -6
View File
@@ -62,13 +62,13 @@
"private": true,
"dependencies": {
"@angular/animations": "15.2.0-next.4",
"@angular/cdk": "15.2.0-next.4",
"@angular/cdk": "15.2.0-rc.0",
"@angular/common": "15.2.0-next.4",
"@angular/compiler": "15.2.0-next.4",
"@angular/core": "15.2.0-next.4",
"@angular/elements": "15.2.0-next.4",
"@angular/forms": "15.2.0-next.4",
"@angular/material": "15.2.0-next.4",
"@angular/material": "15.2.0-rc.0",
"@angular/platform-browser": "15.2.0-next.4",
"@angular/platform-browser-dynamic": "15.2.0-next.4",
"@angular/router": "15.2.0-next.4",
@@ -85,7 +85,7 @@
"@angular-eslint/eslint-plugin": "^15.0.0",
"@angular-eslint/eslint-plugin-template": "^15.0.0",
"@angular-eslint/template-parser": "^15.0.0",
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8b49b0b38e67191b52367d8d0a6317d00b7d5100",
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#c57ba78b57b1a8e43b051e4f2225109b9a64e311",
"@angular/cli": "15.2.0-next.4",
"@angular/compiler-cli": "15.2.0-next.4",
"@bazel/bazelisk": "^1.7.5",
@@ -99,8 +99,8 @@
"@types/node": "^12.7.9",
"@types/trusted-types": "^2.0.2",
"@types/xregexp": "^4.3.0",
"@typescript-eslint/eslint-plugin": "5.52.0",
"@typescript-eslint/parser": "5.52.0",
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.54.0",
"archiver": "^5.3.0",
"assert": "^2.0.0",
"canonical-path": "1.0.0",
@@ -158,7 +158,7 @@
"ts-node": "^10.8.1",
"tsec": "^0.2.2",
"tslint": "~6.1.3",
"typescript": "~4.8.0",
"typescript": "~4.9.3",
"uglify-js": "^3.13.3",
"unist-util-filter": "^2.0.3",
"unist-util-source": "^3.0.0",
+1 -1
View File
@@ -11,7 +11,7 @@
<header>
<mat-toolbar #appToolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning">
<mat-toolbar-row class="notification-container">
<aio-notification notificationId="survey-january-2023" expirationDate="2023-03-30" [dismissOnContentClick]="true" (dismissed)="notificationDismissed()">
<aio-notification notificationId="survey-january-2023" expirationDate="2023-02-15" [dismissOnContentClick]="true" (dismissed)="notificationDismissed()">
<a href="https://goo.gle/angular-devsat-q1-2023">
<mat-icon class="icon" svgIcon="insert_comment" aria-label="Announcement"></mat-icon>
<span class="message">Help Angular by taking a <b>quick survey</b>!</span>
@@ -13,14 +13,14 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"angular": "^1.8.3",
"angular-in-memory-web-api": "~0.15.0",
"angular-route": "1.8.3",
@@ -29,9 +29,9 @@
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@types/angular": "^1.8.4",
"@types/angular-route": "^1.7.2",
"@types/jasmine": "~4.3.0",
@@ -46,6 +46,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -13,23 +13,23 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"angular-in-memory-web-api": "~0.15.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^16.11.35",
"copyfiles": "^2.4.1",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -13,24 +13,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/elements": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/elements": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"angular-in-memory-web-api": "~0.15.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^16.11.35",
"jasmine-core": "~4.5.0",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -15,24 +15,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/localize": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/localize": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"angular-in-memory-web-api": "~0.15.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^16.11.35",
"jasmine-core": "~4.5.0",
@@ -45,6 +45,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -13,24 +13,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/service-worker": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.1",
"@angular/common": "^15.2.0-rc.1",
"@angular/compiler": "^15.2.0-rc.1",
"@angular/core": "^15.2.0-rc.1",
"@angular/forms": "^15.2.0-rc.1",
"@angular/platform-browser": "^15.2.0-rc.1",
"@angular/platform-browser-dynamic": "^15.2.0-rc.1",
"@angular/router": "^15.2.0-rc.1",
"@angular/service-worker": "^15.2.0-rc.1",
"angular-in-memory-web-api": "~0.15.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.1",
"@angular/cli": "^15.2.0-rc.1",
"@angular/compiler-cli": "^15.2.0-rc.1",
"@types/jasmine": "~4.3.0",
"@types/node": "^16.11.35",
"jasmine-core": "~4.5.0",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -25,15 +25,15 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/upgrade": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"@angular/upgrade": "^15.2.0-rc.0",
"core-js": "^2.5.4",
"rxjs": "~7.8.0",
"systemjs": "~0.19.39",
@@ -42,7 +42,7 @@
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^13.2.0",
"@types/angular": "^1.8.4",
@@ -63,6 +63,6 @@
"protractor": "~7.0.0",
"rollup": "^2.70.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
@@ -17,15 +17,15 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/platform-server": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/platform-server": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"@nguniversal/express-engine": "^15.0.0-next.0",
"angular-in-memory-web-api": "~0.15.0",
"express": "^4.18.1",
@@ -34,9 +34,9 @@
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@nguniversal/builders": "^15.0.0",
"@types/express": "^4.17.13",
"@types/jasmine": "~4.3.0",
@@ -51,6 +51,6 @@
"karma-jasmine-html-reporter": "~2.0.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
+17 -17
View File
@@ -20,19 +20,19 @@
"license": "MIT",
"repository": {},
"dependencies": {
"@angular/animations": "^15.0.0-rc.1",
"@angular/common": "^15.0.0-rc.1",
"@angular/compiler": "^15.0.0-rc.1",
"@angular/core": "^15.0.0-rc.1",
"@angular/elements": "^15.0.0-rc.1",
"@angular/forms": "^15.0.0-rc.1",
"@angular/localize": "^15.0.0-rc.1",
"@angular/platform-browser": "^15.0.0-rc.1",
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
"@angular/platform-server": "^15.0.0-rc.1",
"@angular/router": "^15.0.0-rc.1",
"@angular/service-worker": "^15.0.0-rc.1",
"@angular/upgrade": "^15.0.0-rc.1",
"@angular/animations": "^15.2.0-rc.0",
"@angular/common": "^15.2.0-rc.0",
"@angular/compiler": "^15.2.0-rc.0",
"@angular/core": "^15.2.0-rc.0",
"@angular/elements": "^15.2.0-rc.0",
"@angular/forms": "^15.2.0-rc.0",
"@angular/localize": "^15.2.0-rc.0",
"@angular/platform-browser": "^15.2.0-rc.0",
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
"@angular/platform-server": "^15.2.0-rc.0",
"@angular/router": "^15.2.0-rc.0",
"@angular/service-worker": "^15.2.0-rc.0",
"@angular/upgrade": "^15.2.0-rc.0",
"@nguniversal/express-engine": "^15.0.0-next.0",
"angular": "^1.8.3",
"angular-in-memory-web-api": "~0.15.0",
@@ -46,9 +46,9 @@
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.0-rc.1",
"@angular/cli": "^15.0.0-rc.1",
"@angular/compiler-cli": "^15.0.0-rc.1",
"@angular-devkit/build-angular": "^15.2.0-rc.0",
"@angular/cli": "^15.2.0-rc.0",
"@angular/compiler-cli": "^15.2.0-rc.0",
"@nguniversal/builders": "^15.0.0",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^13.2.0",
@@ -81,6 +81,6 @@
"rollup-plugin-terser": "^7.0.2",
"source-map-explorer": "^2.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.8.0"
"typescript": "~4.9.3"
}
}
File diff suppressed because it is too large Load Diff
+497 -348
View File
File diff suppressed because it is too large Load Diff
@@ -3,8 +3,8 @@
"short_name": "Angular DevTools",
"name": "Angular DevTools",
"description": "Angular DevTools extends Chrome DevTools adding Angular specific debugging and profiling capabilities.",
"version": "1.0.6",
"version_name": "1.0.6",
"version": "1.0.7",
"version_name": "1.0.7",
"minimum_chrome_version": "102",
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
@@ -3,7 +3,7 @@
"short_name": "Angular DevTools",
"name": "Angular DevTools",
"description": "Angular DevTools extends Firefox DevTools adding Angular specific debugging and profiling capabilities.",
"version": "1.0.6",
"version": "1.0.7",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"icons": {
"16": "assets/icon16.png",
@@ -183,6 +183,9 @@ export const platformBrowser: (extraProviders?: StaticProvider[]) => PlatformRef
// @public
export function provideProtractorTestingSupport(): Provider[];
// @public
export const REMOVE_STYLES_ON_COMPONENT_DESTROY: InjectionToken<boolean>;
// @public
export interface SafeHtml extends SafeValue {
}
@@ -226,6 +229,7 @@ export class Title {
// @public
export class TransferState {
constructor();
get<T>(key: StateKey<T>, defaultValue: T): T;
hasKey<T>(key: StateKey<T>): boolean;
get isEmpty(): boolean;
+1 -1
View File
@@ -12,7 +12,7 @@
"aio-local": {
"uncompressed": {
"runtime": 4325,
"main": 466290,
"main": 466694,
"polyfills": 33836,
"styles": 74561,
"light-theme": 92890,
@@ -2,7 +2,7 @@
"cli-hello-world": {
"uncompressed": {
"runtime": 908,
"main": 125165,
"main": 126185,
"polyfills": 33824
}
},
@@ -16,46 +16,39 @@
"polyfills": 33848
}
},
"cli-hello-world-ivy-compat": {
"uncompressed": {
"runtime": 930,
"main": 132182,
"polyfills": 33957
}
},
"cli-hello-world-ivy-i18n": {
"uncompressed": {
"runtime": 926,
"main": 123543,
"main": 125026,
"polyfills": 34628
}
},
"cli-hello-world-lazy": {
"uncompressed": {
"runtime": 2734,
"main": 228122,
"polyfills": 33842,
"main": 228487,
"polyfills": 33810,
"src_app_lazy_lazy_routes_ts": 487
}
},
"forms": {
"uncompressed": {
"runtime": 888,
"main": 157604,
"polyfills": 33915
"main": 158000,
"polyfills": 33772
}
},
"animations": {
"uncompressed": {
"runtime": 898,
"main": 156355,
"main": 157699,
"polyfills": 33897
}
},
"standalone-bootstrap": {
"uncompressed": {
"runtime": 918,
"main": 84527,
"main": 85547,
"polyfills": 33945
}
},
@@ -1,13 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
@@ -1,46 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
@@ -1,7 +0,0 @@
load("//integration:index.bzl", "ng_integration_test")
ng_integration_test(
name = "test",
setup_chromium = True,
track_payload_size = "cli-hello-world-ivy-compat",
)
@@ -1,27 +0,0 @@
# CliHelloWorldIvy
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.0-next.9.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](https://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/main/README.md).
@@ -1,126 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"cli-hello-world-ivy-compat": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": [],
"progress": false
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "cli-hello-world-ivy-compat:build"
},
"configurations": {
"production": {
"browserTarget": "cli-hello-world-ivy-compat:build:production"
},
"ci": {},
"ci-production": {
"browserTarget": "cli-hello-world-ivy-compat:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "cli-hello-world-ivy-compat:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": [],
"progress": false,
"watch": false
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "cli-hello-world-ivy-compat:serve",
"webdriverUpdate": false
},
"configurations": {
"production": {
"devServerTarget": "cli-hello-world-ivy-compat:serve:production"
},
"ci": {
"devServerTarget": "cli-hello-world-ivy-compat:serve:ci"
},
"ci-production": {
"devServerTarget": "cli-hello-world-ivy-compat:serve:ci-production"
}
}
}
}
}
}
}
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
##### Test Debug Utility #####
##############################
# Use this script to run the ngcc integration test locally
# in isolation from the other integration tests.
# This is useful when debugging the ngcc code-base.
set -u -e -o pipefail
cd "$(dirname "$0")"
# Go to the project directory and build the release packages.
(cd $(pwd)/../../ && yarn build)
# Workaround https://github.com/yarnpkg/yarn/issues/2165
# Yarn will cache file://dist URIs and not update Angular code
readonly cache=../.yarn_local_cache
function rm_cache {
rm -rf $cache
}
rm_cache
mkdir $cache
trap rm_cache EXIT
rm -rf dist
rm -rf node_modules
yarn install --cache-folder $cache
yarn test
@@ -1,37 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
chromeDriver: process.env.CHROMEDRIVER_BIN,
capabilities: {
browserName: 'chrome',
chromeOptions: {
binary: process.env.CHROME_BIN,
// See /integration/README.md#browser-tests for more info on these args
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
},
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
@@ -1,28 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('cli-hello-world-ivy-compat App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('cli-hello-world-ivy-compat app is running!');
});
it('the percent pipe should work', () => {
page.navigateTo();
expect(page.getPipeContent()).toEqual('100 % awesome');
})
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
@@ -1,15 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
getPipeContent() {
return element(by.css('app-root p')).getText();
}
}
@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
@@ -1,39 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/latest-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
// See /integration/README.md#browser-tests for more info on these args
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
}
},
browsers: ['ChromeHeadlessNoSandbox'],
singleRun: false,
restartOnFileChange: true
});
};
@@ -1,49 +0,0 @@
{
"name": "cli-hello-world-ivy-compat",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"build": "ng build --configuration production",
"e2e": "ng e2e --port 0",
"lint": "ng lint",
"ng": "ng",
"start": "ng serve",
"pretest": "ng version",
"test": "ng test && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production"
},
"private": true,
"dependencies": {
"@angular/animations": "file:../../dist/packages-dist/animations",
"@angular/common": "file:../../dist/packages-dist/common",
"@angular/compiler": "file:../../dist/packages-dist/compiler",
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/forms": "file:../../dist/packages-dist/forms",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/router": "file:../../dist/packages-dist/router",
"rxjs": "file:../../node_modules/rxjs",
"tslib": "file:../../node_modules/tslib",
"zone.js": "file:../../dist/zone.js-dist/archive/zone.js.tgz"
},
"devDependencies": {
"@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular",
"@angular/cli": "file:../../node_modules/@angular/cli",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "file:../../node_modules/@types/jasmine",
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
"@types/node": "file:../../node_modules/@types/node",
"codelyzer": "5.2.0",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "4.4.0",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.0",
"karma-jasmine": "2.0.1",
"karma-jasmine-html-reporter": "1.4.2",
"protractor": "file:../../node_modules/protractor",
"ts-node": "8.3.0",
"tslint": "5.18.0",
"typescript": "file:../../node_modules/typescript"
}
}
@@ -1,507 +0,0 @@
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * Delete the template below * * * * * * * * * * -->
<!-- * * * * * * * to get started with your project! * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<style>
:host {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 14px;
color: #333;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 8px 0;
}
p {
margin: 0;
}
.spacer {
flex: 1;
}
.toolbar {
height: 60px;
margin: -8px;
display: flex;
align-items: center;
background-color: #1976d2;
color: white;
font-weight: 600;
}
.toolbar img {
margin: 0 16px;
}
.toolbar #twitter-logo {
height: 40px;
margin: 0 16px;
}
.toolbar #twitter-logo:hover {
opacity: 0.8;
}
.content {
display: flex;
margin: 32px auto;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;
}
svg.material-icons {
height: 24px;
width: auto;
}
svg.material-icons:not(:last-child) {
margin-right: 8px;
}
.card svg.material-icons path {
fill: #888;
}
.card-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 16px;
}
.card {
border-radius: 4px;
border: 1px solid #eee;
background-color: #fafafa;
height: 40px;
width: 200px;
margin: 0 8px 16px;
padding: 16px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
transition: all 0.2s ease-in-out;
line-height: 24px;
}
.card-container .card:not(:last-child) {
margin-right: 0;
}
.card.card-small {
height: 16px;
width: 168px;
}
.card-container .card:not(.highlight-card) {
cursor: pointer;
}
.card-container .card:not(.highlight-card):hover {
transform: translateY(-3px);
box-shadow: 0 4px 17px rgba(black, 0.35);
}
.card-container .card:not(.highlight-card):hover .material-icons path {
fill: rgb(105, 103, 103);
}
.card.highlight-card {
background-color: #1976d2;
color: white;
font-weight: 600;
border: none;
width: auto;
min-width: 30%;
position: relative;
}
.card.card.highlight-card span {
margin-left: 60px;
}
svg#rocket {
width: 80px;
position: absolute;
left: -10px;
top: -24px;
}
svg#rocket-smoke {
height: calc(100vh - 95px);
position: absolute;
top: 10px;
right: 180px;
z-index: -10;
}
a,
a:visited,
a:hover {
color: #1976d2;
text-decoration: none;
}
a:hover {
color: #125699;
}
.terminal {
position: relative;
width: 80%;
max-width: 600px;
border-radius: 6px;
padding-top: 45px;
margin-top: 8px;
overflow: hidden;
background-color: rgb(15, 15, 16);
}
.terminal::before {
content: "\2022 \2022 \2022";
position: absolute;
top: 0;
left: 0;
height: 4px;
background: rgb(58, 58, 58);
color: #c2c3c4;
width: 100%;
font-size: 2rem;
line-height: 0;
padding: 14px 0;
text-indent: 4px;
}
.terminal pre {
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
color: white;
padding: 0 1rem 1rem;
margin: 0;
}
.circle-link {
height: 40px;
width: 40px;
border-radius: 40px;
margin: 8px;
background-color: white;
border: 1px solid #eeeeee;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
transition: 1s ease-out;
}
.circle-link:hover {
transform: translateY(-0.25rem);
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
}
footer {
margin-top: 8px;
display: flex;
align-items: center;
line-height: 20px;
}
footer a {
display: flex;
align-items: center;
}
.github-star-badge {
color: #24292e;
display: flex;
align-items: center;
font-size: 12px;
padding: 3px 10px;
border: 1px solid rgba(27,31,35,.2);
border-radius: 3px;
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
margin-left: 4px;
font-weight: 600;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
}
.github-star-badge:hover {
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
border-color: rgba(27,31,35,.35);
background-position: -.5em;
}
.github-star-badge .material-icons {
height: 16px;
width: 16px;
margin-right: 4px;
}
svg#clouds {
position: fixed;
bottom: -160px;
left: -230px;
z-index: -10;
width: 1920px;
}
/* Responsive Styles */
@media screen and (max-width: 767px) {
.card-container > *:not(.circle-link) ,
.terminal {
width: 100%;
}
.card:not(.highlight-card) {
height: 16px;
margin: 8px 0;
}
.card.highlight-card span {
margin-left: 72px;
}
svg#rocket-smoke {
right: 120px;
transform: rotate(-5deg);
}
}
@media screen and (max-width: 575px) {
svg#rocket-smoke {
display: none;
visibility: hidden;
}
}
</style>
<p>{{ 1 | percent }} awesome</p>
<!-- Toolbar -->
<div class="toolbar" role="banner">
<img
width="40"
alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
/>
<span>Welcome</span>
<div class="spacer"></div>
<a aria-label="Angular on twitter" target="_blank" rel="noopener" href="https://twitter.com/angular" title="Twitter">
<svg id="twitter-logo" height="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<rect width="400" height="400" fill="none"/>
<path d="M153.62,301.59c94.34,0,145.94-78.16,145.94-145.94,0-2.22,0-4.43-.15-6.63A104.36,104.36,0,0,0,325,122.47a102.38,102.38,0,0,1-29.46,8.07,51.47,51.47,0,0,0,22.55-28.37,102.79,102.79,0,0,1-32.57,12.45,51.34,51.34,0,0,0-87.41,46.78A145.62,145.62,0,0,1,92.4,107.81a51.33,51.33,0,0,0,15.88,68.47A50.91,50.91,0,0,1,85,169.86c0,.21,0,.43,0,.65a51.31,51.31,0,0,0,41.15,50.28,51.21,51.21,0,0,1-23.16.88,51.35,51.35,0,0,0,47.92,35.62,102.92,102.92,0,0,1-63.7,22A104.41,104.41,0,0,1,75,278.55a145.21,145.21,0,0,0,78.62,23" fill="#fff"/>
</svg>
</a>
</div>
<div class="content" role="main">
<!-- Highlight Card -->
<div class="card highlight-card card-small">
<svg id="rocket" alt="Rocket Ship" xmlns="http://www.w3.org/2000/svg" width="101.678" height="101.678" viewBox="0 0 101.678 101.678">
<g id="Group_83" data-name="Group 83" transform="translate(-141 -696)">
<circle id="Ellipse_8" data-name="Ellipse 8" cx="50.839" cy="50.839" r="50.839" transform="translate(141 696)" fill="#dd0031"/>
<g id="Group_47" data-name="Group 47" transform="translate(165.185 720.185)">
<path id="Path_33" data-name="Path 33" d="M3.4,42.615a3.084,3.084,0,0,0,3.553,3.553,21.419,21.419,0,0,0,12.215-6.107L9.511,30.4A21.419,21.419,0,0,0,3.4,42.615Z" transform="translate(0.371 3.363)" fill="#fff"/>
<path id="Path_34" data-name="Path 34" d="M53.3,3.221A3.09,3.09,0,0,0,50.081,0,48.227,48.227,0,0,0,18.322,13.437c-6-1.666-14.991-1.221-18.322,7.218A33.892,33.892,0,0,1,9.439,25.1l-.333.666a3.013,3.013,0,0,0,.555,3.553L23.985,43.641a2.9,2.9,0,0,0,3.553.555l.666-.333A33.892,33.892,0,0,1,32.647,53.3c8.55-3.664,8.884-12.326,7.218-18.322A48.227,48.227,0,0,0,53.3,3.221ZM34.424,9.772a6.439,6.439,0,1,1,9.106,9.106,6.368,6.368,0,0,1-9.106,0A6.467,6.467,0,0,1,34.424,9.772Z" transform="translate(0 0.005)" fill="#fff"/>
</g>
</g>
</svg>
<span>{{ title }} app is running!</span>
<svg id="rocket-smoke" alt="Rocket Ship Smoke" xmlns="http://www.w3.org/2000/svg" width="516.119" height="1083.632" viewBox="0 0 516.119 1083.632">
<path id="Path_40" data-name="Path 40" d="M644.6,141S143.02,215.537,147.049,870.207s342.774,201.755,342.774,201.755S404.659,847.213,388.815,762.2c-27.116-145.51-11.551-384.124,271.9-609.1C671.15,139.365,644.6,141,644.6,141Z" transform="translate(-147.025 -140.939)" fill="#f5f5f5"/>
</svg>
</div>
<!-- Resources -->
<h2>Resources</h2>
<p>Here are some links to help you get started:</p>
<div class="card-container">
<a class="card" target="_blank" rel="noopener" href="https://angular.io/tutorial">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></svg>
<span>Learn Angular</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a>
<a class="card" target="_blank" rel="noopener" href="https://angular.io/cli">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
<span>CLI Documentation</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</a>
<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg>
<span>Angular Blog</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</a>
</div>
<!-- Next Steps -->
<h2>Next Steps</h2>
<p>What do you want to do next with your app?</p>
<input type="hidden" #selection>
<div class="card-container">
<div class="card card-small" (click)="selection.value = 'component'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>New Component</span>
</div>
<div class="card card-small" (click)="selection.value = 'material'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Angular Material</span>
</div>
<div class="card card-small" (click)="selection.value = 'pwa'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Add PWA Support</span>
</div>
<div class="card card-small" (click)="selection.value = 'dependency'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Add Dependency</span>
</div>
<div class="card card-small" (click)="selection.value = 'test'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Run and Watch Tests</span>
</div>
<div class="card card-small" (click)="selection.value = 'build'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Build for Production</span>
</div>
</div>
<!-- Terminal -->
<div class="terminal" [ngSwitch]="selection.value">
<pre *ngSwitchDefault>ng generate component xyz</pre>
<pre *ngSwitchCase="'material'">ng add @angular/material</pre>
<pre *ngSwitchCase="'pwa'">ng add @angular/pwa</pre>
<pre *ngSwitchCase="'dependency'">ng add _____</pre>
<pre *ngSwitchCase="'test'">ng test</pre>
<pre *ngSwitchCase="'build'">ng build --prod</pre>
</div>
<!-- Links -->
<div class="card-container">
<a class="circle-link" title="Animations" href="https://angular.io/guide/animations" target="_blank" rel="noopener">
<svg id="Group_20" data-name="Group 20" xmlns="http://www.w3.org/2000/svg" width="21.813" height="23.453" viewBox="0 0 21.813 23.453">
<path id="Path_15" data-name="Path 15" d="M4099.584,972.736h0l-10.882,3.9,1.637,14.4,9.245,5.153,9.245-5.153,1.686-14.4Z" transform="translate(-4088.702 -972.736)" fill="#ffa726"/>
<path id="Path_16" data-name="Path 16" d="M4181.516,972.736v23.453l9.245-5.153,1.686-14.4Z" transform="translate(-4170.633 -972.736)" fill="#fb8c00"/>
<path id="Path_17" data-name="Path 17" d="M4137.529,1076.127l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1058.315)" fill="#ffe0b2"/>
<path id="Path_18" data-name="Path 18" d="M4137.529,1051.705l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1036.757)" fill="#fff3e0"/>
<path id="Path_19" data-name="Path 19" d="M4137.529,1027.283l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1015.199)" fill="#fff"/>
</svg>
</a>
<a class="circle-link" title="CLI" href="https://cli.angular.io/" target="_blank" rel="noopener">
<svg alt="Angular CLI Logo" xmlns="http://www.w3.org/2000/svg" width="21.762" height="23.447" viewBox="0 0 21.762 23.447">
<g id="Group_21" data-name="Group 21" transform="translate(0)">
<path id="Path_20" data-name="Path 20" d="M2660.313,313.618h0l-10.833,3.9,1.637,14.4,9.2,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-2649.48 -313.618)" fill="#37474f"/>
<path id="Path_21" data-name="Path 21" d="M2741.883,313.618v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-2731.05 -313.618)" fill="#263238"/>
<path id="Path_22" data-name="Path 22" d="M2692.293,379.169h11.724V368.618h-11.724Zm11.159-.6h-10.608v-9.345h10.621v9.345Z" transform="translate(-2687.274 -362.17)" fill="#fff"/>
<path id="Path_23" data-name="Path 23" d="M2709.331,393.688l.4.416,2.265-2.28-2.294-2.294-.4.4,1.893,1.893Z" transform="translate(-2702.289 -380.631)" fill="#fff"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="3.517" height="0.469" transform="translate(9.709 13.744)" fill="#fff"/>
</g>
</svg>
</a>
<a class="circle-link" title="Protractor" href="https://www.protractortest.org/" target="_blank" rel="noopener">
<svg alt="Angular Protractor Logo" xmlns="http://www.w3.org/2000/svg" width="21.81" height="23.447" viewBox="0 0 21.81 23.447">
<g id="Group_26" data-name="Group 26" transform="translate(0)">
<path id="Path_28" data-name="Path 28" d="M4620.155,311.417h0l-10.881,3.9,1.637,14.4,9.244,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-4609.274 -311.417)" fill="#e13439"/>
<path id="Path_29" data-name="Path 29" d="M4702.088,311.417v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-4691.207 -311.417)" fill="#b52f32"/>
<path id="Path_30" data-name="Path 30" d="M4651.044,369.58v-.421h1.483a7.6,7.6,0,0,0-2.106-5.052l-1.123,1.123-.3-.3,1.122-1.121a7.588,7.588,0,0,0-4.946-2.055v1.482h-.421v-1.485a7.589,7.589,0,0,0-5.051,2.058l1.122,1.121-.3.3-1.123-1.123a7.591,7.591,0,0,0-2.106,5.052h1.482v.421h-1.489v1.734h15.241V369.58Zm-10.966-.263a4.835,4.835,0,0,1,9.67,0Z" transform="translate(-4634.008 -355.852)" fill="#fff"/>
</g>
</svg>
</a>
<a class="circle-link" title="Find a Local Meetup" href="https://www.meetup.com/find/?keywords=angular" target="_blank" rel="noopener">
<svg alt="Meetup Logo" xmlns="http://www.w3.org/2000/svg" width="24.607" height="23.447" viewBox="0 0 24.607 23.447">
<path id="logo--mSwarm" d="M21.221,14.95A4.393,4.393,0,0,1,17.6,19.281a4.452,4.452,0,0,1-.8.069c-.09,0-.125.035-.154.117a2.939,2.939,0,0,1-2.506,2.091,2.868,2.868,0,0,1-2.248-.624.168.168,0,0,0-.245-.005,3.926,3.926,0,0,1-2.589.741,4.015,4.015,0,0,1-3.7-3.347,2.7,2.7,0,0,1-.043-.38c0-.106-.042-.146-.143-.166a3.524,3.524,0,0,1-1.516-.69A3.623,3.623,0,0,1,2.23,14.557a3.66,3.66,0,0,1,1.077-3.085.138.138,0,0,0,.026-.2,3.348,3.348,0,0,1-.451-1.821,3.46,3.46,0,0,1,2.749-3.28.44.44,0,0,0,.355-.281,5.072,5.072,0,0,1,3.863-3,5.028,5.028,0,0,1,3.555.666.31.31,0,0,0,.271.03A4.5,4.5,0,0,1,18.3,4.7a4.4,4.4,0,0,1,1.334,2.751,3.658,3.658,0,0,1,.022.706.131.131,0,0,0,.1.157,2.432,2.432,0,0,1,1.574,1.645,2.464,2.464,0,0,1-.7,2.616c-.065.064-.051.1-.014.166A4.321,4.321,0,0,1,21.221,14.95ZM13.4,14.607a2.09,2.09,0,0,0,1.409,1.982,4.7,4.7,0,0,0,1.275.221,1.807,1.807,0,0,0,.9-.151.542.542,0,0,0,.321-.545.558.558,0,0,0-.359-.534,1.2,1.2,0,0,0-.254-.078c-.262-.047-.526-.086-.787-.138a.674.674,0,0,1-.617-.75,3.394,3.394,0,0,1,.218-1.109c.217-.658.509-1.286.79-1.918a15.609,15.609,0,0,0,.745-1.86,1.95,1.95,0,0,0,.06-1.073,1.286,1.286,0,0,0-1.051-1.033,1.977,1.977,0,0,0-1.521.2.339.339,0,0,1-.446-.042c-.1-.092-.2-.189-.307-.284a1.214,1.214,0,0,0-1.643-.061,7.563,7.563,0,0,1-.614.512A.588.588,0,0,1,10.883,8c-.215-.115-.437-.215-.659-.316a2.153,2.153,0,0,0-.695-.248A2.091,2.091,0,0,0,7.541,8.562a9.915,9.915,0,0,0-.405.986c-.559,1.545-1.015,3.123-1.487,4.7a1.528,1.528,0,0,0,.634,1.777,1.755,1.755,0,0,0,1.5.211,1.35,1.35,0,0,0,.824-.858c.543-1.281,1.032-2.584,1.55-3.875.142-.355.28-.712.432-1.064a.548.548,0,0,1,.851-.24.622.622,0,0,1,.185.539,2.161,2.161,0,0,1-.181.621c-.337.852-.68,1.7-1.018,2.552a2.564,2.564,0,0,0-.173.528.624.624,0,0,0,.333.71,1.073,1.073,0,0,0,.814.034,1.22,1.22,0,0,0,.657-.655q.758-1.488,1.511-2.978.35-.687.709-1.37a1.073,1.073,0,0,1,.357-.434.43.43,0,0,1,.463-.016.373.373,0,0,1,.153.387.7.7,0,0,1-.057.236c-.065.157-.127.316-.2.469-.42.883-.846,1.763-1.262,2.648A2.463,2.463,0,0,0,13.4,14.607Zm5.888,6.508a1.09,1.09,0,0,0-2.179.006,1.09,1.09,0,0,0,2.179-.006ZM1.028,12.139a1.038,1.038,0,1,0,.01-2.075,1.038,1.038,0,0,0-.01,2.075ZM13.782.528a1.027,1.027,0,1,0-.011,2.055A1.027,1.027,0,0,0,13.782.528ZM22.21,6.95a.882.882,0,0,0-1.763.011A.882.882,0,0,0,22.21,6.95ZM4.153,4.439a.785.785,0,1,0,.787-.78A.766.766,0,0,0,4.153,4.439Zm8.221,18.22a.676.676,0,1,0-.677.666A.671.671,0,0,0,12.374,22.658ZM22.872,12.2a.674.674,0,0,0-.665.665.656.656,0,0,0,.655.643.634.634,0,0,0,.655-.644A.654.654,0,0,0,22.872,12.2ZM7.171-.123A.546.546,0,0,0,6.613.43a.553.553,0,1,0,1.106,0A.539.539,0,0,0,7.171-.123ZM24.119,9.234a.507.507,0,0,0-.493.488.494.494,0,0,0,.494.494.48.48,0,0,0,.487-.483A.491.491,0,0,0,24.119,9.234Zm-19.454,9.7a.5.5,0,0,0-.488-.488.491.491,0,0,0-.487.5.483.483,0,0,0,.491.479A.49.49,0,0,0,4.665,18.936Z" transform="translate(0 0.123)" fill="#f64060"/>
</svg>
</a>
<a class="circle-link" title="Join the Conversation on Gitter" href="https://gitter.im/angular/angular" target="_blank" rel="noopener">
<svg alt="Gitter Logo" xmlns="http://www.w3.org/2000/svg" width="19.447" height="19.447" viewBox="0 0 19.447 19.447">
<g id="Group_40" data-name="Group 40" transform="translate(-1612 -405)">
<rect id="Rectangle_19" data-name="Rectangle 19" width="19.447" height="19.447" transform="translate(1612 405)" fill="#e60257"/>
<g id="gitter" transform="translate(1617.795 408.636)">
<g id="Group_33" data-name="Group 33" transform="translate(0 0)">
<rect id="Rectangle_15" data-name="Rectangle 15" width="1.04" height="9.601" transform="translate(2.304 2.324)" fill="#fff"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="1.04" height="9.601" transform="translate(4.607 2.324)" fill="#fff"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="1.04" height="4.648" transform="translate(6.91 2.324)" fill="#fff"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="1.04" height="6.971" transform="translate(0 0)" fill="#fff"/>
</g>
</g>
</g>
</svg>
</a>
</div>
<!-- Footer -->
<footer>
Love Angular?&nbsp;
<a href="https://github.com/angular/angular" target="_blank" rel="noopener"> Give our repo a star.
<div class="github-star-badge">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
Star
</div>
</a>
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" fill="#1976d2"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</a>
</footer>
<svg id="clouds" alt="Gray Clouds Background" xmlns="http://www.w3.org/2000/svg" width="2611.084" height="485.677" viewBox="0 0 2611.084 485.677">
<path id="Path_39" data-name="Path 39" d="M2379.709,863.793c10-93-77-171-168-149-52-114-225-105-264,15-75,3-140,59-152,133-30,2.83-66.725,9.829-93.5,26.25-26.771-16.421-63.5-23.42-93.5-26.25-12-74-77-130-152-133-39-120-212-129-264-15-54.084-13.075-106.753,9.173-138.488,48.9-31.734-39.726-84.4-61.974-138.487-48.9-52-114-225-105-264,15a162.027,162.027,0,0,0-103.147,43.044c-30.633-45.365-87.1-72.091-145.206-58.044-52-114-225-105-264,15-75,3-140,59-152,133-53,5-127,23-130,83-2,42,35,72,70,86,49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33,61.112,8.015,113.854-5.72,150.492-29.764a165.62,165.62,0,0,0,110.861-3.236c47,94,178,113,251,33,31.385,4.116,60.563,2.495,86.487-3.311,25.924,5.806,55.1,7.427,86.488,3.311,73,80,204,61,251-33a165.625,165.625,0,0,0,120,0c51,13,108,15,157-5a147.188,147.188,0,0,0,33.5-18.694,147.217,147.217,0,0,0,33.5,18.694c49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33C2446.709,1093.793,2554.709,922.793,2379.709,863.793Z" transform="translate(142.69 -634.312)" fill="#eee"/>
</svg>
</div>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
@@ -1,32 +0,0 @@
import {TestBed, waitForAsync} from '@angular/core/testing';
import {AppComponent} from './app.component';
describe('AppComponent', () => {
beforeEach(waitForAsync(() => {
TestBed
.configureTestingModule({
declarations: [AppComponent],
})
.compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'cli-hello-world-ivy-compat'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('cli-hello-world-ivy-compat');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('.content span').textContent)
.toContain('cli-hello-world-ivy-compat app is running!');
});
});
@@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'cli-hello-world-ivy-compat';
}
@@ -1,21 +0,0 @@
import { BrowserModule } from '@angular/platform-browser';
import { LOCALE_ID, NgModule } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import { AppComponent } from './app.component';
// adding this code to detect issues like https://github.com/angular/angular-cli/issues/10322
registerLocaleData(localeFr);
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [{ provide: LOCALE_ID, useValue: 'fr' }],
bootstrap: [AppComponent]
})
export class AppModule { }
@@ -1,3 +0,0 @@
export const environment = {
production: true
};
@@ -1,16 +0,0 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 B

@@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CliHelloWorldIvyCompat</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
@@ -1,12 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
@@ -1 +0,0 @@
/* You can add global styles to this file, and also import other style files */
@@ -1,14 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
@@ -1,13 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}
@@ -1,26 +0,0 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
@@ -1,17 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
@@ -1,91 +0,0 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}
+5 -7
View File
@@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "15.2.0-next.4",
"version": "15.2.1",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
@@ -49,12 +49,12 @@
"@angular-devkit/core": "15.2.0-next.4",
"@angular-devkit/schematics": "15.2.0-next.4",
"@angular/animations-12": "npm:@angular/animations@12.2.13",
"@angular/cdk": "15.2.0-next.4",
"@angular/cdk": "15.2.0-rc.0",
"@angular/cli": "15.2.0-next.4",
"@angular/common-12": "npm:@angular/common@12.2.13",
"@angular/core-12": "npm:@angular/core@12.2.13",
"@angular/forms-12": "npm:@angular/forms@12.2.13",
"@angular/material": "15.2.0-next.4",
"@angular/material": "15.2.0-rc.0",
"@angular/platform-browser-12": "npm:@angular/platform-browser@12.2.13",
"@angular/platform-browser-dynamic-12": "npm:@angular/platform-browser-dynamic@12.2.13",
"@angular/platform-server-12": "npm:@angular/platform-server@12.2.13",
@@ -142,7 +142,6 @@
"karma-requirejs": "^1.1.0",
"karma-sourcemap-loader": "^0.3.7",
"magic-string": "0.27.0",
"materialize-css": "1.0.0",
"memo-decorator": "^2.0.1",
"ngx-flamegraph": "0.0.11",
"nodejs-websocket": "^1.7.2",
@@ -158,7 +157,6 @@
"shelljs": "^0.8.5",
"source-map": "0.7.4",
"source-map-support": "0.5.21",
"start-server-and-test": "^1.10.11",
"systemjs": "0.18.10",
"terser": "^5.8.0",
"tmp": "0.2.1",
@@ -174,8 +172,8 @@
},
"// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.",
"devDependencies": {
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8b49b0b38e67191b52367d8d0a6317d00b7d5100",
"@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#045097fc5c47d0241b157d3b904df316c737d654",
"@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#c57ba78b57b1a8e43b051e4f2225109b9a64e311",
"@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#99a6d6b96288d3c51b3a99164c16704788b7cb22",
"@bazel/bazelisk": "^1.7.5",
"@bazel/buildifier": "^5.0.0",
"@bazel/ibazel": "^0.16.0",
+1 -7
View File
@@ -83,13 +83,7 @@ export class HttpClientXsrfModule {
* with supporting services for HTTP communications.
*/
providers: [
provideHttpClient(
withInterceptorsFromDi(),
withXsrfConfiguration({
cookieName: XSRF_DEFAULT_COOKIE_NAME,
headerName: XSRF_DEFAULT_HEADER_NAME,
}),
),
provideHttpClient(withInterceptorsFromDi()),
],
})
export class HttpClientModule {
+8 -3
View File
@@ -301,9 +301,14 @@ export function createLocation() {
}
function _stripBasePath(basePath: string, url: string): string {
return basePath && new RegExp(`^${basePath}([/;?#]|$)`).test(url) ?
url.substring(basePath.length) :
url;
if (!basePath || !url.startsWith(basePath)) {
return url;
}
const strippedUrl = url.substring(basePath.length);
if (strippedUrl === '' || ['/', ';', '?', '#'].includes(strippedUrl[0])) {
return strippedUrl;
}
return url;
}
function _stripIndexHtml(url: string): string {
@@ -286,5 +286,18 @@ describe('Location Class', () => {
expect(location.normalize(baseHref + matrixParams)).toBe(matrixParams);
expect(location.normalize(baseHref + fragment)).toBe(fragment);
});
it('in case APP_BASE_HREF contains characters that have special meaning in a regex', () => {
const baseHref = 'c:/users/name(test)/en';
const path = '/test-path';
TestBed.configureTestingModule({providers: [{provide: APP_BASE_HREF, useValue: baseHref}]});
const location = TestBed.inject(Location);
expect(location.normalize(path)).toBe(path);
expect(location.normalize(baseHref)).toBe('');
expect(location.normalize(baseHref + path)).toBe(path);
});
});
});
+2 -1
View File
@@ -7,11 +7,12 @@
*/
import {Location, LocationStrategy, PlatformLocation} from '@angular/common';
import {ɵisPromise as isPromise} from '@angular/core';
import {UpgradeModule} from '@angular/upgrade/static';
import {ReplaySubject} from 'rxjs';
import {UrlCodec} from './params';
import {deepEqual, isAnchor, isPromise} from './utils';
import {deepEqual, isAnchor} from './utils';
const PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/;
const DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
-6
View File
@@ -30,9 +30,3 @@ export function deepEqual(a: any, b: any): boolean {
export function isAnchor(el: (Node&ParentNode)|Element|null): el is HTMLAnchorElement {
return (<HTMLAnchorElement>el).href !== undefined;
}
export function isPromise<T = any>(obj: any): obj is Promise<T> {
// allow any Promise/A+ compliant thenable.
// It's up to the caller to ensure that obj.then conforms to the spec
return !!obj && typeof obj.then === 'function';
}
@@ -318,8 +318,8 @@ export class ComponentDecoratorHandler implements
template = preanalyzed;
} else {
const templateDecl = parseTemplateDeclaration(
decorator, component, containingFile, this.evaluator, this.resourceLoader,
this.defaultPreserveWhitespaces);
node, decorator, component, containingFile, this.evaluator, this.depTracker,
this.resourceLoader, this.defaultPreserveWhitespaces);
template = extractTemplate(
node, templateDecl, this.evaluator, this.depTracker, this.resourceLoader, {
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
@@ -353,6 +353,13 @@ export class ComponentDecoratorHandler implements
this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
}
} catch {
if (this.depTracker !== null) {
// The analysis of this file cannot be re-used if one of the style URLs could
// not be resolved or loaded. Future builds should re-analyze and re-attempt
// resolution/loading.
this.depTracker.recordDependencyAnalysisFailure(node.getSourceFile());
}
if (diagnostics === undefined) {
diagnostics = [];
}
@@ -262,9 +262,9 @@ function parseExtractedTemplate(
}
export function parseTemplateDeclaration(
decorator: Decorator, component: Map<string, ts.Expression>, containingFile: string,
evaluator: PartialEvaluator, resourceLoader: ResourceLoader,
defaultPreserveWhitespaces: boolean): TemplateDeclaration {
node: ClassDeclaration, decorator: Decorator, component: Map<string, ts.Expression>,
containingFile: string, evaluator: PartialEvaluator, depTracker: DependencyTracker|null,
resourceLoader: ResourceLoader, defaultPreserveWhitespaces: boolean): TemplateDeclaration {
let preserveWhitespaces: boolean = defaultPreserveWhitespaces;
if (component.has('preserveWhitespaces')) {
const expr = component.get('preserveWhitespaces')!;
@@ -305,6 +305,12 @@ export function parseTemplateDeclaration(
resolvedTemplateUrl: resourceUrl,
};
} catch (e) {
if (depTracker !== null) {
// The analysis of this file cannot be re-used if the template URL could
// not be resolved. Future builds should re-analyze and re-attempt resolution.
depTracker.recordDependencyAnalysisFailure(node.getSourceFile());
}
throw makeResourceNotFoundError(
templateUrl, templateUrlExpr, ResourceTypeForDiagnostics.Template);
}
@@ -348,7 +354,7 @@ export function preloadAndParseTemplate(
if (templatePromise !== undefined) {
return templatePromise.then(() => {
const templateDecl = parseTemplateDeclaration(
decorator, component, containingFile, evaluator, resourceLoader,
node, decorator, component, containingFile, evaluator, depTracker, resourceLoader,
defaultPreserveWhitespaces);
const template =
extractTemplate(node, templateDecl, evaluator, depTracker, resourceLoader, options);
@@ -359,12 +365,18 @@ export function preloadAndParseTemplate(
return Promise.resolve(null);
}
} catch (e) {
if (depTracker !== null) {
// The analysis of this file cannot be re-used if the template URL could
// not be resolved. Future builds should re-analyze and re-attempt resolution.
depTracker.recordDependencyAnalysisFailure(node.getSourceFile());
}
throw makeResourceNotFoundError(
templateUrl, templateUrlExpr, ResourceTypeForDiagnostics.Template);
}
} else {
const templateDecl = parseTemplateDeclaration(
decorator, component, containingFile, evaluator, resourceLoader,
node, decorator, component, containingFile, evaluator, depTracker, resourceLoader,
defaultPreserveWhitespaces);
const template =
extractTemplate(node, templateDecl, evaluator, depTracker, resourceLoader, options);
@@ -10,6 +10,7 @@ export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAl
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
export {DefaultImportTracker} from './src/default';
export {AbsoluteModuleStrategy, assertSuccessfulReferenceEmit, EmittedReference, FailedEmitResult, ImportedFile, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitKind, ReferenceEmitResult, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter';
export {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './src/patch_alias_reference_resolution';
export {Reexport} from './src/reexport';
export {OwningModule, Reference} from './src/references';
export {ModuleResolver} from './src/resolver';
@@ -11,6 +11,8 @@ import ts from 'typescript';
import {getSourceFile} from '../../util/src/typescript';
import {loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution';
const DefaultImportDeclaration = Symbol('DefaultImportDeclaration');
interface WithDefaultImportDeclaration {
@@ -57,29 +59,31 @@ export function getDefaultImportDeclaration(expr: WrappedNodeExpr<unknown>): ts.
* a dangling reference, as TS will elide the import because it was only used in the type position
* originally.
*
* To avoid this, the compiler must "touch" the imports with `ts.getMutableClone`, and should
* only do this for imports which are actually consumed. The `DefaultImportTracker` keeps track of
* these imports as they're encountered and emitted, and implements a transform which can correctly
* flag the imports as required.
* To avoid this, the compiler must patch the emit resolver, and should only do this for imports
* which are actually consumed. The `DefaultImportTracker` keeps track of these imports as they're
* encountered and emitted, and implements a transform which can correctly flag the imports as
* required.
*
* This problem does not exist for non-default imports as the compiler can easily insert
* "import * as X" style imports for those, and the "X" identifier survives transformation.
*/
export class DefaultImportTracker {
/**
* A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
* a given `ts.SourceFile` and need to be preserved.
* A `Map` which tracks the `Set` of `ts.ImportClause`s for default imports that were used in
* a given file name.
*/
private sourceFileToUsedImports = new Map<ts.SourceFile, Set<ts.ImportDeclaration>>();
private sourceFileToUsedImports = new Map<string, Set<ts.ImportClause>>();
recordUsedImport(importDecl: ts.ImportDeclaration): void {
const sf = getSourceFile(importDecl);
if (importDecl.importClause) {
const sf = getSourceFile(importDecl);
// Add the default import declaration to the set of used import declarations for the file.
if (!this.sourceFileToUsedImports.has(sf)) {
this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>());
// Add the default import declaration to the set of used import declarations for the file.
if (!this.sourceFileToUsedImports.has(sf.fileName)) {
this.sourceFileToUsedImports.set(sf.fileName, new Set<ts.ImportClause>());
}
this.sourceFileToUsedImports.get(sf.fileName)!.add(importDecl.importClause);
}
this.sourceFileToUsedImports.get(sf)!.add(importDecl);
}
/**
@@ -89,65 +93,25 @@ export class DefaultImportTracker {
* This transformer must run after any other transformers which call `recordUsedImport`.
*/
importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => {
return (sf: ts.SourceFile) => {
return this.transformSourceFile(sf);
return context => {
let clausesToPreserve: Set<ts.Declaration>|null = null;
return sourceFile => {
const clausesForFile = this.sourceFileToUsedImports.get(sourceFile.fileName);
if (clausesForFile !== undefined) {
for (const clause of clausesForFile) {
// Initialize the patch lazily so that apps that
// don't use default imports aren't patched.
if (clausesToPreserve === null) {
clausesToPreserve = loadIsReferencedAliasDeclarationPatch(context);
}
clausesToPreserve.add(clause);
}
}
return sourceFile;
};
};
}
/**
* Process a `ts.SourceFile` and replace any `ts.ImportDeclaration`s.
*/
private transformSourceFile(sf: ts.SourceFile): ts.SourceFile {
const originalSf = ts.getOriginalNode(sf) as ts.SourceFile;
// Take a fast path if no import declarations need to be preserved in the file.
if (!this.sourceFileToUsedImports.has(originalSf)) {
return sf;
}
// There are declarations that need to be preserved.
const importsToPreserve = this.sourceFileToUsedImports.get(originalSf)!;
// Generate a new statement list which preserves any imports present in `importsToPreserve`.
const statements = sf.statements.map(stmt => {
if (ts.isImportDeclaration(stmt) && importsToPreserve.has(stmt)) {
// Preserving an import that's marked as unreferenced (type-only) is tricky in TypeScript.
//
// Various approaches have been tried, with mixed success:
//
// 1. Using `ts.updateImportDeclaration` does not cause the import to be retained.
//
// 2. Using `ts.factory.createImportDeclaration` with the same `ts.ImportClause` causes the
// import to correctly be retained, but when emitting CommonJS module format code,
// references to the imported value will not match the import variable.
//
// 3. Emitting "import * as" imports instead generates the correct import variable, but
// references are missing the ".default" access. This happens to work for tsickle code
// with goog.module transformations as tsickle strips the ".default" anyway.
//
// 4. It's possible to trick TypeScript by setting `ts.NodeFlag.Synthesized` on the import
// declaration. This causes the import to be correctly retained and generated, but can
// violate invariants elsewhere in the compiler and cause crashes.
//
// 5. Using `ts.getMutableClone` seems to correctly preserve the import and correctly
// generate references to the import variable across all module types.
//
// Therefore, option 5 is the one used here. It seems to be implemented as the correct way
// to perform option 4, which preserves all the compiler's invariants.
//
// TODO(alxhub): discuss with the TypeScript team and determine if there's a better way to
// deal with this issue.
// tslint:disable-next-line: ban
stmt = ts.getMutableClone(stmt);
}
return stmt;
});
// Save memory - there's no need to keep these around once the transform has run for the given
// file.
this.sourceFileToUsedImports.delete(originalSf);
return ts.factory.updateSourceFile(sf, statements);
}
}
@@ -11,7 +11,7 @@ import ts from 'typescript';
/**
* Describes a TypeScript transformation context with the internal emit
* resolver exposed. There are requests upstream in TypeScript to expose
* that as public API: https://github.com/microsoft/TypeScript/issues/17516..
* that as public API: https://github.com/microsoft/TypeScript/issues/17516.
*/
interface TransformationContextWithResolver extends ts.TransformationContext {
getEmitResolver: () => EmitResolver;
@@ -61,7 +61,7 @@ interface EmitResolver {
* The patch we apply to tell TypeScript about actual referenced aliases (i.e. imported symbols),
* matches conceptually with the logic that runs internally in TypeScript when the
* `emitDecoratorMetadata` flag is enabled. TypeScript basically surfaces the same problem and
* solves it conceptually the same way, but obviously doesn't need to access an `@internal` API.
* solves it conceptually the same way, but obviously doesn't need to access an internal API.
*
* The set that is returned by this function is meant to be filled with import declaration nodes
* that have been referenced in a value-position by the transform, such the installed patch can
@@ -129,9 +129,8 @@ function isTransformationContextWithEmitResolver(context: ts.TransformationConte
*/
function throwIncompatibleTransformationContextError(): never {
throw Error(
'Unable to downlevel Angular decorators due to an incompatible TypeScript ' +
'version.\nIf you recently updated TypeScript and this issue surfaces now, consider ' +
'downgrading.\n\n' +
'Angular compiler is incompatible with this version of the TypeScript compiler.\n\n' +
'If you recently updated TypeScript and this issue surfaces now, consider downgrading.\n\n' +
'Please report an issue on the Angular repositories when this issue ' +
'surfaces and you are using a supposedly compatible TypeScript version.');
}
@@ -6,6 +6,7 @@ ts_library(
name = "downlevel_decorators_transform",
srcs = glob(["*.ts"]),
deps = [
"//packages/compiler-cli/src/ngtsc/imports",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
"@npm//typescript",
@@ -8,10 +8,9 @@
import ts from 'typescript';
import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from '../../ngtsc/imports';
import {Decorator, ReflectionHost} from '../../ngtsc/reflection';
import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution';
/**
* Whether a given decorator should be treated as an Angular decorator.
* Either it's used in @angular/core, or it's imported from there.

Some files were not shown because too many files have changed in this diff Show More