Compare commits

...

238 Commits

Author SHA1 Message Date
Emma Twersky 95f053e4dc docs: remove AIO from search indexing 2024-05-01 12:10:17 -07:00
Andrew Kushnir 4c50e1e7cb release: cut the v17.3.7 release 2024-05-01 11:39:28 -07:00
Doug Parker 82bbcde843 release: bump DevTools to 1.0.14 (#55545)
PR Close #55545
2024-04-30 17:01:57 -07:00
Andrew Scott 266ad40e62 docs: Reference page anchor scroll consistency (#55566)
The docs-viewer loads asynchronously and can happen after the scroll
event has happened and the Router has attempted to scroll to the
requested position/anchor. This commit saves the scroll event and
handles it again if the cards are loaded after scrolling happens.

PR Close #55566
2024-04-30 14:03:06 -07:00
Paul Gschwendtner 4261fd367e build: improve incremental rebuilds of compliance tests (#55594)
Currently whenever a compliance test case TS file is modified, all
compliance tests in repository are rebuilt in partial compilation mode.
This is inefficient and also slows down local development where one may
use a wildcard to run all test targets inside `/test/compliance/...`.

This commit fixes this.

PR Close #55594
2024-04-30 09:22:42 -07:00
Angular Robot 6250269f1f build: update scorecard action dependencies (#55589)
See associated pull request for more information.

PR Close #55589
2024-04-30 09:20:10 -07:00
Andrew Scott 3eea50da64 fix(router): Scroller should scroll as soon as change detection completes (#55105)
Using `setTimeout` to delay scrolling can result in scrolling in the
next frame and cause noticeable flicker. This commit scrolls as soon as
the next render happens (or in `setTimeout` if a render does not happen
before then).

fixes #53985

PR Close #55105
2024-04-30 09:19:09 -07:00
Matthieu Riegler e5d58d250d docs: add mention of the HTTP client for interceptors (#55551)
fixes #53911

PR Close #55551
2024-04-29 13:41:08 -07:00
Matthieu Riegler ac4e8f7bca docs: remove non existing link (#55562)
This link only exist on adev.

PR Close #55562
2024-04-26 12:36:48 -07:00
Kristiyan Kostadinov 51ac883167 fix(compiler-cli): don't type check the bodies of control flow nodes in basic mode (#55558)
Angular only checks the contents of template nodes in full type checking mode. After v17, the new control flow always had its body checked, even in basic mode, which started revealing compilation errors for apps that were using the schematic to automatically switch to the new syntax.

These changes mimic the old behavior by not checking the bodies of `if`, `switch` and `for` blocks in basic mode. Note that the expressions of the blocks are still going to be checked.

Fixes #52969.

PR Close #55558
2024-04-26 09:33:39 -07:00
Matthieu Riegler 9bb3446e35 docs: update ChangeDetectionStrategy links (#55553)
fixes #54227
fixes #50943

PR Close #55553
2024-04-26 09:31:45 -07:00
Matthieu Riegler 04d77d5a1c docs: remove mention of style sanitization. (#55553)
fixes #49566

PR Close #55553
2024-04-26 09:31:44 -07:00
Matthieu Riegler 1ca24cb936 docs: remove image (#55553)
The image was replaced by the following mermaid chart.

PR Close #55553
2024-04-26 09:31:44 -07:00
Matthieu Riegler 967bbc1e3e docs: update app-shell to reflect actual content (#55550)
fixes #55507

PR Close #55550
2024-04-26 09:29:35 -07:00
Matthieu Riegler 507d83d5ed refactor(devtools): hide hydration error when the component tree is collapsed (#54912)
This commit improves the devtools UX.

PR Close #54912
2024-04-25 14:57:21 -07:00
Matthieu Riegler 4a33efaa3e refactor(devtools): prevent dblclick on the expand arrow to show the element panel (#54912)
This will improve the UX

PR Close #54912
2024-04-25 14:57:21 -07:00
Matthieu Riegler 5530c5e505 refactor(devtools): improve signal support. (#54912)
In some cases signals weren't unwraped thus not reading the value correctly. This commit fixes this issue.

PR Close #54912
2024-04-25 14:57:20 -07:00
Matthieu Riegler ce094b227c refactor(devtools): fix issue with virtual scroll viewport in the directive forest (#54912)
In some cases the height of the viewport wasn't calculated correctly because of extension tabs quirks. This commit fixes this issue.

Fixes #53704

PR Close #54912
2024-04-25 14:57:20 -07:00
Kirk Larkin 9258160598 docs: Update di-in-action.md (#55489)
PR Close #55489
2024-04-25 12:55:15 -07:00
Alan Agius 3ee861835a refactor(core): set up framework injector profile exclusively in browser environment (#55530)
This commit modifies the setup of the injector profiler to occur solely when the application is running in a browser context. This adjustment is made because the injector profile serves no purpose when the application is running on the server.

PR Close #55530
2024-04-25 12:53:23 -07:00
Kirk Larkin 9cd633e1b8 docs: Update what-is-angular.md (#55536)
PR Close #55536
2024-04-25 12:52:33 -07:00
Matthieu Riegler 75db462d8b refactor(common): increment test coverage (#55532)
Ahead of #55283 and with what we learned from it,  let's improve our test coverage on the i18n subsystem.

PR Close #55532
2024-04-25 12:50:44 -07:00
Matthieu Riegler c3d077cca1 docs: fix the file naming for the NG05000 error (#55519)
PR Close #55519
2024-04-25 12:49:54 -07:00
Andrew Scott af0eb846a5 fix(core): render hooks should not specifically run outside the Angular zone (#55399)
The timing of render hook execution is almost entirely identical to
`ngZone.onMicrotaskEmpty`. Developers working towards zoneless
compatibility will need to migrate `onMicrotaskEmpty` calls to use
`afterNextRender`/`afterRender` instead. This, however, would lead to
confusing issues if there are promises in the callbacks because
`onMicrotaskEmpty` emits inside the Angular zone while render hooks
execute outside today. This is problematic because it's not documented
and does not produce any notification or error message when async work
is done inside the hooks that requires change detection. Instead, change detection
simply does not run, and this behavior has proven to be surprising to
developers who are used to ZoneJS change detection behavior.

fixes #55299

PR Close #55399
2024-04-25 09:06:37 -07:00
Andrew Kushnir 544aedee6f release: cut the v17.3.6 release 2024-04-25 08:25:55 -07:00
Alan Agius add11c6854 build: make scripts for compatibility with Node.js 18.20+ (#55526)
This commit enhances the ng-dev and other local scripts to ensure compatibility with Node.js 18.20+. This adjustment is essential for releasing this branch, as it would otherwise lack compatibility with that version.

PR Close #55526
2024-04-25 08:11:41 -07:00
Angular Robot 5083d893a9 build: update scorecard action dependencies (#55473)
See associated pull request for more information.

PR Close #55473
2024-04-23 15:17:43 -07:00
Andrew Scott 826861b1fa fix(core): DeferBlockFixture.render should not wait for stability (#55271)
The `DeferBlockFixture.render` function should not await the
`whenStable` promise of the fixture. This does not allow developers to
test any intermediate states that might occur between rendering the
initial content and the full app stability.

fixes #55235

PR Close #55271
2024-04-23 15:12:43 -07:00
Kirk Larkin 9cb003ac3d docs: Update dependency-injection.md (#55432)
PR Close #55432
2024-04-23 13:29:15 -07:00
Kirk Larkin efb9eb4fa9 docs: Update dependency-injection-context.md (#55455)
PR Close #55455
2024-04-23 13:27:17 -07:00
vladboisa 79a29e4f14 docs(docs-infra): fix broken links (#55446)
Add the missing folder's for links

Fixes #55442

docs(docs-infra): fix template reference link

Change the broken link

PR Close #55446
2024-04-23 13:21:49 -07:00
Drew Mares eda03d4b89 docs: Fix typo in waitForAsync example (#55407)
The previous example had an incomplete code snippet that would not work if copied and pasted as is. There were two missing closing parentheses, one for the `inject` function, and the other for the `waitForAsync` function.

PR Close #55407
2024-04-23 13:16:17 -07:00
Kristiyan Kostadinov 8979fba2c5 fix(core): skip defer timers on the server (#55480)
Adds a check that disables the timer scheduling for `placeholder` and `loading` blocks on the server since the underlying timer will delay the server response.

Fixes #55475.

PR Close #55480
2024-04-23 13:06:53 -07:00
Alan Agius d5ee04edc1 build: remove no longer required bazel windows patch (#55477)
This patch is no longer required.

PR Close #55477
2024-04-23 13:06:04 -07:00
Matthieu Riegler 87921d7fff docs: fix links to packages. (#55469)
fixes #55458

PR Close #55469
2024-04-23 13:04:22 -07:00
Angular Robot d38135a12d build: update actions/checkout digest to 1d96c77 (#55464)
See associated pull request for more information.

PR Close #55464
2024-04-23 13:03:51 -07:00
Doug Parker 775476b9ac docs(devtools): update README to be more clear for extension reviewers (#55406)
Mozilla requires add-ons to have reproducible builds and this is managed directly by human reviewers and need to understand how to build Angular DevTools correctly. This commit defines a reproducible way of building Angular DevTools which is useful for reviewers and contributers.

The actual changes I made include:
1. Moving local development content into `devtools/README` (deleting `devtools/DEVELOPING.md`) and explicitly stating the Node version as well as global Yarn install to be clear about the correct way to build the extension. This should make it easier for reviewers to understand and reproduce builds.
2. Moving release instructions to a separate doc to reduce noise as well as making some minor clean ups there.

I briefly considered pointing reviewers at a new `devtools/docs/reviewers.md` doc, but decided against it because I want these instructions to be maintained in a single location where contributors and reviewers use the same content. This way any changes don't miss one of these sets of users. If reviewers have trouble with this format (dev builds isn't useful to reviewers and some content is repeated from other docs for contributors) then we can consider splitting out to a separate `reviewers.md` file. But until then, I'd like to try sharing this content and see how it works out in practice.

PR Close #55406
2024-04-23 13:02:51 -07:00
Andrew Kushnir 5cf14da35c fix(core): make ActivatedRoute inject correct instance inside @defer blocks (#55374)
`RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token dynamically. This logic breaks when a component/directive/pipe that injects the `ActivatedRoute` is located within a `@defer` block, because defer creates an `EnvironmentInjector` instance, which doesn't have that dynamic logic.

We've added some special handling of the `OutletInjector` in one of the previous commits, but it was incomplete and it was not covering cases when different routes use the same component. This commit updates defer logic to re-establish this dynamic behavior for `ActivatedRoute` by creating an instance of the `OutletInjector` when a parent injector was also an instance of `OutletInjector`.

This fix is a short-term solution and longer term we should find a way to achieve the dynamic behavior that Router relies on, but without adding a special case logic into defer.

Resolves #54864.

PR Close #55374
2024-04-22 12:01:39 -07:00
Matthieu Riegler aeeb38a546 docs: fix link to inject-based-di tutorial. (#55453)
Fixes #55451

PR Close #55453
2024-04-22 11:57:37 -07:00
Kristiyan Kostadinov 2c66afe857 docs: remove performance warning for directive composition (#55448)
Removes the warning about performance from the directive composition API docs since some recent benchmarks have snown that their effect is negligible.

PR Close #55448
2024-04-22 11:52:43 -07:00
Angular Robot a19e214cbe build: update actions/cache digest to 0c45773 (#54950)
See associated pull request for more information.

PR Close #54950
2024-04-22 11:26:54 -07:00
AleksanderBodurri d4c103ae08 fix(devtools): allow DevTools to fail gracefully for unsupported versions of Angular. (#55233)
Angular DevTools depends on many modern Angular features in order to function. As a result, at present the last officially supported version is v12. Angular DevTools may function for some Angular 9, 10 and 11 applications, but they are not officially supported.

This commit fixes an issue where DevTools would not inject a backend script into an Angular application if it detected it was below version 12. This backend script is important because it's used to inform the DevTools panel that the inspected application is in fact Angular, but that it is not on a supported version.

Angular 9, 10 and 11 applications that successfully have Angular DevTools initialize will now have a red highlight and tooltip on their version number, informing the user that they are using Angular DevTools on a version of Angular that is no longer supported.

Angular DevTools for applications that are below version 9 will continue to display the "Angular Devtools supports Angular versions 12 and above" message.

PR Close #55233
2024-04-22 11:25:21 -07:00
Angular Robot 39b7bdabec build: update scorecard action dependencies (#55419)
See associated pull request for more information.

PR Close #55419
2024-04-19 13:26:43 -07:00
Alan Agius 99df5a8ba2 build: update Domino to 8f228f8862540c6ccd14f76b5a1d9bb5458618af (#55426)
This contains the fix for #55416

PR Close #55426
2024-04-19 13:25:21 -07:00
Angular Robot 8ae4783d9b build: update github/codeql-action action to v3.25.0 (#55358)
See associated pull request for more information.

PR Close #55358
2024-04-17 18:02:50 +02:00
Pawel Kozlowski cc57d4c499 release: cut the v17.3.5 release 2024-04-17 17:43:14 +02:00
Doug Parker 8c6f261fa3 release: bump Angular DevTools version to 1.0.13 (#55371)
PR Close #55371
2024-04-17 07:53:12 +02:00
Matthieu Riegler c67cbe98a6 docs: use 17.3.x branch for aio quick start (#55328)
PR Close #55328
2024-04-15 17:27:16 +02:00
Paul Gschwendtner 9bf9864e79 build(docs-infra): fix playground lezer parsing (#55349)
When we started fixing the version mismatch with local 1st-party
packages, we also re-routed all dependencies like `@lezer/javascript`
into `adev/node_modules`. This works fine, but results in a different
version mismatch because the codemirror dependencies may resolve the
Angular version from `/node_modules`- causing some subtle complex
runtime error.

This commit fixes this by only re-routing dependencies that have
dependency on e.g. `@angular/core` into `adev/node_modules`.

Fixes #55298.

PR Close #55349
2024-04-15 16:36:41 +02:00
Mo Ibrahim a3b1dc005d docs: fix repeated note in "Add an Input to the component" section (#55306)
PR Close #55306
2024-04-12 10:14:08 -07:00
Jeremy Elbourn 8bd9ad02a4 docs: add team docs for when to use dev preview and experimental (#55308)
Adds a brief team doc for when to use developer preview and experimental
API tags.

PR Close #55308
2024-04-12 10:10:56 -07:00
Angular Robot 8a670e6bc5 build: update io_bazel_rules_sass digest to c3f163f (#55307)
See associated pull request for more information.

PR Close #55307
2024-04-12 10:34:41 +02:00
Leon Senft afdcff3f71 test(core): fix expectations that accidentally used incorrect matchers (#55294)
Expect that the number of template executions is `===` to a number, rather than expect that it
does not contain a number. As they were, these expectations would pass regardless of the expected
value.

PR Close #55294
2024-04-12 07:38:55 +02:00
Matthieu Riegler 051c140cc3 build(http): Mark the http tests as flaky because of one particular transferCache test. (#55255)
There is one particular transfer cache test that seldom fails. Let's mark it as flaky for now as this issue is non-obvious.

PR Close #55255
2024-04-11 20:50:20 +02:00
ehlemur c77ebb6f51 docs(core): add missing space (#55287)
Add missing space after period in section describing producers and consumers.

PR Close #55287
2024-04-11 17:25:35 +02:00
Chinthoorie b955999d29 refactor(compiler-cli): move config initialization into constructor (#54975)
refactor(compiler-cli): move config initialization into constructor
PR Close #54975
2024-04-11 11:20:18 +02:00
Andrew Scott 268a26f1b6 release: cut the v17.3.4 release 2024-04-10 15:13:53 -07:00
Andrew Scott ca8969b946 Revert "docs(docs-infra): move CLI docs to ADEV (#55242)" (#55291)
This reverts commit 4d2a8005e8.

PR Close #55291
2024-04-10 13:06:44 -07:00
Rahat Ahmed ce231fb271 ci: Merge primitives-shared and primitives-event-dispatch groups (#55273)
This simplifies the approvals necessary for the event dispatch directory

PR Close #55273
2024-04-10 10:59:46 -07:00
Angular Robot 888360a6b0 build: update github/codeql-action action to v3.24.10 (#55258)
See associated pull request for more information.

PR Close #55258
2024-04-10 10:50:52 -07:00
Paul Gschwendtner 1713d31026 build: do not clear stdout of architect-cli when building adev (#55282)
Similar to what we did for aio, we should not clear the architect CLI
output in Bazel. This messes up with the output and makes debugging
hard.

PR Close #55282
2024-04-10 10:48:40 -07:00
Paul Gschwendtner 4390d09d58 build(docs-infra): disable caching of adev inside bazel (#55282)
Similar to `aio`, disables caching inside Bazel as it would be discarded
anyway and is likely a source of slowness then.

PR Close #55282
2024-04-10 10:48:40 -07:00
Paul Gschwendtner 7676d8fc2d build(docs-infra): fix version mismatch of local built packages (#55282)
There is quite some trickery going on with the adev build related to
local packages:

- Adev builds using npm packages from `/node_modules`
- At runtime, we are adding `HEAD` packages for e.g. `@angular/core` to
  the bundles.
- At build time, the CLI, or Angular devkit may accidentally resolve to
  `@angular/core` from `/node_modules/`— which is the core version from
  npm, transitively installed via `@angular/docs`.

This causes a version mismatch, leading to issues like:

- CLI throwing because of a mismatch. https://github.com/angular/angular/issues/54858#issuecomment-2047188739
- Compiler changes not being picked up. https://github.com/angular/angular/issues/54858#issuecomment-2041322427

This commit attempts to fix this by:

- Linking all Angular `HEAD` packages into `adev/node_modules`. The
  current logic attempts to link into `/node_modules`, but this does not
  override existing `@angular/core`!
- Linking all direct external NPM packages, like
  `@angular_devkit/build-angular` into `adev/node_modules` without their
  transitive deps. This allows proper resolution of e.g. compiler as
  node looks in `adev/node_modules` first, and falls back for the rest
  to the execroot `node_modules`, or symlink target destination (if
  `preserveSymlinks=false`).

Note: This is still not 100% ideal because a direct external NPM
dependency may have a transitive dependency that has another transitive
dependency on `@angular/core`. In those cases, the may be a conflict
that is not resolvable until we switch to a Bazel toolchain with better
first party resolution support.

PR Close #55282
2024-04-10 10:48:40 -07:00
Paul Gschwendtner e9e8960e76 docs: add guide for the output() function (#54946)
Adds a guide for angular.io that introduces the new `output()` function
(and `outputFromObservable`).

PR Close #54946
2024-04-10 10:40:43 -07:00
Jeffrey Smith 766548c3ec fix(common): skip transfer cache on client (#55012)
transfer cache interceptor should not run again on the client as it is intended for server to client handoff

PR Close #55012
2024-04-09 14:45:39 -07:00
Martin Machava 545fb0d88b docs(forms): validate email field with proper validator (#55142)
PR Close #55142
2024-04-09 12:15:31 -07:00
Matthieu Riegler 9a2295fcee refactor(core): Remove zone assertion from hydration cleanup. (#55214)
In zoneless apps with hydration, this assertion was throwing.

Fixes #52160

PR Close #55214
2024-04-09 12:13:01 -07:00
Martin Machava d0ae5561b0 docs: fix input name (#55141)
This commit fixes the name of the input used for the email.

PR Close #55141
2024-04-09 11:21:09 -07:00
Matthieu Riegler 9d6effcec9 docs: update readme link (#55155)
fixes#55153

PR Close #55155
2024-04-09 10:32:52 -07:00
Boris Pouw 5587dde5d5 docs: add back dynamic form image to fix adev broken link (#55247)
This commit adds back the dynamic form image that was previously removed. This fixes the broken link in the adev documentation.

PR Close #55247
2024-04-08 14:33:30 -07:00
Mahdi Lazraq 650101e19d docs: add closing tag in hierarchical injectors documentation (#54954)
PR Close #54954
2024-04-08 14:11:32 -07:00
Andrew Mayer 36e8f4d5c8 docs: update links to calling-compilecomponents intesting-utility-apis (#55055)
PR Close #55055
2024-04-08 14:08:40 -07:00
Andrew Mayer 766db6a069 docs: update links to the calling-compilecomponents section (#55055)
PR Close #55055
2024-04-08 14:08:40 -07:00
gwario 5826d8cb52 docs: fix typo in event-binding documentation (#55189)
PR Close #55189
2024-04-08 13:47:38 -07:00
Angular Robot a7c1115c3d build: update io_bazel_rules_sass digest to 147f7fa (#55208)
See associated pull request for more information.

PR Close #55208
2024-04-08 13:41:26 -07:00
lilbeqiri 5c0004bb62 refactor: do cleanups to theme, version and content loader services at adev (#55234)
Keep theme constants in sync betwee index.html file and theme manager. Move versions config static data to their own file, and replace the deprecated toPromise() api with the new firstValueFrom() api

PR Close #55234
2024-04-08 13:38:12 -07:00
Matthieu Riegler 9f0691577e docs: replace code-example with docs-code (#55186)
PR Close #55186
2024-04-08 11:49:29 -07:00
Matthieu Riegler 4d2a8005e8 docs(docs-infra): move CLI docs to ADEV (#55242)
Previously located in the `aio` directory, the commit moves the docs file into `adev` and also moves the `script` used to generated them from the `angular-cli` repo.

PR Close #55242
2024-04-08 11:35:13 -07:00
Matthieu Riegler 13404d5107 docs(docs-infra): remove compiler import (#55229)
PR Close #55229
2024-04-08 11:31:58 -07:00
Matthieu Riegler 68d51f08a3 build: update licence date. (#55129)
Wasn't done last year, let's do it in 2024

PR Close #55129
2024-04-08 11:31:14 -07:00
Mohamed 422e9754b0 docs: fix broken logo link (#55239)
replace logo in main readme

PR Close #55239
2024-04-08 11:26:10 -07:00
Johnny Gérard 80d1094a4e test(common): update ImageKit low quality test (#55193)
For commit cd911a8008

PR Close #55193
2024-04-05 14:55:14 +00:00
Johnny Gérard 53427d875d fix(common): invalid ImageKit quality parameter (#55193)
The current quality parameter is ignored by ImageKit.

PR Close #55193
2024-04-05 14:55:14 +00:00
Jessica Janiuk 9cb1d27406 ci: pullapprove is case sensitive (#55220)
This fixes thePunderWoman for case sensitivity

PR Close #55220
2024-04-04 21:27:19 -07:00
Matthieu Riegler ee0412fb29 docs: use structural directives workflow for structural directives docs. (#55212)
fixes #53078

PR Close #55212
2024-04-04 16:03:44 +00:00
Jessica Janiuk 05e7bf8734 ci: jessicajaniuk is thePunderWoman (#55205)
this swaps jessica for jessica.

PR Close #55205
2024-04-03 16:05:21 -07:00
Jessica Janiuk 16eba152c4 release: cut the v17.3.3 release 2024-04-03 13:39:43 -07:00
Jessica Janiuk 4ac78fb846 ci: remove event-dispatch in pullapprove (#55200)
patch does not include event-dispatch paths, which was causing pullapprove to fail.

PR Close #55200
2024-04-03 18:45:56 +00:00
Jessica Janiuk 5ba2a48f49 ci: fixes jsaction path name (#55199)
This updates the jsaction path to be event-dispatch.

PR Close #55199
2024-04-03 18:27:46 +00:00
Jessica Janiuk 88f0545e8e refactor(core): remove unnecessary folder (#55197)
This removes a temporary folder for jsaction.

PR Close #55197
2024-04-03 18:08:16 +00:00
Jessica Janiuk eb9a5f5bbe ci: Add primitives groups to pullapprove (#53404)
This adds the shared primitives reviewers groups and list of reviewers
to the pullapprove rules.

PR Close #53404
2024-04-03 17:06:05 +00:00
Andrew Kushnir 158ceaf062 fix(core): handle ChainedInjectors in injector debug utils (#55144)
The fix from PR #55079 introduced a configuration of the injector chain, which wasn't properly handled by the injector debug utils, thus resulting in JS exceptions in DevTools. This commit updates injector debug utils logic that calculates injector resolution path to also handle `ChainedInjector`s.

Resolves #55137.

PR Close #55144
2024-04-03 15:26:48 +00:00
James 816ddf4539 docs: fix typo in first-app/steps/06-property-binding/README.md (#55181)
PR Close #55181
2024-04-03 00:14:15 +00:00
Matthieu Riegler f4e9768da2 docs: fix aio job (#55179)
PR Close #55179
2024-04-02 20:33:20 +00:00
ivanwonder 7b67ad2fc5 refactor(language-service): support showing tags info in the completion (#51140)
The Angular VSCode extension will support showing the tags info in this

[PR][1], so the language service can return the tags info now.

[1]: https://github.com/angular/vscode-ng-language-service/pull/1904

PR Close #51140
2024-04-02 16:21:02 +00:00
garrettld 129c973934 fix(devtools): fix padding in property tree view (#54648)
This import was missed when switching devtools to standalone.

fixes #54622

PR Close #54648
2024-04-01 10:52:45 -07:00
Jessica Janiuk bc429baa92 ci: Update workflows to use latest dev infra hash (#55128)
This bumps the hash for dev-infra dependencies to utilize the latest build and changes

PR Close #55128
2024-04-01 10:00:11 -07:00
Paweł Kubiak a84beaa4a7 fix(docs-infra): scrolling experience in API Reference (#55133)
Fix scrolling experience in API Reference:
- set correct margin top for members container (right side) which is aligned with tabs (left side)
- prevent scroll on focus
- remove redundant blur calls

PR Close #55133
2024-03-29 19:05:56 -07:00
Matthieu Riegler fd45d03d2f docs(docs-infra): Use monospace font for API reference (#54558)
fixes #52612

PR Close #54558
2024-03-29 16:46:25 -07:00
Kristiyan Kostadinov 949dec26b8 fix(migrations): avoid conflicts with some greek letters in control flow migration (#55113)
The control flow migration was using a couple of Greek letters as placeholders. This ended up conflicting with templates authored in Greek.

These changes use a more obscure placeholder to make conflicts less likely. It also moves the placeholder generation to a centralized function so it's easier to make changes if we decide to update the pattern again.

Fixes #55085.

PR Close #55113
2024-03-29 13:10:55 -07:00
Matthieu Riegler fc034137ab docs(docs-infra): move Features APIs above the filter section & rename to "Most Common (#54557)
Fixes #52705

PR Close #54557
2024-03-29 13:08:31 -07:00
vladboisa ca60d9dd3f refactor(docs-infra): move gif to display (#54795)
To display the image correctly, swap the image from the folders

Fixes  #54778

feat(docs-infra): change path for correct ADEV

Changing path for correctly displaying in adev

PR Close #54795
2024-03-29 13:06:43 -07:00
Johnny Gérard 10851d27cc docs(common): clean up comment (#55088)
PR Close #55088
2024-03-29 13:02:50 -07:00
Andrew Scott d10dc5c85c Revert "refactor(compiler): add support for the new search element (#54945)" (#55127)
This reverts commit 367b3ee6e9.
The search element is not a void element but existing components may use
the same selector and be used as a void element.

PR Close #55127
2024-03-29 12:56:33 -07:00
Andrew Scott cb95e2f8ab Revert "ci: Update workflows to use latest dev infra hash (#55123)" (#55126)
This reverts commit e4fde98279.

PR Close #55126
2024-03-29 12:48:28 -07:00
Jessica Janiuk eb0ccfc40c ci: Update workflows to use latest dev infra hash (#55123)
This bumps the hash for dev-infra dependencies to utilize the latest build and changes

PR Close #55123
2024-03-29 11:51:32 -07:00
Matthieu Riegler a8a20ad238 refactor(compiler): add support for the new search element (#54945)
See #whatwg/html#5811
Spec: https://html.spec.whatwg.org/multipage/grouping-content.html#the-search-element

fixes #54944

PR Close #54945
2024-03-29 10:02:03 -07:00
reafernandes10 e53d02df85 docs(http): Update the Setup for testing in Http Client to include provideHttpClient() (#53716)
It is not enough to use provideHttpClientTesting() for HTTP Client Testing, it will throw a dependency injection error. Update the docs to include both provideHttpClient() and provideHttpClientTesting()

Fixes #53390

PR Close #53716
2024-03-29 09:40:13 -07:00
vladboisa 109df125c8 docs(docs-infra): rephrase sentence to make sense (#54914)
Remove the confuse sentences with 2 categories, and rephrase

Fixes #54792

PR Close #54914
2024-03-29 09:34:50 -07:00
Matthieu Riegler 17f2181299 docs: fix API tabs height to allow overflow/scrolling (#55109)
fixes #53294

PR Close #55109
2024-03-29 09:33:15 -07:00
Matthieu Riegler 0bcea6c928 docs: document why undefined is usefull and does not remove classes (#55112)
Fixes #54601

PR Close #55112
2024-03-29 09:22:11 -07:00
Matthieu Riegler 2c325d46b0 docs(docs-infra): improve styling for deprecated CLI option (#54794)
Related to #54770

PR Close #54794
2024-03-28 15:48:26 -07:00
Matthieu Riegler 54a8fc3b19 docs(docs-infra): add margin for command aliases (#54794)
PR Close #54794
2024-03-28 15:48:24 -07:00
Matthieu Riegler 555696635e docs(docs-infra): improve space between cli command options (#54794)
fixes #54767

PR Close #54794
2024-03-28 15:48:23 -07:00
Allan Barrantes 878f581961 docs: remove broken link from CONTRIBUTING.md (#54953)
PR Close #54953
2024-03-28 15:06:59 -07:00
Garrett Darnell 9cef0d210f docs(core): fix toSignal docs (#54964)
PR Close #54964
2024-03-28 13:06:45 -07:00
Angular Robot 346a555e93 build: update github/codeql-action action to v3.24.9 (#55039)
See associated pull request for more information.

PR Close #55039
2024-03-28 13:05:59 -07:00
Ben Hong fa837a393e docs: fix outdated security guide reference (#55069)
PR Close #55069
2024-03-28 13:02:00 -07:00
Andrew Scott 4d043992e5 fix(core): test cleanup should not throw if Zone is not present (#55096)
The test hooks should not throw if applications choose not to load ZoneJS.

fixes #48198

PR Close #55096
2024-03-28 12:12:31 -07:00
Ben Hong 2abcb040eb docs: add missing h1 to page for skip to main content on home page (#55052)
PR Close #55052
2024-03-28 12:09:59 -07:00
Dylan Hunn 67f0cf5fc8 release: cut the v17.3.2 release 2024-03-28 09:56:43 -07:00
Kristiyan Kostadinov 2f9d94bc4a fix(migrations): account for variables in imports initializer (#55081)
Fixes that the control flow migration was throwing an error if the `imports` of a component are initialized to an identifier.

Fixes #55080.

PR Close #55081
2024-03-28 09:40:33 -07:00
Paul Gschwendtner ee76001431 refactor(compiler-cli): support ignoring specific doc entries during extraction (#55053)
This commit adds support for ignoring specific doc entries when
extracting doc entries. This allows us to drop e.g. `InputFunction` from
the API docs, given that the `input` API entry holds all the relevant
information.

`InputFunction` only exists for type purposes in the `.d.ts`.

PR Close #55053
2024-03-28 09:38:43 -07:00
Paul Gschwendtner b63afb9e93 refactor(core): improve API documentation for query initializer APIs (#55053)
Similar to `input`, `model`, `output`, the query initializer APIs are
also explicitly denoted as such. This allows angular.dev to display them
more readable and compactly.

PR Close #55053
2024-03-28 09:38:43 -07:00
Paul Gschwendtner 658fb0a17f refactor(core): improve API documentation for output (#55053)
This improves the API documentatino for `output` in angular.dev,
similar to how we improved the API for `input`.

Angular.dev can now show these documentation entries more
readable if annotated explicitly as initializer API.

Note: output API is short enough that we want to include
the types in the code snippet previews.

PR Close #55053
2024-03-28 09:38:42 -07:00
Paul Gschwendtner f8aa778b70 refactor(core): improve API documentation for model (#55053)
Improves the API documentation for `model`, similarly to how
we updated the `input` function.

PR Close #55053
2024-03-28 09:38:41 -07:00
Paul Gschwendtner 60ed00a601 refactor(core): improve API documentation for input after angular.dev support (#55053)
This commit improves the API documentation for `input` after
we added support for initializer APIs in angular.dev docs generation.

Changes:

- Rename `ReadT` to `T`. This conceptually makes it easy to talk about
  inputs of type `T` if there is no transform involved. The common case.
- Rename `WriteT` to `TransformT`. This makes it clear that this is the
  type that the "transform" needs to handle.
- Improves the "overall" description of the input function so that it
  can be shown as a general overview for the API site.
- Improves usage notes to be a little more helpful, yielding more useful
  content in the API docs usage notes section.
- Add short JSDoc description for each individual overload.

PR Close #55053
2024-03-28 09:38:40 -07:00
Paul Gschwendtner c7ff3d158a refactor(docs-infra): support initializer API functions (#55053)
This commit ensures that extracted initializer API functions are
showing up in angular.dev and can be filtered.

PR Close #55053
2024-03-28 09:38:39 -07:00
Paul Gschwendtner aeb20f95d2 refactor(compiler-cli): support extracting initializer API functions (#55053)
This commit adds support for extracting initializer API functions.
Initialixer API functions are functions conceptually that can are
intended to be used as class member initializers.

Angular started introducing a few of these for the new signal
APIs, like `input`, `model` or signal-based queries.

These APIs are currently confusingly represented in the API docs because
the API extraction:

- does not properly account for call signatures of interfaces
- does not expose information about sub-property objects and call
  signatures (e.g. `input.required`)
- the docs rendering syntax highlighting is too bloated and confusing
  with all types being included.

This commit adds support for initializer API functions, namely two
variants:

- interface-based initializer APIs. e.g. `export const input:
  InputFunction`- which is a pattern for `input` and `input.required`.
- function-based simpler initializer APIs with overloads. e.g.
  `contentChildren` has many signatures but doesn't need to be an
  interface as there are no sub-property call signatures.

PR Close #55053
2024-03-28 09:38:38 -07:00
Andrew Kushnir 708ba8115f fix(core): establish proper injector resolution order for @defer blocks (#55079)
This commit updates the `@defer` logic to establish proper injector resolution order. More specifically:

- Makes node injectors to be inspected first, similar to how it happens when `@defer` block is not used.
- Adds extra handling for the Router's `OutletInjector`, until we replace it with an `EnvironmentInjector`.

Resolves #54864.
Resolves #55028.
Resolves #55036.

PR Close #55079
2024-03-28 09:23:42 -07:00
Matthieu Riegler fb5a28858d refactor(devtools): prevent exeception on state serializer (#55061)
`Object.getPrototypeOf(obj)` returns `null` if `obj` is an empty object. `Object.getOwnPropertyDescriptors` throws on `null`/`undefined`

PR Close #55061
2024-03-28 09:14:25 -07:00
Paul Gschwendtner 5b67c94775 refactor(compiler-cli): improve error message for uncaught FatalDiagnosticError (#55070)
For `FatalDiagnosticError` we are currently hiding the `message` string
field in favor of the actual TS `diagnosticMessage`.

This works as expected, but makes these errors hard to debug in certain
environments (e.g. Jasmine). That is because `null` is the value of
`message` at runtime. We fix this by just overriding the type, like we
originally intended to do.

In addition, we properly render message chains in the `Error#message`
field— so that these errors, when uncaught, are somewhat reasonable and
can be useful.

PR Close #55070
2024-03-28 09:13:09 -07:00
Paul Gschwendtner b478dfbfda fix(compiler-cli): report errors when initializer APIs are used on private fields (#55070)
This commit ensures that the new APIs like `input`, `model`, `output`,
or signal-based queries are not accidentally used on fields that have a
problematic visibility/access level that won't work.

For example, queries defined using a private identifier (e.g. `#bla`)
will not be accessible by the Angular runtime and therefore _dont_ work.

This commit ensures:

- `input` is only declared via public and protected fields.
- `output` is only declared via public and protected fields.
- `model` is only declared via public and protected fields.
- signal queries are only declared via public, protected and TS private
  fields (`private` works, while `#bla` does not).

Fixes #54863.

PR Close #55070
2024-03-28 09:13:08 -07:00
Paul Gschwendtner 75d1cae616 refactor(compiler-cli): support enforcing field access for initializer APIs (#55070)
An initializer API like `input`, `output`, or signal queries may not be
compatible with certain access levels. E.g. queries cannot work with ES
private class fields.

This commit introduces a check for access levels into the initializer
API recognition— enforcing that every initializer API *clearly*
specifies what type of access is allowed.

PR Close #55070
2024-03-28 09:13:07 -07:00
Paul Gschwendtner 53fe455630 refactor(compiler-cli): reflection should inspect ES private fields and visibility (#55070)
This commit changes the TypeScript reflection host to:

* inspect / process ES private fields. e.g. `#someField` — those are
  ignored right now and we would want to check them to issue
  diagnostics.

* determine an access level of a class member. E.g. a member may be
  public, may be private, may be ES private, or public readonly. This
  can then be used in various checks later.

PR Close #55070
2024-03-28 09:13:06 -07:00
Kristiyan Kostadinov 1470b7d40e refactor(compiler-cli): expand type of initializer parsing function (#55070)
Updates the function that parses initializer APIs to check any `Expression`, instead of expecting a class member. This will be useful for the upcoming changes.

PR Close #55070
2024-03-28 09:13:05 -07:00
Matthieu Riegler 76631995e1 docs: link to http security now at best-practices/security (#55060)
fixes #55058

PR Close #55060
2024-03-27 11:32:09 -07:00
Esteban Luchsinger 519e781701 docs: explain difference between @placeholder and @loading (#54779)
Add a sentence to the `@loading` block documentation that emphasizes more that the `@loading` block will replace the `@placeholder` block once the deferred view starts loading.

PR Close #54779
2024-03-27 11:09:17 -07:00
vladboisa 1af2be9b23 docs(docs-infra): replace link by HTTPsecurity tab (#55029)
Replace a broke link by content of HTTP Client Security Tab.

Fixes #54918 & #54922

PR Close #55029
2024-03-27 10:50:25 -07:00
JiaLiPassion a893f0b60d fix(zone.js): should not clear onhandler when remove capture listener (#54602)
Close #54581

Should not clear `onHandler` when remove capture event listeners.

PR Close #54602
2024-03-27 10:40:06 -07:00
Paul Gschwendtner 93ce4d0483 refactor(compiler-cli): properly preserve file overview comments (#54983)
This commit updates the logic for preserving file overview comments
to be more reliable and less dependent on previous transforms.

Previously, with the old import manager, we had a utility called
`addImport` that always separated import statements and non-import
statements. This meant that the non-emitted statement from Tsickle
for the synthetic file-overview comments no longer lived at the
beginning of the file.

`addImports` tried to overcome this by adding another new non-emitted
statement *before* all imports. This then was later used by the
transform (or was assumed!) to attach the synthetic file overview
comments if the original tsickle AST Node is no longer at the top.

This logic can be improved, because the import manager shouldn't need to
bother about this fileoverview non-emitted statement, and the logic for
re-attaching the fileoverview comment should be local. This commit fixes
this and makes it a local transform.

PR Close #54983
2024-03-27 10:18:30 -07:00
Paul Gschwendtner ed271ebb37 test(compiler-cli): add tests to verify import generation in TCB files/blocks (#54983)
This commit adds some unit tests verifying the import generation in TCB
files and inline blocks. We don't seem to have any unit tests for these
in general. This commit adds some, verifying some characteristics we
would like to guarantee.

PR Close #54983
2024-03-27 10:18:30 -07:00
Paul Gschwendtner 6734b59b35 refactor(compiler-cli): rename ImportManagerV2 to ImportManager (#54983)
To ease review and to allow for both instances to co-exist, `ImportManagerV2`
was introduced. This commit renames it to `ImportManager` now that we
deleted the older one.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 789326d483 refactor(compiler-cli): delete old unused ImportManager (#54983)
This commit deletes the older and now unused `ImportManager`.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 4b63716b4a test(compiler-cli): enable incremental re-use type checking with signal inputs (#54983)
Enables the incremental type-checking test that we never enabled when we
landed signal inputs. Now that we fixed incremental re-use by re-using
the existing user imports for inline type check blocks, the test is
passing and can be enabled.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 308b194ee3 refactor(compiler-cli): switch jit transforms to use new import manager (#54983)
Switches the JIT transforms to use the new import manager.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 62510a7b4d refactor(compiler-cli): update type check generation code to use new import manager (#54983)
Updates the type-check block generation code (also for inline type check
blocks) to use the new import manager.

This is now a requirement because the translator utilities from the
reference emit environment expect an import manager that follows the
new contract established via `ImportGenerator<TFile, TExpression>`.

For type check files, we can simply print new imports as we don't expect
existing imports to be updated. That is because type check files do not
have any _original_ source files (or in practice— those are empty).

For type check blocks inline, or constructors, imports _may_ be re-used.
This is great as it helps fixing some incrementality bugs that we were
seeing in the type check code. That is, sometimes the type check block
code may generate imports conditionally for e.g. `TemplateRef`, or
animations. Those then **prevent** incremental re-use if TCB code
switches between those continously. We tried to account for that with
signal inputs by always pre-generating such imports. This fixed the
issue for type-check files, but for inline type check blocks this is
different as we would introduce new imports in user code that would then
be changed back in subsequential edit iterations. See:
https://github.com/angular/angular/pull/53521#pullrequestreview-1778130879.

In practice, the assumption was that we would be fine since user code is
most likely containing imports to `@angular/core` already. That is a
true assumption, but unfortunately it doesn't help with incremental
re-use because TypeScript's structural change detection does not dedupe
and expects 1:1 exact imports from their old source files.

https://github.com/microsoft/TypeScript/pull/56845

To improve incremental re-use for the type check integration, we should
re-use original source file imports when possible. This commit enables
this.

To update imports and execute inline operations, we are now uisng
`magic-string` (which is then bundled) as it simplifies the string
manipulatuons.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner ece2deeab6 refactor(compiler-cli): use new import manager for ngtsc transforms (#54983)
This commit switches ngtsc's JS and DTS transform to use the new import
manager. This is a drop-in replacement as we've updated the translator
helpers in the previous commit to align with the new API suggested by
the `ImportManagerV2` (to be renamed then).

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 4d8d324ea6 refactor(compiler-cli): update ImportGenerator abstraction for new manager (#54983)
`ImportGenerator` is the abstraction used by the translator functions to
insert imports for `ExternalExpr` in an AST-agnostic way.

This was built specifically for the linker which does not use any of the
complex import managers- but rather re-uses `ngImport` or uses
`ngImport.Bla`.

This commit also switches the linker AST-agnostic generator to follow
the new signatures. This was rather trivial.

PR Close #54983
2024-03-27 10:18:29 -07:00
Paul Gschwendtner 3253576db8 refactor(compiler-cli): introduce new implementation of ImportManager (#54983)
This commit introduces a new implementation of `ImportManager` that has
numerous benefits:

- It allows efficient re-use of original source file imports.
  * either fully re-using original imports if matching
  * updating existing import declarations to include new symbols.
- It allows efficient re-use of previous generated imports.
- The manager can be used for schematics and migrations.

The implementation is a rework of the import manager that we originally
built for schematics in Angular Material, but this commit improved it
to be more flexible, more readable, and "correct".

In follow-ups we can use this for schematics/migrations.

PR Close #54983
2024-03-27 10:18:29 -07:00
Andrew Scott 365fd50407 fix(router): RouterLinkActive will always remove active classes when links are not active (#54982)
Previously, `RouterLinkActive` would only add or remove the active classes when
its active state changed. This means that if you accidentally add one of
the active classes to the static class attribute, it won't get removed
until the link becomes active and then deactives (because the class is
added at creation time and never removed until the `RouterLinkActive`
state changes from active to inactive).

fixes #54978

PR Close #54982
2024-03-27 10:16:23 -07:00
Matthieu Riegler d1d9f5569f docs: update compat table with 17.3 (#54882)
PR Close #54882
2024-03-27 09:58:11 -07:00
Ben Hong 7763387253 docs: reduce confusion on tutorial intro and reorder how resources are introduced (#55051)
PR Close #55051
2024-03-27 09:56:44 -07:00
Doug Parker 0ec68a2a40 release: bump Angular DevTools version to 1.0.12 (#55056)
Also fixes a typo in release docs for the Firefox `zip` command.

PR Close #55056
2024-03-27 09:55:41 -07:00
Paul Gschwendtner 2ad5dcf3f0 [17.3.x]: build: follow-up fixes for AIO lighthouse checks (#55065)
* build: follow-up fixes for AIO lighthouse checks

Looks like the latest versions of Lighthouse and puppeteer
are not compatible with our Chromium version from dev-infra.

This commit reverts the updates, and fixes the jobs.

Update payload size; now matching — seems expected
2024-03-27 15:46:46 +01:00
Paul Gschwendtner 9aff43b5e8 ci: fix build failures in patch branch (#55064)
This commit fixes various issues that seem to cause the 17.3.x patch
to be red for weeks:

- Eslint checking a file for aio/tools that is not part of a tsconfig—
  hence an error is being reported.

- Audit-web-app importing some Lighthouse APIs that aren't available
  under the given path.

PR Close #55064
2024-03-27 11:58:46 +00:00
AleksanderBodurri fa46844f5c fix(devtools): issue where backendReady race condition causes Angular not detected error (#54805)
Previously, a race condition could cause DevTools to enter a state where it can't detect an application on reload. This was caused by a sequencing issue between the content script connection, the devtools panel connection and an event "backendReady" that lets DevTools know when a particular frame is ready to be inspected.

This commit replaces the previously stored backendReady boolean with a promise, so that the devtools panel can eventually run a callback to connect to a content script when that content script emits it's backendReady message.

PR Close #54805
2024-03-26 09:19:16 -07:00
AleksanderBodurri cd00ec582b Revert "Revert "refactor(devtools): implement multiframe support in devtools page (#53934)" (#54629)" (#54805)
This reverts commit 133319eba0.

PR Close #54805
2024-03-26 09:19:14 -07:00
AleksanderBodurri 821012d0f7 Revert "Revert "refactor(devtools): implement iframe support for Angular DevTools' browser code (#53934)" (#54629)" (#54805)
This reverts commit dd9f9d7d44.

PR Close #54805
2024-03-26 09:19:14 -07:00
Alan Agius 8958e0bf70 Revert "fix(http): exclude caching for authenticated HTTP requests (#54746)" (#55033)
This reverts commit 2258ac7a32.

Closes: #55031

PR Close #55033
2024-03-26 09:16:34 -07:00
Matthieu Riegler 4bb332ef08 refactor(common): request low quality placeholder images (#54899)
For every built-in load, this commit adds a parameter to load low quality placeholder images. Using 20/100 as base value.

PR Close #54899
2024-03-25 11:17:34 -07:00
Alan Agius 64f202cab9 fix(http): manage different body types for caching POST requests (#54980)
This update enhances the encoding handling of request bodies to generate the necessary cache key for transfer cache functionality.

Closes #54956

PR Close #54980
2024-03-25 11:17:03 -07:00
Matthieu Riegler cf34d2b87f docs: unescape html entities (#55016)
adev supports regular characters in place of entity. Let's use them to make the ease the work for the editors.

PR Close #55016
2024-03-25 11:16:35 -07:00
Andrea Canciani ec4483c471 test(zone.js): fix zone.js test listener removal (#55017)
The test was checking the wrong set of listeners because of a typo.

PR Close #55017
2024-03-25 11:16:06 -07:00
Mahdi Lazraq 1f7e81aef9 refactor(http): use nullish coalescing operator in HttpRequest.clone() (#54939)
Refactor how boolean options are handled in HttpRequest.clone() method by using nullish coalescing operator

PR Close #54939
2024-03-21 22:13:45 -07:00
Mahdi Lazraq cb433af0e1 fix(http): include transferCache when cloning HttpRequest (#54939)
Fixes a bug where HttpRequest.clone() does not include the transferCache property.

Fixes #54924.

PR Close #54939
2024-03-21 22:13:44 -07:00
Joey Perrott 3cf70e26ba ci: change label set by renovate to be merge ready immediately (#54947)
Since the PR is always expected to be ready to merge as renovate won't make changes in response to any sort of comment, it should already be marked as merge ready

PR Close #54947
2024-03-21 22:11:10 -07:00
Kristiyan Kostadinov 2b7bad5151 fix(compiler): invoke method-based tracking function with context (#54960)
Previously we assumed that if a `for` loop tracking function is in the form of `someMethod($index, $item)`, it will be pure so we didn't pass the parameter to bind the context to it. This appears to be risky, because we don't know if the method is trying to access `this`.

These changes play it safe by always binding method-based tracking functions.

Fixes #53628.

PR Close #54960
2024-03-21 22:08:41 -07:00
Andrew Scott 78d4ad20e6 ci: temporarily disable failing test (#54970)
this test is failing after #54711

PR Close #54970
2024-03-20 13:31:35 -07:00
Andrew Scott c2149dfaa8 release: cut the v17.3.1 release 2024-03-20 12:49:30 -07:00
Alex Rickabaugh 99e9474aa2 fix(compiler-cli): symbol feature detection for the compiler (#54711)
Use the actual symbol presence in the .d.ts to detect whether two-way
binding to writable signals should be template type-checked.

PR Close #54711
2024-03-20 12:14:42 -07:00
Angular Robot 766bdf3207 build: update dependency google-closure-compiler to v20240317 (#54931)
See associated pull request for more information.

PR Close #54931
2024-03-19 17:00:46 +00:00
Angular Robot 2a1ea5a22b build: update github/codeql-action action to v3.24.8 (#54932)
See associated pull request for more information.

PR Close #54932
2024-03-19 16:59:49 +00:00
Joey Perrott 529a5f434d refactor(docs-infra): don't rely on aio_npm in adev (#54928)
Use @npm// instead of @aio_npm// in adev BUILD files

PR Close #54928
2024-03-19 09:55:45 -07:00
Matthieu Riegler e70228afa9 refactor(core): Add hydration missmatch on the component rather than the node. (#54671)
In some cases the hydration mismatch is nested within a component.

As the devTool only reports issues on the component level, we need to mark the component node rather than the actual mismatched node.

PR Close #54671
2024-03-18 16:52:19 -07:00
Angular Robot 4ab3a9afad docs: update Angular CLI help [17.3.x] (#54911)
Updated Angular CLI help contents.

PR Close #54911
2024-03-18 16:51:28 -07:00
Andrew Kushnir 1f5ab960ef refactor(core): allow passing an environment injector while creating a view (#54903)
This commit updates the name of an internal function argument that represents an embedded view injector. Also it introduces a new config option that allows passing an environment injector to be used in the underlying LView. There are no changes to the behavior, just some initial cleanup for upcoming changes.

PR Close #54903
2024-03-18 16:37:36 -07:00
Joey Perrott 860eccef48 ci: move saucelabs to regular CI job (#54926)
Move the saucelabs job to regular CI instead of the priviledged CI to prevent cache poisoning/overload

PR Close #54926
2024-03-18 11:02:39 -07:00
Joey Perrott e61ccd2ac5 build: update to latest angular/dev-infra/github-actions/create-pr-for-changes (#54904)
Update to latest to correct node verison issue

PR Close #54904
2024-03-17 00:41:31 +00:00
Paul Gschwendtner 80e5a0a03d test(compiler-cli): add unit tests for output() JIT transform (#54841)
We are already testing the JIT transforms via integration tests, but
this commit adds dedicated unit tests for the transform behavior for
proper test coverage (planned follow-up).

PR Close #54841
2024-03-15 15:14:32 -07:00
Kristiyan Kostadinov c0788200e2 fix(compiler): capture data bindings for content projection purposes in blocks (#54876)
Fixes a regression in the template pipeline where data bindings weren't being captured for content projection purposes.

Fixes #54872.

PR Close #54876
2024-03-15 15:11:19 -07:00
Chinthoorie 42318e7c3d refactor(compiler-cli): move DelegatingPerfRecorder initialization into constructor (#54834)
Move the initialization of class field `DelegatingPerfRecorder` into the constructor.

This fixes the error : `TypeError: Cannot read properties of undefined (reading 'eventCount')`

This is blocking the roll-out of public class.

PR Close #54834
2024-03-15 15:10:46 -07:00
Angular Robot 33dc072b70 build: update dependency google-closure-compiler to v20231112 (#54893)
See associated pull request for more information.

PR Close #54893
2024-03-15 18:31:23 +00:00
Angular Robot 209632e64d build: update dependency archiver to v7 (#54892)
See associated pull request for more information.

PR Close #54892
2024-03-15 18:18:35 +00:00
Angular Robot e78dd31cea build: update actions/cache digest to ab5e6d0 (#54886)
See associated pull request for more information.

PR Close #54886
2024-03-15 17:28:14 +00:00
Angular Robot 91029aa082 build: update dependency @octokit/graphql to v8 (#54888)
See associated pull request for more information.

PR Close #54888
2024-03-15 17:24:52 +00:00
Angular Robot 52296bc29d build: update dependency lighthouse to v11 (#54292)
See associated pull request for more information.

PR Close #54292
2024-03-15 17:22:55 +00:00
Angular Robot 64862cbff2 build: update io_bazel_rules_sass digest to 79bd239 (#54887)
See associated pull request for more information.

PR Close #54887
2024-03-15 17:16:23 +00:00
Angular Robot 90bbc68d44 build: update cross-repo angular dependencies (#54883)
See associated pull request for more information.

PR Close #54883
2024-03-15 17:06:44 +00:00
Angular Robot a5cc82ee84 docs: update events (#54749)
Generated `events.json` with the latest events retrieved from the Firebase DB.

PR Close #54749
2024-03-15 16:38:14 +00:00
Angular Robot 855b8e046c build: update dependency puppeteer-core to v22 (#54298)
See associated pull request for more information.

PR Close #54298
2024-03-15 16:31:26 +00:00
Angular Robot 35e8184935 build: update peter-evans/create-or-update-comment action to v4 (#54299)
See associated pull request for more information.

PR Close #54299
2024-03-15 16:30:52 +00:00
Angular Robot 74506e76a8 build: update eslint dependencies (#50993)
See associated pull request for more information.

PR Close #50993
2024-03-15 16:30:11 +00:00
Andrew Scott c22c5c4543 release: cut the v17.3.0 release 2024-03-13 14:05:31 -07:00
JoostK 5d41ab9cd1 refactor(core): restructure logic in isCssClassMatching function (#54800)
The logic in `isCssClassMatching` is only interested in two areas in the attributes:
implicit attributes and the `AttributeMarker.Classes` area, with the first area only
of interest for projection matching, not directive matching. This commit splits these
two searches to make this more apparent.

PR Close #54800
2024-03-12 14:05:19 -07:00
JoostK 243ccce624 fix(core): exclude class attribute intended for projection matching from directive matching (#54800)
This commit resolves a regression that was introduced when the compiler switched from
`TemplateDefinitionBuilder` (TDB) to the template pipeline (TP) compiler. The TP compiler
has changed the output of

```html
if (false) { <div class="test"></div> }
```

from

```ts
defineComponent({
  consts: [['class', 'test'], [AttributeMarker.Classes, 'test']],
  template: function(rf) {
    if (rf & 1) {
      ɵɵtemplate(0, App_Conditional_0_Template, 2, 0, "div", 0)
    }
  }
});
```

to

```ts
defineComponent({
  consts: [[AttributeMarker.Classes, 'test']],
  template: function(rf) {
    if (rf & 1) {
      ɵɵtemplate(0, App_Conditional_0_Template, 2, 0, "div", 0)
    }
  }
});
```

The last argument to the `ɵɵtemplate` instruction (0 in both compilation outputs) corresponds with
the index in `consts` of the element's attribute's, and we observe how TP has allocated only a single
attribute array for the `div`, where there used to be two `consts` entries with TDB. Consequently,
the `ɵɵtemplate` instruction is now effectively referencing a different attributes array, where the
distinction between the `"class"` attribute vs. the `AttributeMarker.Classes` distinction affects
the behavior: TP's emit causes the runtime to incorrectly match a directive with `selector: '.foo'` to
be instantiated on the `ɵɵtemplate` instruction as if it corresponds with a structural directive!

Instead of changing TP to align with TDB's emit, this commit updates the runtime instead. This uncovered
an inconsistency in selector matching for class names, where there used to be two paths dealing with
class matching:

1. The first check was commented to be a special-case for class matching, implemented in `isCssClassMatching`.
2. The second path was part of the main selector matching algorithm, where `findAttrIndexInNode` was being used
   to find the start position in `tNode.attrs` to match the selector's value against.

The second path only considers `AttributeMarker.Classes` values if matching for content projection, OR of the
`TNode` is not an inline template. The special-case in path 1 however does not make that distinction, so it
would consider the `AttributeMarker.Classes` binding as a selector match, incorrectly causing a directive to
match on the `ɵɵtemplate` itself.

The second path was also buggy for class bindings, as the return value of `classIndexOf` was incorrectly
negated: it considered a matching class attribute as non-matching and vice-versa. This bug was not observable
because of another issue, where the class-handling in part 2 was never relevant because of the special-case
in part 1.

This commit separates path 1 entirely from path 2 and removes the buggy class-matching logic in part 2, as
that is entirely handled by path 1 anyway. `isCssClassMatching` is updated to exclude class bindings from
being matched for inline templates.

Fixes #54798

PR Close #54800
2024-03-12 14:05:18 -07:00
Ben Hong e8badecf4f docs: fix missing security guide in navigation (#54830)
The paths for the security guide were flipped in the original PR. As a result, it looked for a markdown file in the best-practices directory when it should have looked for it in the guide directory instead.

PR Close #54830
2024-03-12 13:13:03 -07:00
Gerald Monaco cdfcb779df refactor(core): add internal API to enable i18n hydration (#54784)
Add an internal API to enable and use i18n hydration for testing and development. This helps ensure that we don't accidentally break the current behavior until we are completely ready to roll out i18n support.

PR Close #54784
2024-03-12 11:39:03 -07:00
cexbrayat ac395d0f68 refactor(compiler-cli): cleanup unused code (#54775)
The initializer api no longer needs to take care of `ɵoutput`.

PR Close #54775
2024-03-12 11:31:39 -07:00
Ben Hong e75a9a4d83 docs: improve in-depth guides information architecture (#54365)
PR Close #54365
2024-03-12 10:25:40 -07:00
Joey Perrott 41f28f88b1 fix(docs-infra): remove the prerender directory from adev (#54820)
Remove the prerender directory as it is entirely unused and unneeded

PR Close #54820
2024-03-12 10:24:08 -07:00
Paul Gschwendtner 7df0a8a278 refactor(core): report subscription errors for OutputEmitterRef to ErrorHandler (#54821)
Currently if an `(output)` listener fails, it will be handled gracefully
by Angular and reported to the `ErrorHandler`.

For programmatic subscriptions with `OutputEmitterRef`, this is not the case.
Instead, as soon as any subscription is failing, all other subsequent
subscription callbacks are not firing anymore.

This commit intends to make this more consistent by gracefully
reporting errors from `OutputEmitterRef#emit` to `ErrorHandler`,
allowing for listener execution to continue.

PR Close #54821
2024-03-12 10:21:50 -07:00
cexbrayat 9889346620 docs: typo in output documentation (#54773)
PR Close #54773
2024-03-12 09:15:05 -07:00
Kristiyan Kostadinov 39a50f9a8d fix(core): ensure all initializer functions run in an injection context (#54761)
Ensures that all of the functions intended to be run in initializers are in an injection context. This is a stop-gap until we have a compiler diagnostic for it.

PR Close #54761
2024-03-12 09:08:08 -07:00
Pawel Kozlowski 1f77083a2d refactor(core): assert presence of the track function (#54814)
This commits assert that the repeater instruction gets a reference
to a tracking function. This change will allow us to better track
occurences of https://github.com/angular/angular/issues/53628 -
in certain situations a reference to a tracking function might be
undefiened.

We are not fixing the underlying issue here, just getting better
visibility.

PR Close #54814
2024-03-11 16:33:01 -07:00
Paul Gschwendtner bb35414a38 perf(core): speed up retrieval of DestroyRef in EventEmitter (#54748)
Speeds up the retrieval of `DestroyRef` in `EventEmitter` because
`try/catch` is expensive if there is no injection context.

We saw a script time regression in Cloud.

The goldens had to be updated because `getInjectImplementation` is now
referenced. `inject` also references the underlying field, but directly.
This is super minimal overhead of a function exposing the internal
field.

PR Close #54748
2024-03-11 16:31:03 -07:00
Matthieu Riegler fcfb42a8ba docs: use the right path for first-app images (#54694)
PR Close #54694
2024-03-11 16:30:28 -07:00
Joey Perrott 57e54e29e7 docs: bump to 2024 for copyright text (#54822)
Bump copyright text to 2024

PR Close #54822
2024-03-11 13:39:49 -07:00
Andrew Kushnir 2909e9817d fix(core): prevent infinite loops in clobbered elements check (#54425)
This commit updates HTML sanitization logic to avoid infinite loops in case clobbered elements contain fields like `nextSibling` or `parentNode`. Those fields are used for DOM traversal and this update makes sure that those calls return valid results.

Also this commit fixes an issue when clobbering `nodeName` causes JS exceptions.

PR Close #54425
2024-03-11 12:46:26 -07:00
Gerald Monaco 619f3c8792 refactor(core): add i18nNodes in preparation for i18n hydration (#54750)
An i18n message effectively acts as a dynamic template: two elements with contiguous instruction indices won't necessarily be contiguous in the DOM.

For that reason, we need to maintain a mapping from instruction index to a physical DOM node in order to hydrate views with i18n, pointing to where hydration for that view should begin.

PR Close #54750
2024-03-11 11:12:46 -07:00
Andrew Scott 0ee1a1419d refactor(core): Remove isG3 usages in patch branch (#54816)
This commit removes uses of isG3 in the patch branch. G3 always executes
against main so any references in patch are unused.

PR Close #54816
2024-03-11 11:11:32 -07:00
Kristiyan Kostadinov f386a04c9d fix(compiler): handle two-way bindings to signal-based template variables in instruction generation (#54714)
Updates the instruction generation for two-way bindings to only emit the `twoWayBindingSet` call when writing to template variables. Since template variables are constants, it's only allowed to write to them when they're signals. Non-signal values are flagged during template type checking.

Fixes #54670.

PR Close #54714
2024-03-11 11:01:43 -07:00
Kristiyan Kostadinov 492e03f699 fix(compiler-cli): flag two-way bindings to non-signal values in templates (#54714)
We have a diagnostic that reports writes to template variables which worked both for regular event bindings and two-way bindings, however the latter was broken by #54154 because two-way bindings no longer had a `PropertyWrite` AST.

These changes fix the diagnostic and expand it to allow two-way bindings to template variables that are signals.

PR Close #54714
2024-03-11 11:01:43 -07:00
Kristiyan Kostadinov ba9ddd7ac2 refactor(compiler-cli): move illegal template assignment check into template semantics checker (#54714)
Moves the check which ensures that there are no writes to template variables into the `TemplateSemanticsChecker` to prepare for the upcoming changes.

PR Close #54714
2024-03-11 11:01:43 -07:00
Kristiyan Kostadinov 5d23e601d1 refactor(compiler-cli): move signal identification function (#54714)
Moves the function that identifies signals into a separate file so that it can be reused outside of extended diagnostics.

PR Close #54714
2024-03-11 11:01:43 -07:00
Kristiyan Kostadinov a7872ca77a refactor(compiler-cli): introduce template semantics checker (#54714)
Introduces a new `TemplateSemanticsChecker` that will be used to flag semantic errors in the user's template. Currently we do some of this in the type check block, but the problem is that it doesn't have access to the template type checker which prevents us from properly checking cases like #54670. This pass is also distinct from the extended template checks, because we don't want users to be able to turn the checks off and we want them to run even if `strictTemplates` are disabled.

PR Close #54714
2024-03-11 11:01:43 -07:00
Matthieu Riegler 10314787d6 docs: adding signal inputs and model to ADEV. (#54753)
Those docs are a copy from AIO.

PR Close #54753
2024-03-11 11:01:01 -07:00
theRealc0d3c 40a94b3ab6 docs: fix typo in tutorial (#54817)
PR Close #54817
2024-03-11 10:59:53 -07:00
Kristiyan Kostadinov 1f129f114e fix(compiler): not catching for loop empty tracking expressions (#54772)
Fixes that the template parser wasn't catching empty expressions in the `track` parameter of for loops.

Fixes #54763.

PR Close #54772
2024-03-11 09:17:43 -07:00
Kristiyan Kostadinov 962934bc4f build: update to TypeScript 5.4 stable (#54743)
Updates the repo to the stable version of TypeScript 5.4.

PR Close #54743
2024-03-11 09:16:55 -07:00
JoostK 6ce8fc8637 refactor(core): avoid additional closure for queued microtask callback (#54801)
The `runCallbackOnce` closure is declared not to have any parameters itself, so it is
compatible as `queueMicrotask` callback without the extra closure. This reduces the call
stack by a frame and avoids the extra closure allocation.

PR Close #54801
2024-03-11 08:59:55 -07:00
JoostK e82173e8c4 refactor(core): properly type withBody and withHead testing helpers (#54801)
This commit addresses a typing mismatch, where these functions were declared to return whichever
value their callback returned, but this was inaccurate: it's always a test callback function
with `done` argument.

PR Close #54801
2024-03-11 08:59:55 -07:00
JoostK b6a7d509fe test(core): ensure async tests are awaited properly (#54801)
The assertion in `packages/core/test/acceptance/after_render_hook_spec.ts:165` was prone to flakes,
where Jasmine could frequently report an error:

```
Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out
    at Env.expect (node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1945:15)
    at expect (node_modules/jasmine-core/lib/jasmine-core/jasmine.js:8267:18)
    at file:///packages/core/test/acceptance/after_render_hook_spec.ts:165:12
```

This happens because `wrapTestFn` checks for an exact type of `Promise`, which may have been patched by zone.js
such that the `instanceof` condition is dependent on whether zone.js has patched the `Promise` constructor.

PR Close #54801
2024-03-11 08:59:55 -07:00
Kristiyan Kostadinov 01722ee637 build: fix size failure (#54756)
Fixes a broken size CI check.

PR Close #54756
2024-03-07 16:13:44 -08:00
Andrew Scott 7225485311 fix(router): Navigations triggered by cancellation events should cancel previous navigation (#54710)
There is an edge case where synchronous navigations caused in
response to navigation events can result in a previous navigation not
being unsubscribed from. b/328219996

PR Close #54710
2024-03-07 13:07:10 -08:00
Andrew Scott a45e69f627 refactor(core): render hooks should always run on node attach or detach (#54083)
This commit ensures that render hooks are rerun when a node is attached
or detached. We do not necessarily need to run change detection but DOM
did change so render hooks should execute.

PR Close #54083
2024-03-07 12:59:11 -08:00
Andrew Scott a7d6c64a47 refactor(core): Skip refresh views if render hooks are the only notification source (#54083)
Do not refresh views if the only thing that notified the scheduler was
registration of a new render hook.

PR Close #54083
2024-03-07 12:59:10 -08:00
Andrew Scott 2f7c7b3833 refactor(core): registering afterRender hooks notify scheduler (#54083)
This commit updates the `afterRender` and `afterNextRender` hooks to
notify the scheduler (which subsequently schedules change detection)
when created. This makes the hooks similar to `requestAnimationFrame`,
which requests that the browser schedule a rendering operation. This
reqeust is not conditional. Even if there was nothing to repaint, the
`requestAnimationFrame` callback will execute.

In Angular, this is useful because callers of `afterNextRender` don't
necessarily have any way of knowing whether a change detection is even
scheduled. For example, the anchor scrolling with the Angular Router
needs to wait for rendering to complete before attempting to scroll
because rendering can affect the size of the page. However, if the user
is already on the page that the navigation is targeting, such as
navigating to an anchor on the page, there is nothing new for the Router
to render so a render might not even be scheduled.

Related to https://github.com/angular/angular/issues/53985, which
could use `afterNextRender` instead of `setTimeout` to ensure the
scrolling happens in the same frame as the page rendering, but would not
necessarily work without this change (as described above). Note that the
scrolling _cannot_ use a microtask to ensure scrolling happens in the
same frame because `NgZone` will ensure microtasks flush before
change detection, so it would cause the scroll to happen before rendering.

PR Close #54083
2024-03-07 12:59:10 -08:00
Gerald Monaco 0daa1ea19d refactor(core): refactor i18n node creation to prepare for hydration (#54722)
Adds a `locateOrCreateNode` helper and uses it for all DOM operations in preparation for supporting hydration.

PR Close #54722
2024-03-07 12:41:46 -08:00
Leonardo Ortiz 658ae699d8 docs: update content readme link (#53763)
PR Close #53763
2024-03-07 11:07:09 -08:00
Leonardo Ortiz 9cf6b9369e docs: readme from blossom (#53763)
PR Close #53763
2024-03-07 11:07:09 -08:00
Angular Robot 9ea0d69275 docs: update events (#54721)
Generated `events.json` with the latest events retrieved from the Firebase DB.

PR Close #54721
2024-03-07 11:05:39 -08:00
Gerald Monaco f1991d49b0 refactor(core): don't hydrate detached nodes (#54723)
In preparation for hydration support, make sure that we don't attempt to hydrate detached nodes and instead, always newly create them.

PR Close #54723
2024-03-07 11:01:31 -08:00
Gerald Monaco 198f2ff5f2 refactor(core): add i18n AST to prepare for hydration (#54724)
In order to serialize and hydrate i18n blocks, we need to be able to walk an AST for the translated message. This AST is generated during normal parsing of the message.

PR Close #54724
2024-03-07 11:00:38 -08:00
Alan Agius 8d37ed035c fix(http): exclude caching for authenticated HTTP requests (#54746)
This update modifies the transfer cache logic to prevent caching of HTTP requests that require authorization.

Closes: #54745

PR Close #54746
2024-03-07 11:00:03 -08:00
Andrew Scott 365955322f refactor(core): Remove isInternal flag (#54740)
This is not needed any longer since the changes that used it have
landed.

PR Close #54740
2024-03-07 09:07:01 -08:00
Andrew Scott d870856840 refactor(core): Fix internal test util types (#54736)
This fixes and error that has been seen recently where compilation fails
due to domino.impl type not being recognized

PR Close #54736
2024-03-06 13:34:55 -08:00
Pawel Kozlowski 7bfa3610c5 release: cut the v17.3.0-rc.0 release 2024-03-06 20:40:30 +01:00
504 changed files with 16799 additions and 9397 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
runs:
using: 'composite'
steps:
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4
with:
path: |
./node_modules/
+4 -4
View File
@@ -21,16 +21,16 @@ jobs:
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build --config=aio_local_deps //adev:build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@5774b71c01a55c4c998f858ee37d3b77ae704c31
with:
workflow-artifact-name: 'adev-preview'
pull-number: '${{github.event.pull_request.number}}'
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4
with:
token: '${{secrets.GITHUB_TOKEN}}'
@@ -40,7 +40,7 @@ jobs:
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@5774b71c01a55c4c998f858ee37d3b77ae704c31
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -16,6 +16,6 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/branch-manager@4b433074a806bbbd4d319264430740cd46e62f27
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+3 -3
View File
@@ -19,7 +19,7 @@ jobs:
exit: true
# Indicate that the benchmark command was received.
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{github.event.comment.id}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
@@ -38,7 +38,7 @@ jobs:
- uses: ./.github/actions/yarn-install
- uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
with:
bazelrc: ./.bazelrc.user
@@ -53,7 +53,7 @@ jobs:
id: benchmark
name: Running benchmark
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
issue-number: ${{github.event.issue.number}}
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
-64
View File
@@ -1,64 +0,0 @@
name: CI
on:
pull_request_target:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
permissions: {}
defaults:
run:
shell: bash
jobs:
saucelabs:
runs-on: ubuntu-latest-4core
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
with:
cache-node-modules: true
# Checking out the pull request commit is intended here as we need to run the changed code tests.
ref: ${{ github.event.pull_request.head.sha }}
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
- name: Starting Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh run &
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- name: Preparing Bazel-generated fixtures required in legacy tests
run: |
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
mkdir -p dist/legacy-test-out/core/test/reflection/
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
# Locale files are needed for i18n tests running within Saucelabs. These are added
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
# can succeed. Note that the base locale and currencies files are checked-in, so
# we do not need to re-generate those through Bazel.
mkdir -p packages/common/locales/extra
cp dist/bin/packages/common/locales/*.ts packages/common/locales
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
- name: Build bundle of tests to run on Saucelabs
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
- name: Wait and confirm Saucelabs tunnel has connected
run: ./tools/saucelabs/sauce-service.sh ready-wait
timeout-minutes: 3
- name: Running tests on Saucelabs.
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
- name: Stop Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh stop
+70 -24
View File
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
node-module-directories: |
@@ -60,13 +60,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run unit tests
@@ -78,13 +78,13 @@ jobs:
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
@@ -115,11 +115,11 @@ jobs:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
@@ -197,13 +197,13 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
@@ -215,7 +215,7 @@ jobs:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
node-module-directories: |
@@ -223,9 +223,9 @@ jobs:
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile
- run: |
@@ -264,7 +264,7 @@ jobs:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
# Checking out the pull request commit is intended here as we need to run the changed code tests.
@@ -272,20 +272,66 @@ jobs:
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@50bb71e51d3372ca2e359adb9069f89beeac4ae7
- name: Set up Sauce Tunnel Daemon
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
- name: Run all saucelabs bazel tests
run: |
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "fixme-saucelabs", ...)')
yarn bazel test --config=saucelabs --jobs=$JOBS ${TESTS}
saucelabs:
runs-on: ubuntu-latest-4core
env:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
with:
cache-node-modules: true
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@50bb71e51d3372ca2e359adb9069f89beeac4ae7
- name: Starting Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh run &
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- name: Preparing Bazel-generated fixtures required in legacy tests
run: |
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
mkdir -p dist/legacy-test-out/core/test/reflection/
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
# Locale files are needed for i18n tests running within Saucelabs. These are added
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
# can succeed. Note that the base locale and currencies files are checked-in, so
# we do not need to re-generate those through Bazel.
mkdir -p packages/common/locales/extra
cp dist/bin/packages/common/locales/*.ts packages/common/locales
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
- name: Build bundle of tests to run on Saucelabs
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
- name: Wait and confirm Saucelabs tunnel has connected
run: ./tools/saucelabs/sauce-service.sh ready-wait
timeout-minutes: 3
- name: Running tests on Saucelabs.
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
- name: Stop Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh stop
adev-deploy:
needs: [adev]
if: needs.adev.result == 'success' && github.event_name == 'push' && github.ref_name == 'main'
@@ -295,11 +341,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@4b433074a806bbbd4d319264430740cd46e62f27
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@4b433074a806bbbd4d319264430740cd46e62f27
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
+2 -2
View File
@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/commit-message-based-labels@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/commit-message-based-labels@4b433074a806bbbd4d319264430740cd46e62f27
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/post-approval-changes@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/post-approval-changes@4b433074a806bbbd4d319264430740cd46e62f27
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: angular/dev-infra/github-actions/google-internal-tests@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/google-internal-tests@4b433074a806bbbd4d319264430740cd46e62f27
with:
run-tests-guide-url: http://go/angular-g3sync-start
github-token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@c83e99a12397014162531ca125c94549db55dd84
- uses: angular/dev-infra/github-actions/unified-status-check@4b433074a806bbbd4d319264430740cd46e62f27
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+4 -4
View File
@@ -22,15 +22,15 @@ jobs:
version: ['https://next.angular.io/', 'https://angular.io/']
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
with:
cache-node-modules: true
node-module-directories: |
./aio/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Run basic e2e and deployment config tests.
@@ -44,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
with:
cache-node-modules: true
node-module-directories: |
+3 -3
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
@@ -39,7 +39,7 @@ jobs:
# Upload the results as artifacts.
- name: 'Upload artifact'
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: SARIF file
path: results.sarif
@@ -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@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5
uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
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@c83e99a12397014162531ca125c94549db55dd84
uses: angular/dev-infra/github-actions/create-pr-for-changes@1421ff04811ecb45812930c5d1f61d912880e75f
with:
branch-prefix: update-cli-help
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
+84 -23
View File
@@ -140,7 +140,7 @@ groups:
- alxhub
- crisbeto
- ~jelbourn
- jessicajaniuk
- thePunderWoman
# =========================================================
# Framework: Compiler
@@ -183,7 +183,7 @@ groups:
- crisbeto
- devversion
- dylhunn
- jessicajaniuk
- thePunderWoman
- AndrewKushnir
# =========================================================
@@ -193,7 +193,7 @@ groups:
<<: *defaults
conditions:
- >
contains_any_globs(files.exclude("packages/core/schematics/*"), [
contains_any_globs(files.exclude("packages/core/schematics/*").exclude('packages/core/primitives/*'), [
'packages/core/**/{*,.*}',
'packages/examples/core/**/{*,.*}',
'packages/platform-browser/**/{*,.*}',
@@ -358,7 +358,7 @@ groups:
- crisbeto
- devversion
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -378,7 +378,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -415,7 +415,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -438,7 +438,7 @@ groups:
- andrewseguin
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -536,7 +536,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -615,7 +615,7 @@ groups:
reviewers:
users:
- alxhub
- jessicajaniuk
- thePunderWoman
# =========================================================
# Framework: Testing
@@ -644,7 +644,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -663,7 +663,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -682,7 +682,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -778,7 +778,7 @@ groups:
- atscott
- crisbeto
- dylhunn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
# =========================================================
@@ -813,7 +813,7 @@ groups:
- AndrewKushnir
- atscott
- dylhunn
- jessicajaniuk
- thePunderWoman
- josephperrott
- pkozlowski-opensource
@@ -867,7 +867,7 @@ groups:
reviewers:
users:
- AndrewKushnir
- jessicajaniuk
- thePunderWoman
# =========================================================
# Docs: Marketing
@@ -946,7 +946,7 @@ groups:
- alxhub
- AndrewKushnir
- atscott
- jessicajaniuk
- thePunderWoman
- jelbourn
# =========================================================
@@ -1078,11 +1078,12 @@ groups:
- josephperrott
- jelbourn
- bencodezen
- jessicajaniuk
- thePunderWoman
- dylhunn
- AndrewKushnir
- alxhub
- crisbeto
- devversion
- atscott
- pkozlowski-opensource
- ~JeanMeche
@@ -1143,7 +1144,7 @@ groups:
reviewers:
users:
- AndrewKushnir
- jessicajaniuk
- thePunderWoman
- josephperrott
# =========================================================
@@ -1200,7 +1201,7 @@ groups:
'tools/gulp-tasks/**/{*,.*}',
'tools/legacy-saucelabs/**/{*,.*}',
'tools/manual_api_docs/**/{*,.*}',
'tools/npm-patches/**/{*,.*}',
'tools/npm-patches*/**/{*,.*}',
'tools/rxjs/**/{*,.*}',
'tools/saucelabs-daemon/**/{*,.*}',
'tools/saucelabs/**/{*,.*}',
@@ -1242,7 +1243,7 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
reviews:
request: 3 # Request reviews from 3 people
@@ -1268,7 +1269,7 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
reviews:
request: 2 # Request reviews from 2 people
@@ -1294,7 +1295,7 @@ groups:
- atscott
- dylhunn
- ~jelbourn
- jessicajaniuk
- thePunderWoman
- pkozlowski-opensource
####################################################################################
@@ -1318,9 +1319,69 @@ groups:
- andrewseguin
- dgp1130
- ~jelbourn
- jessicajaniuk
- thePunderWoman
- josephperrott
# =========================================================
# Primitives
# =========================================================
# Angular team required reviews
primitives:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
users:
- pkozlowski-opensource # Pawel Kozlowski
- alxhub # Alex Rickabaugh
- thePunderWoman # Jessica Janiuk
- dylhunn # Dylan Hunn
- AndrewKushnir # Andrew Kushnir
- atscott # Andrew Scott
reviews:
required: 1
reviewed_for: required
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
# External team required reviews
primitives-shared:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
users:
- csmick # Cameron Smick
- lannka # Hongfei Ding
- eduhmc # Eduardo Huerta-Mercado
- ehlemur # Edward Lesmes
- ellenyuan # Ellen Yuan
- jatraman # Jatin Ramanathan
- jesse-good # Jesse Costello-Good
- mturco # Matt Turco
- neonstalwart # Ben Hockey
- nicholasyu-google # Nicholas Yu
- emspishak # Eric Spishak-Thomas
- iteriani # Thomas Nguyen
- tbondwilkinson # Tom Wilkinson
- rahatarmanahmed # Rahat Ahmed
reviews:
required: 1
reviewed_for: required
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
####################################################################################
# Override managed result groups
#
+141 -40
View File
@@ -1,3 +1,144 @@
<a name="17.3.7"></a>
# 17.3.7 (2024-05-01)
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [51ac883167](https://github.com/angular/angular/commit/51ac8831670637b562dfa135bec1e27a0b49f21d) | fix | don't type check the bodies of control flow nodes in basic mode ([#55558](https://github.com/angular/angular/pull/55558)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [af0eb846a5](https://github.com/angular/angular/commit/af0eb846a572d0e9de2c6bb7016ddd339473dc00) | fix | render hooks should not specifically run outside the Angular zone ([#55399](https://github.com/angular/angular/pull/55399)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [3eea50da64](https://github.com/angular/angular/commit/3eea50da644f6023e7f001b8738c54cb89aece4c) | fix | Scroller should scroll as soon as change detection completes ([#55105](https://github.com/angular/angular/pull/55105)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.6"></a>
# 17.3.6 (2024-04-25)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [826861b1fa](https://github.com/angular/angular/commit/826861b1fa7693010743f5dd881fb31ec33eee52) | fix | DeferBlockFixture.render should not wait for stability ([#55271](https://github.com/angular/angular/pull/55271)) |
| [5cf14da35c](https://github.com/angular/angular/commit/5cf14da35cce1ea4456bae4365d3dbe6667b1881) | fix | make `ActivatedRoute` inject correct instance inside `@defer` blocks ([#55374](https://github.com/angular/angular/pull/55374)) |
| [8979fba2c5](https://github.com/angular/angular/commit/8979fba2c5813673767a133caeec9ac768d17329) | fix | skip defer timers on the server ([#55480](https://github.com/angular/angular/pull/55480)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.5"></a>
# 17.3.5 (2024-04-17)
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.4"></a>
# 17.3.4 (2024-04-10)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [53427d875d](https://github.com/angular/angular/commit/53427d875d33de16b1dbb04e76a9fe32a2b8a445) | fix | invalid ImageKit quality parameter ([#55193](https://github.com/angular/angular/pull/55193)) |
| [766548c3ec](https://github.com/angular/angular/commit/766548c3ecc06ffd8f06ed9a7d0cbb3db6f1b0f6) | fix | skip transfer cache on client ([#55012](https://github.com/angular/angular/pull/55012)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.3"></a>
# 17.3.3 (2024-04-03)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [158ceaf062](https://github.com/angular/angular/commit/158ceaf062036bde2a828ee769580f99433a31a8) | fix | handle `ChainedInjector`s in injector debug utils ([#55144](https://github.com/angular/angular/pull/55144)) |
| [4d043992e5](https://github.com/angular/angular/commit/4d043992e5635a2f841b633fb38d0009a39f05d0) | fix | test cleanup should not throw if Zone is not present ([#55096](https://github.com/angular/angular/pull/55096)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [949dec26b8](https://github.com/angular/angular/commit/949dec26b860a500e90874ad778da74549d21d7e) | fix | avoid conflicts with some greek letters in control flow migration ([#55113](https://github.com/angular/angular/pull/55113)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.2"></a>
# 17.3.2 (2024-03-28)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [2b7bad5151](https://github.com/angular/angular/commit/2b7bad515100cbfa40b3e8d844bae13d43fd5602) | fix | invoke method-based tracking function with context ([#54960](https://github.com/angular/angular/pull/54960)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [b478dfbfda](https://github.com/angular/angular/commit/b478dfbfda3f32fbe723a1e8725e86490422520d) | fix | report errors when initializer APIs are used on private fields ([#55070](https://github.com/angular/angular/pull/55070)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [708ba8115f](https://github.com/angular/angular/commit/708ba8115f7ad05201db5c529aefe4dd48fc64c5) | fix | establish proper injector resolution order for `@defer` blocks ([#55079](https://github.com/angular/angular/pull/55079)) |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [cb433af0e1](https://github.com/angular/angular/commit/cb433af0e1ba61073eb4a02ccd75cf360e9fd409) | fix | include transferCache when cloning HttpRequest ([#54939](https://github.com/angular/angular/pull/54939)) |
| [64f202cab9](https://github.com/angular/angular/commit/64f202cab9e7a5c873b17bbddd02368006426152) | fix | manage different body types for caching POST requests ([#54980](https://github.com/angular/angular/pull/54980)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [2f9d94bc4a](https://github.com/angular/angular/commit/2f9d94bc4ab5a94e620a13404aba4e094f8b2344) | fix | account for variables in imports initializer ([#55081](https://github.com/angular/angular/pull/55081)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [365fd50407](https://github.com/angular/angular/commit/365fd504077d0e7509efc3077ea4ae8bbafb01f7) | fix | RouterLinkActive will always remove active classes when links are not active ([#54982](https://github.com/angular/angular/pull/54982)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.1"></a>
# 17.3.1 (2024-03-20)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [c0788200e2](https://github.com/angular/angular/commit/c0788200e26406bacb90ee0079ea9753eebd1b32) | fix | capture data bindings for content projection purposes in blocks ([#54876](https://github.com/angular/angular/pull/54876)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [99e9474aa2](https://github.com/angular/angular/commit/99e9474aa2ccccdf4385fbac6b62b1a4585ed4b4) | fix | symbol feature detection for the compiler ([#54711](https://github.com/angular/angular/pull/54711)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.0"></a>
# 17.3.0 (2024-03-13)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [1a6beae8a2](https://github.com/angular/angular/commit/1a6beae8a2bdcff27d4c1e402f98246a52247906) | feat | Enable template pipeline by default. ([#54571](https://github.com/angular/angular/pull/54571)) |
| [f386a04c9d](https://github.com/angular/angular/commit/f386a04c9ddc2951c8105d5cafcce7b4bedea569) | fix | handle two-way bindings to signal-based template variables in instruction generation ([#54714](https://github.com/angular/angular/pull/54714)) |
| [1f129f114e](https://github.com/angular/angular/commit/1f129f114edb21137d74471dd6b652848d2ffb68) | fix | not catching for loop empty tracking expressions ([#54772](https://github.com/angular/angular/pull/54772)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [12dc4d074e](https://github.com/angular/angular/commit/12dc4d074e63edaff626003ad6136a8d122b2ba6) | fix | account for as expression in docs extraction ([#54414](https://github.com/angular/angular/pull/54414)) |
| [da7fbb40f0](https://github.com/angular/angular/commit/da7fbb40f06e6e37504f69e7b335f8219f424de2) | fix | detect when the linker is working in unpublished angular and widen supported versions ([#54439](https://github.com/angular/angular/pull/54439)) |
| [492e03f699](https://github.com/angular/angular/commit/492e03f699ca95df4cf854885253f63a5a1fb5c1) | fix | flag two-way bindings to non-signal values in templates ([#54714](https://github.com/angular/angular/pull/54714)) |
| [5afa4f0ec1](https://github.com/angular/angular/commit/5afa4f0ec1b64b88ef875d48bd143e0f36e0a955) | fix | support `ModuleWithProviders` literal detection with `typeof` ([#54650](https://github.com/angular/angular/pull/54650)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [331b16efd2](https://github.com/angular/angular/commit/331b16efd2f5af876e6dc0ad2474ee7a87b00de5) | feat | add API to inject attributes on the host node ([#54604](https://github.com/angular/angular/pull/54604)) |
| [fb540e169a](https://github.com/angular/angular/commit/fb540e169a78a61f38d611f538eea8fdb0971f1d) | feat | add migration for invalid two-way bindings ([#54630](https://github.com/angular/angular/pull/54630)) |
| [c687b8f453](https://github.com/angular/angular/commit/c687b8f4531252cd1c3dfbb9a7bd42bdbe666a36) | feat | expose new `output()` API ([#54650](https://github.com/angular/angular/pull/54650)) |
| [c809069f21](https://github.com/angular/angular/commit/c809069f213244afd0e2d803a6a43510b218e6f5) | feat | introduce `outputFromObservable()` interop function ([#54650](https://github.com/angular/angular/pull/54650)) |
| [aff65fd1f4](https://github.com/angular/angular/commit/aff65fd1f4a61ed76a6f9b623852f197eb3500e4) | feat | introduce `outputToObservable` interop helper ([#54650](https://github.com/angular/angular/pull/54650)) |
| [974958913c](https://github.com/angular/angular/commit/974958913ca632971f878a045537472f2c99c665) | feat | support TypeScript 5.4 ([#54414](https://github.com/angular/angular/pull/54414)) |
| [39a50f9a8d](https://github.com/angular/angular/commit/39a50f9a8df5afc4968d18924f3d9d7d6b649d3a) | fix | ensure all initializer functions run in an injection context ([#54761](https://github.com/angular/angular/pull/54761)) |
| [243ccce624](https://github.com/angular/angular/commit/243ccce62475ae03a2e727d2b3cb2d51a595c4a7) | fix | exclude class attribute intended for projection matching from directive matching ([#54800](https://github.com/angular/angular/pull/54800)) |
| [2909e9817d](https://github.com/angular/angular/commit/2909e9817daf69b6478d7d09229491a9a48cff16) | fix | prevent infinite loops in clobbered elements check ([#54425](https://github.com/angular/angular/pull/54425)) |
| [7243c704cf](https://github.com/angular/angular/commit/7243c704cf8a4986fae419793027458e142658f0) | fix | return a readonly signal on `asReadonly`. ([#54706](https://github.com/angular/angular/pull/54706)) |
| [bb35414a38](https://github.com/angular/angular/commit/bb35414a38f8a8447d03c66dc5bf60b84409f9e3) | perf | speed up retrieval of `DestroyRef` in `EventEmitter` ([#54748](https://github.com/angular/angular/pull/54748)) |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [8d37ed035c](https://github.com/angular/angular/commit/8d37ed035c9e9796ba9a7b1f055404ea220dbb3b) | fix | exclude caching for authenticated HTTP requests ([#54746](https://github.com/angular/angular/pull/54746)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [c1c7384e02](https://github.com/angular/angular/commit/c1c7384e02becc623c6a42985f7178ca98137264) | feat | Add reusable types for router guards ([#54580](https://github.com/angular/angular/pull/54580)) |
| [7225485311](https://github.com/angular/angular/commit/722548531108e247660ebe6966b99b57d510b615) | fix | Navigations triggered by cancellation events should cancel previous navigation ([#54710](https://github.com/angular/angular/pull/54710)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.2.4"></a>
# 17.2.4 (2024-03-06)
### compiler-cli
@@ -38,46 +179,6 @@
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.0-next.1"></a>
# 17.3.0-next.1 (2024-02-27)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [707bfc9b32](https://github.com/angular/angular/commit/707bfc9b326f321f302dccfdfebef7380c914eb5) | perf | `AsyncPipe` should not call `markForCheck` on subscription ([#54554](https://github.com/angular/angular/pull/54554)) |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [1a6beae8a2](https://github.com/angular/angular/commit/1a6beae8a2bdcff27d4c1e402f98246a52247906) | feat | Enable template pipeline by default. ([#54571](https://github.com/angular/angular/pull/54571)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [12dc4d074e](https://github.com/angular/angular/commit/12dc4d074e63edaff626003ad6136a8d122b2ba6) | fix | account for as expression in docs extraction ([#54414](https://github.com/angular/angular/pull/54414)) |
| [f578889ca2](https://github.com/angular/angular/commit/f578889ca2dbb22f2e050d4c4af6aa2d597bf70d) | fix | catch function instance properties in interpolated signal diagnostic ([#54325](https://github.com/angular/angular/pull/54325)) |
| [f04ecc0cda](https://github.com/angular/angular/commit/f04ecc0cdaeb3a292a748b1ccc94ce70a573fc79) | fix | identify aliased initializer functions ([#54480](https://github.com/angular/angular/pull/54480)) |
| [f5c566c079](https://github.com/angular/angular/commit/f5c566c0793eacf9ca146c8a6b8da15b0e8f4c4d) | fix | identify aliased initializer functions ([#54609](https://github.com/angular/angular/pull/54609)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [331b16efd2](https://github.com/angular/angular/commit/331b16efd2f5af876e6dc0ad2474ee7a87b00de5) | feat | add API to inject attributes on the host node ([#54604](https://github.com/angular/angular/pull/54604)) |
| [974958913c](https://github.com/angular/angular/commit/974958913ca632971f878a045537472f2c99c665) | feat | support TypeScript 5.4 ([#54414](https://github.com/angular/angular/pull/54414)) |
| [dcb9deb363](https://github.com/angular/angular/commit/dcb9deb3631ff4e839f738f2fb97ca6b894256ef) | fix | collect providers from NgModules while rendering `@defer` block ([#52881](https://github.com/angular/angular/pull/52881)) |
| [ff40c9f762](https://github.com/angular/angular/commit/ff40c9f762b2c14870ac2859201ece9660087e79) | fix | fix typo in injectors.svg file ([#54596](https://github.com/angular/angular/pull/54596)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [71e0c7df69](https://github.com/angular/angular/commit/71e0c7df695f274a54d5ffb9dfc0587d06d3a953) | fix | resolve infinite loop for a single line element with a long tag name and angle bracket on a new line ([#54588](https://github.com/angular/angular/pull/54588)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.0-next.0"></a>
# 17.3.0-next.0 (2024-02-21)
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [da7fbb40f0](https://github.com/angular/angular/commit/da7fbb40f06e6e37504f69e7b335f8219f424de2) | fix | detect when the linker is working in unpublished angular and widen supported versions ([#54439](https://github.com/angular/angular/pull/54439)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.2.2"></a>
# 17.2.2 (2024-02-21)
### common
-5
View File
@@ -389,7 +389,6 @@ The following documents can help you sort out issues with GitHub accounts and mu
[angular-group]: https://groups.google.com/forum/#!forum/angular
[coc]: https://github.com/angular/code-of-conduct/blob/main/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[corporate-cla]: https://cla.developers.google.com/about/google-corporate
@@ -398,7 +397,3 @@ The following documents can help you sort out issues with GitHub accounts and mu
[discord]: https://discord.gg/angular
[individual-cla]: https://cla.developers.google.com/about/google-individual
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: https://jsfiddle.net/
[plunker]: https://plnkr.co/edit
[runnable]: https://runnable.com/
[stackoverflow]: https://stackoverflow.com/questions/tagged/angular
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2023 Google LLC. https://angular.io/license
Copyright (c) 2010-2024 Google LLC. https://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+3 -3
View File
@@ -1,7 +1,7 @@
<h1 align="center">Angular - The modern web developer's platform</h1>
<p align="center">
<img src="aio/src/assets/images/logos/angular/angular_renaissance.png" alt="angular-logo" width="120px" height="120px"/>
<img src="adev/src/assets/images/press-kit/angular_icon_gradient.gif" alt="angular-logo" width="120px" height="120px"/>
<br>
<em>Angular is a development platform for building mobile and desktop web applications
<br> using TypeScript/JavaScript and other languages.</em>
@@ -167,8 +167,8 @@ Join the conversation and help the community.
[angularmaterial]: https://material.angular.io/
[cli]: https://angular.dev/tools/cli
[architecture]: https://angular.dev/essentials
[componentstemplates]: https://angular.dev/tutorials/learn-angular/components-in-angular
[forms]: https://angular.dev/tutorials/learn-angular/forms
[componentstemplates]: https://angular.dev/tutorials/learn-angular/1-components-in-angular
[forms]: https://angular.dev/tutorials/learn-angular/15-forms
[api]: https://angular.dev/api
[angularelements]: https://angular.dev/guide/elements
[ssr]: https://angular.dev/guide/ssr
+3 -17
View File
@@ -9,20 +9,6 @@ workspace(
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//:yarn.bzl", "YARN_LABEL")
# Add a patch fix for rules_webtesting v0.3.5 required for enabling runfiles on Windows.
# TODO: Remove the http_archive for this transitive dependency when a release is cut
# for https://github.com/bazelbuild/rules_webtesting/commit/581b1557e382f93419da6a03b91a45c2ac9a9ec8
# and the version is updated in rules_nodejs.
http_archive(
name = "io_bazel_rules_webtesting",
patch_args = ["-p1"],
patches = [
"//:tools/bazel-repo-patches/rules_webtesting__windows_runfiles_fix.patch",
],
sha256 = "e9abb7658b6a129740c0b3ef6f5a2370864e102a5ba5ffca2cea565829ed825a",
urls = ["https://github.com/bazelbuild/rules_webtesting/releases/download/0.3.5/rules_webtesting.tar.gz"],
)
http_archive(
name = "build_bazel_rules_nodejs",
patches = [
@@ -206,10 +192,10 @@ cldr_xml_data_repository(
# sass rules
http_archive(
name = "io_bazel_rules_sass",
sha256 = "974f5e14c54d300ca63a67910ebfcea6d838d8811fefc61184f2872107152705",
strip_prefix = "rules_sass-888dd9c02709e228b60a8b3e51e2b16b2ca5279d",
sha256 = "ca27b3dcd294b134ccc9d0a1c6b63b810e115204e87a2b3d4cc247f5676d0a85",
strip_prefix = "rules_sass-c3f163fd9f570846c2aae4bf16ac69b4217e6f3b",
urls = [
"https://github.com/bazelbuild/rules_sass/archive/888dd9c02709e228b60a8b3e51e2b16b2ca5279d.zip",
"https://github.com/bazelbuild/rules_sass/archive/c3f163fd9f570846c2aae4bf16ac69b4217e6f3b.zip",
],
)
+29 -18
View File
@@ -1,8 +1,9 @@
load("//:packages.bzl", "link_packages")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin")
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
load("@bazel_skylib//lib:collections.bzl", "collections")
load("//adev/tools/local_deps:index.bzl", "ensure_local_package_deps", "link_local_packages")
package(default_visibility = ["//visibility:public"])
@@ -30,7 +31,17 @@ TEST_FILES = APPLICATION_FILES + [
["**/*.spec.ts"],
)
APPLICATION_DEPS = link_packages([
APPLICATION_ASSETS = [
"//adev/src/assets/images",
"//adev/src/assets/textures",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
"//adev/src/assets/icons",
"//adev/src/assets:api",
"//adev/src/assets:content",
]
APPLICATION_DEPS = [
"@npm//@angular-devkit/build-angular",
"@npm//@angular/animations",
"@npm//@angular/cdk",
@@ -50,13 +61,6 @@ APPLICATION_DEPS = link_packages([
"@npm//ogl",
"@npm//rxjs",
"@npm//typescript",
"//adev/src/assets/images",
"//adev/src/assets/textures",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
"//adev/src/assets/icons",
"//adev/src/assets:api",
"//adev/src/assets:content",
"@npm//@typescript/vfs",
"@npm//@codemirror/state",
"@npm//@codemirror/view",
@@ -76,11 +80,10 @@ APPLICATION_DEPS = link_packages([
"@npm//xterm",
"@npm//xterm-addon-fit",
"@npm//angular-split",
])
]
TEST_DEPS = APPLICATION_DEPS + link_packages([
TEST_DEPS = APPLICATION_DEPS + [
"@npm//@angular/platform-browser-dynamic",
"@npm//@angular/build-tooling/bazel/browsers/chromium",
"@npm//@types/jasmine",
"@npm//@types/node",
"@npm//assert",
@@ -90,8 +93,13 @@ TEST_DEPS = APPLICATION_DEPS + link_packages([
"@npm//karma-coverage",
"@npm//karma-jasmine",
"@npm//karma-jasmine-html-reporter",
"//aio/tools:windows-chromium-path",
])
]
# Create `npm_link` targets for all dependencies that correspond to a
# first-party Angular package that can be built from `HEAD`.
link_local_packages(
all_deps = collections.uniq(APPLICATION_DEPS + TEST_DEPS),
)
copy_to_bin(
name = "application_files_bin",
@@ -129,7 +137,7 @@ architect(
"--output-path=build",
] + config_based_architect_flags,
chdir = "$(RULEDIR)",
data = APPLICATION_DEPS + [
data = ensure_local_package_deps(APPLICATION_DEPS) + APPLICATION_ASSETS + [
":application_files_bin",
],
# Network is required to inline fonts.
@@ -149,7 +157,7 @@ architect(
"--watch",
],
chdir = package_name(),
data = APPLICATION_DEPS + [
data = ensure_local_package_deps(APPLICATION_DEPS) + APPLICATION_ASSETS + [
":application_files_bin",
],
tags = [
@@ -164,7 +172,10 @@ architect_test(
"--no-watch",
],
chdir = package_name(),
data = TEST_DEPS + TEST_FILES,
data = ensure_local_package_deps(TEST_DEPS) + TEST_FILES + APPLICATION_ASSETS + [
"//aio/tools:windows-chromium-path",
"@npm//@angular/build-tooling/bazel/browsers/chromium",
],
env = {
"CHROME_BIN": "../$(CHROMIUM)",
},
+8 -9
View File
@@ -19,9 +19,7 @@
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"externalDependencies": [
"path"
],
"externalDependencies": ["path"],
"outputPath": "dist/angular-dev",
"index": "src/index.html",
"browser": "src/main.ts",
@@ -29,11 +27,7 @@
"polyfills": ["src/polyfills.ts", "zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/robots.txt",
"src/assets"
],
"assets": ["src/favicon.ico", "src/robots.txt", "src/assets"],
"styles": ["@angular/docs/styles/global-styles.scss"],
"scripts": [],
"webWorkerTsConfig": "tsconfig.worker.json",
@@ -99,6 +93,11 @@
}
},
"cli": {
"analytics": "dca119a9-da31-47f7-a6cb-b60541037021"
"analytics": "dca119a9-da31-47f7-a6cb-b60541037021",
"cache": {
// Disable build caching as the cache folder will just be dropped
// when run under Bazel sandboxed execution.
"enabled": false
}
}
}
-135
View File
@@ -1,135 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {input, confirm} from '@inquirer/prompts';
import {existsSync} from 'fs';
import {copyFile, mkdir, writeFile} from 'fs/promises';
import {dirname, join} from 'path';
import {fileURLToPath} from 'url';
import {copyFolder} from './utils/fs.mjs';
const EXAMPLES_BASE = join(
dirname(fileURLToPath(import.meta.url)),
'../../src/content/examples',
);
const STACKBLITZ_TEMPLATE = join(
dirname(fileURLToPath(import.meta.url)),
'../../src/content/stackblitz-template',
);
const MAIN_TS_FILENAME = 'main.ts';
const STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json';
createExample();
async function createExample(): Promise<void> {
console.info('Create example');
const name = await input({message: 'What is the example name?'});
const description = await input({message: 'Could you provide the description of the example?'});
const tags: string[] = [];
let addMoreTags = true;
while (addMoreTags) {
const tag = await input({message: 'Could you provide the tag of the example?'});
if (!!tag) {
tags.push(tag);
}
console.info();
addMoreTags = await confirm({message: 'Would you like to add another tag?', default: true});
}
const shouldCopySrcAppFolderFromTemplate = await confirm({
message: 'Would you like to copy /src/app/**.* from template?',
default: true,
});
const shouldCopyMainTsFromTemplate = await confirm({
message: 'Would you like to copy /src/main.ts from template?',
default: false,
});
const examplePath = join(EXAMPLES_BASE, name);
await createEmptyExample(examplePath, name, description, tags);
await copyFilesFromTemplate(
examplePath,
shouldCopySrcAppFolderFromTemplate,
shouldCopyMainTsFromTemplate,
);
}
/**
* Create the directory and marker files for the new example.
*/
async function createEmptyExample(
examplePath: string,
exampleName: string,
description: string,
tags: string[],
): Promise<void> {
validateExampleName(exampleName);
await ensureExamplePath(examplePath);
await writeStackBlitzFile(examplePath, description, tags);
}
function validateExampleName(exampleName: string): void {
if (/\s/.test(exampleName)) {
throw new Error(`Unable to create example. The example name contains spaces: '${exampleName}'`);
}
}
/**
* Ensure that the new example directory exists.
*/
async function ensureExamplePath(examplePath: string): Promise<void> {
if (existsSync(examplePath)) {
throw new Error(
`Unable to create example. The path to the new example already exists: ${examplePath}`,
);
}
await mkdir(examplePath);
}
/**
* Write the `stackblitz.json` file into the new example.
*/
async function writeStackBlitzFile(
examplePath: string,
description: string,
tags: string[],
): Promise<void> {
const config = {
description,
ignore: ['**/*.d.ts', '**/*.js', '**/*.[1,2].*'],
tags,
};
await writeFile(
join(examplePath, STACKBLITZ_CONFIG_FILENAME),
JSON.stringify(config, null, 2) + '\n',
);
}
async function copyFilesFromTemplate(
examplePath: string,
shouldCopySrcAppFolderFromTemplate: boolean,
shouldCopyMainTsFromTemplate: boolean,
): Promise<void> {
if (shouldCopySrcAppFolderFromTemplate) {
const appTemplateFolderPath = join(STACKBLITZ_TEMPLATE, 'src/app');
const exampleAppFolderPath = join(examplePath, 'src/app');
await copyFolder(appTemplateFolderPath, exampleAppFolderPath);
}
if (shouldCopyMainTsFromTemplate) {
const mainTsTemplatePath = join(STACKBLITZ_TEMPLATE, 'src', MAIN_TS_FILENAME);
const exampleMainTsPath = join(examplePath, 'src', MAIN_TS_FILENAME);
await copyFile(mainTsTemplatePath, exampleMainTsPath);
}
}
@@ -1,158 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import ts from 'typescript';
import * as fs from 'fs';
import {dirname, join, relative} from 'path';
import {fileURLToPath} from 'url';
import {readFile, writeFile} from 'fs/promises';
const TYPESCRIPT_EXTENSION = '.ts';
const SKIP_FILES_WITH_EXTENSIONS = ['.e2e-spec.ts', '.spec.ts', '.po.ts'];
const EXAMPLES_PATH = `../../content/examples`;
interface File {
path: string;
content: string;
}
interface AnalyzedFiles {
path: string;
componentNames: string[];
}
/** Absolute disk path to the project directory. */
const examplesDir = join(
dirname(fileURLToPath(import.meta.url)),
'../../src/content/examples',
);
const projectDir = join(dirname(fileURLToPath(import.meta.url)), '../../src');
const previewsComponentMapFileName = 'previews.ts';
main();
async function main() {
const files = await retrieveAllTypescriptFiles(
examplesDir,
(path) => !SKIP_FILES_WITH_EXTENSIONS.some((extensionToSkip) => path.endsWith(extensionToSkip)),
);
const filesWithComponent = files
.map((file) => ({
componentNames: analyzeFile(file),
path: file.path,
}))
.filter((result) => result.componentNames.length > 0);
const previewsComponentMap = generatePreviewsComponentMap(filesWithComponent);
fs.mkdirSync(join(projectDir, 'assets/previews'), {recursive: true});
await writeFile(
join(projectDir, 'assets/previews', previewsComponentMapFileName),
previewsComponentMap,
);
}
/** Recursively search the provided directory for all typescript files and asynchronously load them. */
function retrieveAllTypescriptFiles(
baseDir: string,
predicateFn: (path: string) => boolean,
): Promise<File[]> {
const typescriptFiles: Promise<File>[] = [];
const checkFilesInDirectory = (dir: string) => {
const files = fs.readdirSync(dir, {withFileTypes: true});
for (const file of files) {
const fullPathToFile = join(dir, file.name);
const relativeFilePath = relative(baseDir, fullPathToFile);
if (
file.isFile() &&
file.name.endsWith(TYPESCRIPT_EXTENSION) &&
predicateFn(relativeFilePath)
) {
typescriptFiles.push(
readFile(fullPathToFile, {encoding: 'utf-8'}).then((fileContent) => {
return {
path: relativeFilePath,
content: fileContent,
};
}),
);
} else if (file.isDirectory()) {
checkFilesInDirectory(fullPathToFile);
}
}
};
checkFilesInDirectory(baseDir);
return Promise.all(typescriptFiles);
}
/** Returns list of the `Standalone` @Component class names for given file */
function analyzeFile(file: File): string[] {
const componentClassNames: string[] = [];
const sourceFile = ts.createSourceFile(file.path, file.content, ts.ScriptTarget.Latest, false);
const visitNode = (node: ts.Node): void => {
if (ts.isClassDeclaration(node)) {
const decorators = ts.getDecorators(node);
const componentName = node.name ? node.name.text : null;
if (decorators && decorators.length) {
for (const decorator of decorators) {
const call = decorator.expression;
if (
ts.isCallExpression(call) &&
ts.isIdentifier(call.expression) &&
call.expression.text === 'Component' &&
call.arguments.length > 0 &&
ts.isObjectLiteralExpression(call.arguments[0])
) {
const hasStandaloneProperty = call.arguments[0].properties.some(
(property) =>
property.name &&
ts.isIdentifier(property.name) &&
property.name.text === 'standalone',
);
if (hasStandaloneProperty && componentName) {
componentClassNames.push(componentName);
}
}
}
}
}
ts.forEachChild(node, visitNode);
};
visitNode(sourceFile);
return componentClassNames;
}
function generatePreviewsComponentMap(data: AnalyzedFiles[]): string {
let result = '';
for (const fileData of data) {
for (const componentName of fileData.componentNames) {
const key = `${fileData.path}${
fileData.componentNames.length > 1 ? '_' + componentName : ''
}`.replace(/\\/g, '/');
result += `['${key}']: () => import('${EXAMPLES_PATH}/${fileData.path
.replace(/\\/g, '/')
.replace('.ts', '')}').then(c => c.${componentName}),\n`;
}
}
return fs
.readFileSync('scripts/examples/previews-components.template', 'utf8')
.replace(/\${previewsComponents}/g, result);
}
@@ -1,23 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
/**
******************************************************************************
* DO NOT MANUALLY EDIT THIS FILE. THIS FILE IS AUTOMATICALLY GENERATED.
******************************************************************************
*/
import {Type} from '@angular/core';
/**
* Map of the previews components, values are functions which returns the promise of the component type, which will be displayed as preview in the ExampleViewer component.
* Keys has to be equal to paths written down in the docs markdown files.
*/
export const PREVIEWS_COMPONENTS_MAP: Record<string, () => Promise<Type<unknown>>> = {
${previewsComponents}
};
@@ -1,221 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {dirname, join} from 'path';
import {readFile, writeFile} from 'fs/promises';
import {
ASSETS_EXAMPLE_PATH,
CSS_TS_COPYRIGHT,
EXAMPLES_PATH,
HTML_COPYRIGHT,
STACKBLITZ_TEMPLATE_PATH,
STACKBLITZ_CONFIG_FILENAME,
TEMPORARY_EXAMPLES_PATH,
EXCLUDE_FILES_FOR_STACKBLITZ,
} from './utils/examples-constants.mjs';
import {copyFolder, createFolder, removeFolder} from './utils/fs.mjs';
import jsdom from 'jsdom';
import {glob} from 'glob';
import {regionParser} from './../../prerender/markdown-pipeline/regions/region-parser.mjs';
interface StackblitzConfig {
ignore: string[];
file: string;
tags: string[];
description: string;
}
export async function generateStackblitzExample(
exampleFolderName: string,
primaryFilePath: string,
title: string,
) {
const exampleDir = join(EXAMPLES_PATH, exampleFolderName);
const temporaryExampleDir = join(TEMPORARY_EXAMPLES_PATH, exampleFolderName);
const config = await readFile(join(exampleDir, STACKBLITZ_CONFIG_FILENAME), 'utf-8');
const stackblitzConfig: StackblitzConfig = JSON.parse(config);
primaryFilePath = join(...primaryFilePath.split('/'));
await createFolder(temporaryExampleDir);
await combineTemplateWithExample(exampleDir, temporaryExampleDir);
await generateStackblitzHtml(
temporaryExampleDir,
stackblitzConfig,
exampleFolderName,
primaryFilePath,
title,
);
await removeFolder(temporaryExampleDir);
}
async function combineTemplateWithExample(
exampleDir: string,
temporaryExampleDir: string,
): Promise<void> {
// Copy template files to TEMP folder
await copyFolder(STACKBLITZ_TEMPLATE_PATH, temporaryExampleDir);
// Copy example files to TEMP folder
await copyFolder(exampleDir, temporaryExampleDir);
}
async function generateStackblitzHtml(
temporaryExampleDir: string,
stackBlitzConfig: StackblitzConfig,
exampleFolderName: string,
primaryFilePath: string,
title: string,
): Promise<void> {
const defaultIncludes = [
'**/*.ts',
'**/*.js',
'**/*.css',
'**/*.html',
'**/*.md',
'**/*.json',
'**/*.svg',
];
const exampleFilePaths = await glob(defaultIncludes, {
cwd: temporaryExampleDir,
nodir: true,
dot: true,
ignore: stackBlitzConfig.ignore,
});
const postData = await createPostData(
temporaryExampleDir,
stackBlitzConfig,
exampleFilePaths,
title,
);
const primaryFile = getPrimaryFile(primaryFilePath ?? stackBlitzConfig.file, exampleFilePaths);
const html = createStackblitzHtml(postData, primaryFile);
const stackblitzHtmlPath = join(
join(ASSETS_EXAMPLE_PATH, exampleFolderName),
`${primaryFile}.html`,
);
await createFolder(dirname(stackblitzHtmlPath));
await writeFile(stackblitzHtmlPath, html, 'utf-8');
}
function getPrimaryFile(primaryFilePath: string, exampleFilePaths: string[]): string {
if (primaryFilePath) {
if (!exampleFilePaths.some((filePath) => filePath === primaryFilePath)) {
throw new Error(`The specified primary file (${primaryFilePath}) does not exist!`);
}
return primaryFilePath;
} else {
const defaultPrimaryFilePaths = [
'src/app/app.component.html',
'src/app/app.component.ts',
'src/app/main.ts',
];
const primaryFile = defaultPrimaryFilePaths.find((path) =>
exampleFilePaths.some((filePath) => filePath === path),
);
if (!primaryFile) {
throw new Error(
`None of the default primary files (${defaultPrimaryFilePaths.join(', ')}) exists.`,
);
}
return primaryFile;
}
}
async function createPostData(
exampleDir: string,
config: StackblitzConfig,
exampleFilePaths: string[],
title: string,
): Promise<Record<string, string>> {
const postData: Record<string, string> = {};
for (const filePath of exampleFilePaths) {
if (EXCLUDE_FILES_FOR_STACKBLITZ.some((excludedFile) => filePath.endsWith(excludedFile))) {
continue;
}
let content = await readFile(join(exampleDir, filePath), 'utf-8');
content = appendCopyright(filePath, content);
content = extractRegions(filePath, content);
postData[`project[files][${filePath}]`] = content;
}
const tags = ['angular', 'example', ...(config.tags || [])];
tags.forEach((tag, index) => (postData[`project[tags][${index}]`] = tag));
postData['project[description]'] = `Angular Example - ${config.description}`;
postData['project[template]'] = 'node';
postData['project[title]'] = title ?? 'Angular Example';
return postData;
}
function createStackblitzHtml(postData: Record<string, string>, primaryFile: string): string {
const baseHtml = createBaseStackblitzHtml(primaryFile);
const doc = new jsdom.JSDOM(baseHtml).window.document;
const form = doc.querySelector('form');
for (const [key, value] of Object.entries(postData)) {
const element = htmlToElement(doc, `<input type="hidden" name="${key}">`);
if (element && form) {
element.setAttribute('value', value as string);
form.appendChild(element);
}
}
return doc.documentElement.outerHTML;
}
function createBaseStackblitzHtml(primaryFile: string) {
const file = `?file=${primaryFile}`;
const action = `https://stackblitz.com/run${file}`;
return `
<!DOCTYPE html><html lang="en"><body>
<form id="mainForm" method="post" action="${action}" target="_self"></form>
<script>
var embedded = 'ctl=1';
var isEmbedded = window.location.search.indexOf(embedded) > -1;
if (isEmbedded) {
var form = document.getElementById('mainForm');
var action = form.action;
var actionHasParams = action.indexOf('?') > -1;
var symbol = actionHasParams ? '&' : '?'
form.action = form.action + symbol + embedded;
}
document.getElementById("mainForm").submit();
</script>
</body></html>
`.trim();
}
function appendCopyright(filename: string, content: string): string {
if (filename.endsWith('.html')) {
return `${HTML_COPYRIGHT}${content}`;
} else if (filename.endsWith('.ts') || filename.endsWith('.css')) {
return `${CSS_TS_COPYRIGHT}${content}`;
}
return content;
}
function htmlToElement(document: Document, html: string) {
const div = document.createElement('div');
div.innerHTML = html;
return div.firstElementChild;
}
function extractRegions(path: string, contents: string): string {
const regionParserResult = regionParser(contents, path);
return regionParserResult.contents;
}
-8
View File
@@ -1,8 +0,0 @@
{
"extends": "../../tsconfig.json",
"ts-node": {
"compilerOptions": {
"module": "es2020"
}
}
}
@@ -1,55 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {dirname, join} from 'path';
import {fileURLToPath} from 'url';
const BASE = dirname(fileURLToPath(import.meta.url));
// Directory paths
export const ASSETS_EXAMPLE_PATH = join(
BASE,
'../../../src/assets/content/examples',
);
export const EXAMPLES_PATH = join(BASE, '../../../src/content/examples');
export const STACKBLITZ_TEMPLATE_PATH = join(
BASE,
'../../../src/content/stackblitz-template',
);
export const TEMPORARY_EXAMPLES_PATH = join(fileURLToPath(import.meta.url), '../../../../TEMP');
// Extensions
export const TEST_FILES_EXTENSION_SUFFIX = '.spec.ts';
export const TEST_FILES_E2E_EXTENSION_SUFFIX = '.e2e-spec.ts';
// Filename
// TODO: BUILD.bazel and example-config will be deprecated when migration to standalone examples will be done.
export const BUILD_BAZEL_FILENAME = 'BUILD.bazel';
export const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
export const STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json';
// Copyright
const PAD = '\n\n';
const COPYRIGHT =
'@license\n' +
'Copyright Google LLC All Rights Reserved.\n' +
'\n' +
'Use of this source code is governed by an MIT-style license that can be\n' +
'found in the LICENSE file at https://angular.dev/license\n';
export const CSS_TS_COPYRIGHT = `/*\n${COPYRIGHT}\n*/${PAD}`;
export const HTML_COPYRIGHT = `<!-- \n${COPYRIGHT}\n-->${PAD}`;
// Exclude
export const EXCLUDE_FILES_FOR_STACKBLITZ = [
STACKBLITZ_CONFIG_FILENAME,
BUILD_BAZEL_FILENAME,
EXAMPLE_CONFIG_FILENAME,
TEST_FILES_EXTENSION_SUFFIX,
TEST_FILES_E2E_EXTENSION_SUFFIX,
];
-77
View File
@@ -1,77 +0,0 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {Dirent, existsSync, readdirSync} from 'fs';
import {copyFile, mkdir, readFile, readdir, rm, stat} from 'fs/promises';
import {join, relative} from 'path';
export async function copyFolder(source: string, destination: string) {
if (!existsSync(destination)) {
await mkdir(destination, {recursive: true});
}
const files = await readdir(source);
for (const file of files) {
const sourcePath = join(source, file);
const destPath = join(destination, file);
const stats = await stat(sourcePath);
const isDirectory = await stats.isDirectory();
if (isDirectory) {
await copyFolder(sourcePath, destPath);
} else {
await copyFile(sourcePath, destPath);
}
}
}
/** Recursively search the provided directory for all files which satisfy predicate and asynchronously load them. */
export function retrieveFiles(
baseDir: string,
predicate?: (file: Dirent) => boolean,
): Promise<{path: string; content: string}[]> {
const filesWithContent: Promise<{path: string; content: string}>[] = [];
const checkFilesInDirectory = (dir: string) => {
const files = readdirSync(dir, {withFileTypes: true});
for (const file of files) {
const fullPathToFile = join(dir, file.name);
if (file.isFile() && (predicate ? predicate(file) : true)) {
filesWithContent.push(
readFile(fullPathToFile, {encoding: 'utf-8'}).then((fileContent) => {
return {
path: relative(baseDir, fullPathToFile),
content: fileContent,
};
}),
);
} else if (file.isDirectory()) {
checkFilesInDirectory(fullPathToFile);
}
}
};
checkFilesInDirectory(baseDir);
return Promise.all(filesWithContent);
}
export async function createFolder(path: string): Promise<void> {
if (!existsSync(path)) {
await mkdir(path, {recursive: true});
}
}
export async function removeFolder(path: string): Promise<void> {
if (existsSync(path)) {
await rm(path, {recursive: true});
}
}
+74
View File
@@ -0,0 +1,74 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
export const VERSIONS_CONFIG = {
currentVersion: 'stable',
historicalVersionsLinkPattern: 'https://v{{version}}.angular.dev',
mainVersions: [
{
version: 'stable',
url: 'https://angular.dev',
},
{
version: 'v16',
url: 'https://v16.angular.io/docs',
},
{
version: 'v15',
url: 'https://v15.angular.io/docs',
},
{
version: 'v14',
url: 'https://v14.angular.io/docs',
},
{
version: 'v13',
url: 'https://v13.angular.io/docs',
},
{
version: 'v12',
url: 'https://v12.angular.io/docs',
},
{
version: 'v11',
url: 'https://v11.angular.io/docs',
},
{
version: 'v10',
url: 'https://v10.angular.io/docs',
},
{
version: 'v9',
url: 'https://v9.angular.io/docs',
},
{
version: 'v8',
url: 'https://v8.angular.io/docs',
},
{
version: 'v7',
url: 'https://v7.angular.io/docs',
},
{
version: 'v6',
url: 'https://v6.angular.io/docs',
},
{
version: 'v5',
url: 'https://v5.angular.io/docs',
},
{
version: 'v4',
url: 'https://v4.angular.io/docs',
},
{
version: 'v2',
url: 'https://v2.angular.io/docs',
},
],
};
@@ -116,7 +116,7 @@
</div>
</div>
<p class="docs-license">
Super-powered by Google ©2010-2023. Code licensed under an
Super-powered by Google ©2010-2024. Code licensed under an
<a routerLink="/license" title="License text">MIT-style License</a>
. Documentation licensed under
<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>
@@ -10,6 +10,7 @@ import {HttpClient} from '@angular/common/http';
import {Injectable, inject} from '@angular/core';
import {DocContent, DocsContentLoader} from '@angular/docs';
import {Router} from '@angular/router';
import {firstValueFrom} from 'rxjs';
import {map} from 'rxjs/operators';
@Injectable()
@@ -27,12 +28,13 @@ export class ContentLoader implements DocsContentLoader {
try {
this.cache.set(
path,
this.httpClient
.get(`assets/content/${path}`, {
responseType: 'text',
})
.pipe(map((contents) => ({contents, id: path})))
.toPromise(),
firstValueFrom(
this.httpClient
.get(`assets/content/${path}`, {
responseType: 'text',
})
.pipe(map((contents) => ({contents, id: path}))),
),
);
} catch {
this.router.navigateByUrl('/404');
@@ -82,7 +82,7 @@ export class ThemeManager {
}
private watchPreferredColorScheme() {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
window.matchMedia(PREFERS_COLOR_SCHEME_DARK).addEventListener('change', (event) => {
const preferredScheme = event.matches ? 'dark' : 'light';
this.setThemeBodyClasses(preferredScheme);
});
@@ -90,5 +90,5 @@ export class ThemeManager {
}
function preferredScheme(): 'dark' | 'light' {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
return window.matchMedia(PREFERS_COLOR_SCHEME_DARK).matches ? 'dark' : 'light';
}
@@ -7,74 +7,7 @@
*/
import {Injectable, VERSION, computed, signal} from '@angular/core';
// TODO(josephperrott): extract this out of the file into a managed location.
const VERSIONS_CONFIG = {
currentVersion: 'stable',
historicalVersionsLinkPattern: 'https://v{{version}}.angular.dev',
mainVersions: [
{
version: 'stable',
url: 'https://angular.dev',
},
{
version: 'v16',
url: 'https://v16.angular.io/docs',
},
{
version: 'v15',
url: 'https://v15.angular.io/docs',
},
{
version: 'v14',
url: 'https://v14.angular.io/docs',
},
{
version: 'v13',
url: 'https://v13.angular.io/docs',
},
{
version: 'v12',
url: 'https://v12.angular.io/docs',
},
{
version: 'v11',
url: 'https://v11.angular.io/docs',
},
{
version: 'v10',
url: 'https://v10.angular.io/docs',
},
{
version: 'v9',
url: 'https://v9.angular.io/docs',
},
{
version: 'v8',
url: 'https://v8.angular.io/docs',
},
{
version: 'v7',
url: 'https://v7.angular.io/docs',
},
{
version: 'v6',
url: 'https://v6.angular.io/docs',
},
{
version: 'v5',
url: 'https://v5.angular.io/docs',
},
{
version: 'v4',
url: 'https://v4.angular.io/docs',
},
{
version: 'v2',
url: 'https://v2.angular.io/docs',
},
],
};
import {VERSIONS_CONFIG} from '../constants/versions';
export interface Version {
displayName: string;
+207
View File
@@ -0,0 +1,207 @@
# EmbeddedEditor components, services and functionality
- [Scenarios](#scenarios)
- [Loading a project](#loading-a-project)
- [Updating the code](#updating-the-code)
- [Creating a new file](#creating-a-new-file)
- [Deleting a file](#deleting-a-file)
- [Switching a project](#switching-a-project)
- [Components and services](#components-and-services)
- [EmbeddedEditor](#EmbeddedEditor)
- [CodeEditor](#CodeEditor)
- [CodeMirrorEditor](#CodeMirrorEditor)
- [TypeScript Web Worker](#typescript-web-worker)
- [Preview](#Preview)
- [Terminal](#Terminal)
- [InteractiveTerminal](#InteractiveTerminal)
- [Console](#Console)
- [NodeRuntimeSandbox](#NodeRuntimeSandbox)
- [NodeRuntimeState](#NodeRuntimeState)
- [EmbeddedTutorialManager](#EmbeddedTutorialManager)
- [EditorUiState](#EditorUiState)
- [DownloadManager](#DownloadManager)
- [AlertManager](#AlertManager)
- [TypingsLoader](#TypingsLoader)
## External libraries
- [WebContainers API](https://webcontainers.io/)
- [CodeMirror](https://codemirror.net/)
- [@typescript/vfs](https://www.npmjs.com/package/@typescript/vfs)
- [Xterm.js](https://xtermjs.org/)
## Notes
- See [scripts/tutorials/README.md](/scripts/tutorials/README.md) for more information about the tutorials script.
- See [adev/src/content/tutorials/README.md](/adev/src/content/tutorials/README.md) for more information about the tutorials content.
---
## Scenarios
### Loading a project
1. The page responsible for the embedded editor lazy loads the [`EmbeddedEditor`](./embedded-editor.component.ts) component and the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts), then triggers the initialization of all components and services. The embedded editor is available in the following pages:
- homepage: https://angular.dev
- playground: https://angular.dev/playground
- tutorial pages: https://angular.dev/tutorials
2. The project assets are fetched by the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts). Meanwhile:
- The code editor is initialized
- The code editor initializes the TypeScript Web Worker, which initializes the "default file system map" using TypeScript's CDN.
- The WebContainer is initialized
- The terminal is initialized
3. The tutorial source code is mounted in the `WebContainer`'s filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts)
4. The tutorial project dependencies are installed by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
5. The development server is started by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts) and the types are loaded by the [`TypingsLoader`](./typings-loader.service.ts) service.
6. The preview is loaded with the URL provided by the WebContainer API after the development server is started.
7. The project is ready.
### Updating the code
1. The user update the code in the code editor.
2. The code editor state is updated on real time, without debouncing so that the user can see the changes in the code editor and CodeMirror can handle the changes accordingly.
3. At the same time, the changes are sent to the TypeScript web worker to provide diagnostics, autocomplete and type features as soon as possible.
4. The code changes are debounced to be written in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
5. After the debounce time is reached, the code changes are written in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts), then the user can see the changes in the preview.
### Creating a new file
1. The user clicks on the new file button.
2. The new file tab is opened.
3. The user types the new file name.
4. If the file name is valid, the file is created in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
- `..` is disallowed in the file name to prevent users to create files outside the `src` directory.
5. The file is added to the TypeScript virtual file system, allowing the TypeScript web worker to provide diagnostics, autocomplete and type features for the new file. Also, exports from the new file are available in other files.
6. The new file is added as the last tab in the code editor and the new file can be edited.
Note: If the new file name matches a file that already exists but is hidden in the code editor, the content for that file will show up in the created file. An example for a file that always exists is `index.html`.
### Deleting a file
1. The user clicks on the delete file button.
2. The file is deleted from the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
3. The file is removed from the TypeScript virtual file system.
4. The file is removed from the code editor tabs.
Note: Some files can't be deleted to prevent users to break the app, being `src/main.ts`and `src/index.html`
### Switching a project
The embedded editor considers a project change when the embedded editor was already initialized and the user changes the page in the following scenarios:
- Navigating through tutorial steps
- Going from the homepage after the embedded editor is initialized to the playground
- Going from a tutorial page to the playground
- Going from a tutorial page to the homepage
- Going from the playground to the homepage
When a project change is detected, the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts) emits the `tutorialChanged` observable, which is listened in multiple sub-components and services, then each component/service performs the necessary operations to switch the project.
The following steps are executed on project change:
1. The new project files are fetched by the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts).
2. The new project files are mounted in the WebContainer filesystem.
3. The TypeScript virtual filesystem is updated with the new files and contents.
4. The previous project and new project files are compared.
1. Files that are not available in the new project are deleted from the WebContainer filesystem.
2. Files that have the same path and name have their content replaced on the previous step when the files are mounted.
5. The previous project dependencies are compared with the new project dependencies.
1. If there are differences, a `npm install` is triggered, hiding the preview and going to the install loading step.
2. If there are no differences, the project is ready.
6. Some states are resetted, for example the "reveal answer" state if the previous project was in the "reveal answer" state.
## Components and services
### [`EmbeddedEditor`](./embedded-editor.component.ts)
The embedded editor is the parent component that holds all the components and services that compose the embedded editor.
#### [`CodeEditor`](./code-editor/code-editor.component.ts)
The component that holds the code editor view and the code editor state.
##### [`CodeMirrorEditor`](./code-editor/code-mirror-editor.service.ts)
[CodeMirror](https://codemirror.net/) is the library used to handle the code editor.
The `CodeMirrorEditor` service manages the CodeMirror instance and all the interactions with the library used to handle the code editor.
- handle the file edits and the CodeMirror view and state
- handle the current project files in the code editor
- handle the file creations and deletions
- handle the file changes
- handle all the CodeMirror specific events and extensions
###### [TypeScript Web Worker](./code-editor/workers/typescript-vfs.worker.ts)
The TypeScript features are provided by the TypeScript web worker, that is initialized by the `CodeMirrorEditor` service.
The TypeScript web worker uses `@typescript/vfs` and the TypeScript language service to provide diagnostics, autocomplete and type features.
#### [`Preview`](./preview/preview.component.ts)
The preview component manages the `iframe` responsible for displaying the tutorial project preview, with the URL provided by the WebContainer API after the development server is started.
While the project is being initialized, the preview displays the loading state.
#### [`Terminal`](./terminal/terminal.component.ts)
[Xterm.js](https://xtermjs.org/) is the library used to handle the terminals.
The terminal component handles the Xterm.js instance for the console and for the interactive terminal.
##### [`InteractiveTerminal`](./terminal/interactive-terminal.ts)
The interactive terminal is the terminal where the user can interact with the terminal and run commands, supporting only commands for the Angular CLI.
##### Console
The console displays the output for `npm install` and `ng serve`.
#### [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts)
Responsible for managing the WebContainer instance and all communication with its API. This service handles:
- the WebContainer instance
- all Node.js scripts
- the WebContainer filesystem, mounting the tutorial project files, writing new content, deleting and creating files.
- the terminal session, reading and processing user inputs.
- the tutorial project dependencies, installing the dependencies.
- the processes running inside the WebContainer, being the npm scripts to install the dependencies, run the development server and the user inputs for the `ng` CLI.
##### [`NodeRuntimeState`](./node-runtime-state.service.ts)
Manages the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts) loading and error state.
#### [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts)
Manages the tutorial assets, being responsible for fetching the tutorial source code and metadata.
The source code is mounted in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
The metadata is used to manage the project, handle the project changes and the user interactivity with the app.
This service also handles the reveal answer and reset reveal answer feature.
#### [`EditorUiState`](./editor-ui-state.service.ts)
Manages the editor UI state, being responsible for handling the user interactions with the editor tabs, switching between the preview, the terminal and the console.
#### [`DownloadManager`](./download-manager.service.ts)
Responsible for handling the download button in the embedded editor, fetching the tutorial project files and generating a zip file with the project content.
#### [`AlertManager`](./alert-manager.service.ts)
Manage the alerts displayed in the embedded editor, being the out of memory alert when multiple tabs are opened, and unsupported enviroments alerts.
#### [`TypingsLoader`](./typings-loader.service.ts)
Manages the types definitions for the code editor.
File diff suppressed because one or more lines are too long
@@ -36,6 +36,7 @@
box-sizing: border-box;
transition: background 0.3s ease, border 0.3s ease;
h1,
p {
display: inline;
font-size: 0.875rem;
@@ -108,7 +108,6 @@
& > .docs-code {
box-sizing: border-box;
width: 100%;
max-height: 93vh;
overflow: hidden;
padding: 0;
@@ -119,6 +118,7 @@
button {
transition: background-color 0.3s ease;
font-family: monospace;
&.hljs-ln-line {
font-weight: 400;
@@ -154,6 +154,10 @@
}
}
.docs-reference-cli-toc {
margin-bottom: 1rem;
}
.adev-reference-tab {
min-width: 50ch;
margin-block-start: 2.5rem;
@@ -267,7 +271,9 @@
background-color: var(--octonary-contrast);
position: relative;
z-index: 10;
transition: background-color 0.3s ease, border 0.3s ease;
transition:
background-color 0.3s ease,
border 0.3s ease;
// h3 + code || # of overloads
.docs-reference-header {
@@ -404,4 +410,17 @@
margin-block-start: 0;
}
}
.docs-ref-content {
padding: 1rem 0;
&:not(:first-child) {
border-block-start: 1px solid var(--senary-contrast);
}
.docs-param-keyword {
display: block;
margin: 0 0 0.5rem 0;
}
}
}
@@ -16,12 +16,12 @@ import {
inject,
signal,
} from '@angular/core';
import {DOCUMENT, NgFor, NgIf} from '@angular/common';
import {DOCUMENT, NgFor, NgIf, ViewportScroller} from '@angular/common';
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {distinctUntilChanged, map} from 'rxjs/operators';
import {takeUntilDestroyed, toSignal} from '@angular/core/rxjs-interop';
import {distinctUntilChanged, filter, map, take} from 'rxjs/operators';
import {DocContent, DocViewer} from '@angular/docs';
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
import {ActivatedRoute, Router, RouterLink, Scroll} from '@angular/router';
import {ApiItemType} from './../interfaces/api-item-type';
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
import {
@@ -51,6 +51,15 @@ export default class ApiReferenceDetailsPage implements OnInit, AfterViewInit {
private readonly document = inject(DOCUMENT);
private readonly router = inject(Router);
private readonly scrollHandler = inject(ReferenceScrollHandler);
private readonly scroller = inject(ViewportScroller);
private scrollEvent = toSignal(
this.router.events.pipe(
filter((e): e is Scroll => e instanceof Scroll),
take(1),
),
{initialValue: null},
);
private cardsDisplayedOnInit = false;
ApiItemType = ApiItemType;
@@ -67,10 +76,22 @@ export default class ApiReferenceDetailsPage implements OnInit, AfterViewInit {
ngAfterViewInit(): void {
this.setActiveTab();
if (this.canDisplayCards()) {
this.cardsDisplayedOnInit = true;
}
this.listenToTabChange();
}
membersCardsLoaded(): void {
const event = this.scrollEvent();
if (this.cardsDisplayedOnInit && event) {
// Cards loaded after scroll event happened. We have to handle the scroll event manually
if (event.position) {
this.scroller.scrollToPosition(event.position);
} else if (event.anchor) {
this.scroller.scrollToAnchor(event.anchor);
}
}
this.scrollHandler.setupListeners(API_TAB_CLASS_NAME);
}
@@ -3,6 +3,11 @@
<h6>Getting Started</h6>
<h1 tabindex="-1">API Reference</h1>
</header>
@if (featuredGroup().items.length) {
<adev-api-items-section [group]="featuredGroup()" class="adev-featured-list" />
}
<form class="adev-reference-list-form">
<docs-text-field
name="query"
@@ -22,23 +27,20 @@
</div>
<ul class="adev-reference-list-legend">
@for (itemType of itemTypes; track itemType) {
<li
class="adev-reference-list-legend-item"
[class.adev-reference-list-legend-item-active]="type() === itemType"
(click)="filterByItemType(itemType)"
>
<docs-api-item-label [type]="itemType" mode="short" class="docs-api-item-label" />
<span>{{ itemType | adevApiLabel : 'full' }}</span>
</li>
<li
class="adev-reference-list-legend-item"
[class.adev-reference-list-legend-item-active]="type() === itemType"
(click)="filterByItemType(itemType)"
>
<docs-api-item-label [type]="itemType" mode="short" class="docs-api-item-label" />
<span class="docs-api-item-label-full">{{ itemType | adevApiLabel : 'full' }}</span>
</li>
}
</ul>
</form>
@if (featuredGroup().items.length) {
<adev-api-items-section [group]="featuredGroup()" class="adev-featured-list" />
} @for (group of filteredGroups(); track group) {
<adev-api-items-section [group]="group" />
@for (group of filteredGroups(); track group) {
<adev-api-items-section [group]="group" />
}
<span id="deprecated-description" class="docs-deprecated-description">Deprecated</span>
</div>
@@ -77,8 +77,9 @@
justify-content: flex-start;
flex-wrap: wrap-reverse;
gap: 1.5rem;
padding-block-start: 0;
padding-block-start: 1.5rem;
padding-block-end: 2rem;
border-block: 1px solid var(--senary-contrast);
}
.adev-reference-list-form-part-two {
@@ -89,13 +90,12 @@
.adev-featured-list {
display: block;
padding-block-end: 2rem;
padding-block-start: 1.5rem;
padding-block-end: 1rem;
margin-block: 1rem;
border-block: 1px solid var(--senary-contrast);
border-top: 0;
margin-top: -1.5rem;
}
.docs-deprecated-description {
display: none;
.docs-api-item-label-full {
white-space: nowrap;
}
@@ -14,7 +14,7 @@ import {ApiItemsGroup} from '../interfaces/api-items-group';
import {ApiManifest} from '../interfaces/api-manifest';
export const FEATURED_API_ITEMS_KEY = 'apiFeaturedItems';
export const FEATURED_GROUP_TITLE = 'Featured';
export const FEATURED_GROUP_TITLE = 'Most Common';
export type FeaturedItemsByGroup = Record<string, ApiItem[]>;
@@ -19,4 +19,5 @@ export enum ApiItemType {
PIPE = 'pipe',
NG_MODULE = 'ng_module',
TYPE_ALIAS = 'type_alias',
INITIALIZER_API_FUNCTION = 'initializer_api_function',
}
@@ -7,6 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {ApiItemType} from '../interfaces/api-item-type';
@Pipe({
@@ -27,6 +28,7 @@ export class ApiLabel implements PipeTransform {
[ApiItemType.PIPE]: 'P',
[ApiItemType.NG_MODULE]: 'M',
[ApiItemType.TYPE_ALIAS]: 'T',
[ApiItemType.INITIALIZER_API_FUNCTION]: 'IA',
};
private readonly fullLabelsMap: Record<ApiItemType, string> = {
@@ -42,6 +44,7 @@ export class ApiLabel implements PipeTransform {
[ApiItemType.PIPE]: 'Pipe',
[ApiItemType.NG_MODULE]: 'Module',
[ApiItemType.TYPE_ALIAS]: 'Type Alias',
[ApiItemType.INITIALIZER_API_FUNCTION]: 'Initializer API',
};
transform(value: ApiItemType, labelType: 'short' | 'full'): string {
@@ -160,9 +160,6 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
for (const line of Array.from(activeLines)) {
line.classList.remove(API_TAB_ACTIVE_CODE_LINE);
}
this.getAllMemberCards().forEach((card) => {
card.blur();
});
} else {
const lines = this.document.querySelectorAll<HTMLButtonElement>(
`button[${MEMBER_ID_ATTRIBUTE}="${currentActiveMemberId}"]`,
@@ -170,7 +167,7 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
for (const line of Array.from(lines)) {
line.classList.add(API_TAB_ACTIVE_CODE_LINE);
}
this.document.getElementById(`${currentActiveMemberId}`)?.focus();
this.document.getElementById(`${currentActiveMemberId}`)?.focus({preventScroll: true});
}
}
@@ -179,7 +176,9 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
return;
}
card.focus();
if (card !== <HTMLElement>document.activeElement) {
(<HTMLElement>document.activeElement).blur();
}
this.window.scrollTo({
top: card!.offsetTop - this.membersMarginTopInPx(),
@@ -211,8 +210,9 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
this.resizeObserver = new ResizeObserver((_) => {
this.ngZone.run(() => {
if (tabBody.offsetTop) {
this.membersMarginTopInPx.set(tabBody.offsetTop);
const offsetTop = tabBody.getBoundingClientRect().top;
if (offsetTop) {
this.membersMarginTopInPx.set(offsetTop);
}
});
});
+195 -175
View File
@@ -117,6 +117,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/components/outputs',
contentPath: 'guide/components/outputs',
},
{
label: 'output() function',
path: 'guide/components/output-fn',
contentPath: 'guide/components/output-function',
},
{
label: 'Content projection with ng-content',
path: 'guide/components/content-projection',
@@ -157,6 +162,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/components/advanced-configuration',
contentPath: 'guide/components/advanced-configuration',
},
{
label: 'Custom Elements',
path: 'guide/elements',
contentPath: 'guide/elements',
},
],
},
{
@@ -218,9 +228,39 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/templates/control-flow',
},
{
label: 'Deferrable views',
path: 'guide/defer',
contentPath: 'guide/defer',
label: 'Pipes',
children: [
{
label: 'Overview',
path: 'guide/pipes',
contentPath: 'guide/pipes/overview',
},
{
label: 'Using a pipe in a template',
path: 'guide/pipes/template',
contentPath: 'guide/pipes/template',
},
{
label: 'Custom pipes',
path: 'guide/pipes/transform-data',
contentPath: 'guide/pipes/transform-data',
},
{
label: 'Pipe precedence in expressions',
path: 'guide/pipes/precedence',
contentPath: 'guide/pipes/precedence',
},
{
label: 'Change detection with pipes',
path: 'guide/pipes/change-detection',
contentPath: 'guide/pipes/change-detection',
},
{
label: 'Unwrapping data from an observable',
path: 'guide/pipes/unwrapping-data-observables',
contentPath: 'guide/pipes/unwrapping-data-observables',
},
],
},
{
label: 'Template reference variables',
@@ -317,6 +357,16 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/signals/rxjs-interop',
contentPath: 'guide/signals/rxjs-interop',
},
{
label: 'Inputs as signals',
path: 'guide/signals/inputs',
contentPath: 'guide/signals/inputs',
},
{
label: 'Model inputs',
path: 'guide/signals/model',
contentPath: 'guide/signals/model',
},
{
label: 'Queries as signals',
path: 'guide/signals/queries',
@@ -324,71 +374,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
],
},
{
label: 'NgModule',
children: [
{
label: 'Overview',
path: 'guide/ngmodules',
contentPath: 'guide/ngmodules/overview',
},
{
label: 'JS Modules vs NgModules',
path: 'guide/ngmodules/vs-jsmodule',
contentPath: 'guide/ngmodules/vs-jsmodule',
},
{
label: 'Launching your app with a root module',
path: 'guide/ngmodules/bootstrapping',
contentPath: 'guide/ngmodules/bootstrapping',
},
{
label: 'Sharing NgModules',
path: 'guide/ngmodules/sharing',
contentPath: 'guide/ngmodules/sharing',
},
{
label: 'Frequently used NgModules',
path: 'guide/ngmodules/frequent',
contentPath: 'guide/ngmodules/frequent',
},
{
label: 'Feature modules',
path: 'guide/ngmodules/feature-modules',
contentPath: 'guide/ngmodules/feature-modules',
},
{
label: 'Types of feature modules',
path: 'guide/ngmodules/module-types',
contentPath: 'guide/ngmodules/module-types',
},
{
label: 'Providing dependencies',
path: 'guide/ngmodules/providers',
contentPath: 'guide/ngmodules/providers',
},
{
label: 'Singleton services',
path: 'guide/ngmodules/singleton-services',
contentPath: 'guide/ngmodules/singleton-services',
},
{
label: 'Lazy-loading feature modules',
path: 'guide/ngmodules/lazy-loading',
contentPath: 'guide/ngmodules/lazy-loading',
},
{
label: 'NgModule API',
path: 'guide/ngmodules/api',
contentPath: 'guide/ngmodules/api',
},
{
label: 'NgModule FAQs',
path: 'guide/ngmodules/faq',
contentPath: 'guide/ngmodules/faq',
},
],
},
{
label: 'Routing',
children: [
@@ -455,29 +440,64 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
],
},
{
label: 'Server-side Rendering',
path: 'guide/ssr',
contentPath: 'guide/ssr',
label: 'HTTP Client',
children: [
{
label: 'Overview',
path: 'guide/http',
contentPath: 'guide/http/overview',
},
{
label: 'Setting up HttpClient',
path: 'guide/http/setup',
contentPath: 'guide/http/setup',
},
{
label: 'Making requests',
path: 'guide/http/making-requests',
contentPath: 'guide/http/making-requests',
},
{
label: 'Intercepting requests and responses',
path: 'guide/http/interceptors',
contentPath: 'guide/http/interceptors',
},
{
label: 'Testing',
path: 'guide/http/testing',
contentPath: 'guide/http/testing',
},
],
},
{
label: 'Build-time prerendering',
path: 'guide/prerendering',
contentPath: 'guide/prerendering',
},
{
label: 'Hydration',
path: 'guide/hydration',
contentPath: 'guide/hydration',
},
{
label: 'Deferrable views',
path: 'guide/defer',
contentPath: 'guide/defer',
},
{
label: 'Image Optimization',
path: 'guide/image-optimization',
contentPath: 'guide/image-optimization',
label: 'Performance',
children: [
{
label: 'Deferrable views',
path: 'guide/defer',
contentPath: 'guide/defer',
},
{
label: 'Image Optimization',
path: 'guide/image-optimization',
contentPath: 'guide/image-optimization',
},
{
label: 'Server-side Rendering',
path: 'guide/ssr',
contentPath: 'guide/ssr',
},
{
label: 'Build-time prerendering',
path: 'guide/prerendering',
contentPath: 'guide/prerendering',
},
{
label: 'Hydration',
path: 'guide/hydration',
contentPath: 'guide/hydration',
},
],
},
{
label: 'Testing',
@@ -589,81 +609,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
],
},
{
label: 'Security',
path: 'guide/security',
contentPath: 'guide/security',
},
{
label: 'Pipes',
children: [
{
label: 'Overview',
path: 'guide/pipes',
contentPath: 'guide/pipes/overview',
},
{
label: 'Using a pipe in a template',
path: 'guide/pipes/template',
contentPath: 'guide/pipes/template',
},
{
label: 'Custom pipes',
path: 'guide/pipes/transform-data',
contentPath: 'guide/pipes/transform-data',
},
{
label: 'Pipe precedence in expressions',
path: 'guide/pipes/precedence',
contentPath: 'guide/pipes/precedence',
},
{
label: 'Change detection with pipes',
path: 'guide/pipes/change-detection',
contentPath: 'guide/pipes/change-detection',
},
{
label: 'Unwrapping data from an observable',
path: 'guide/pipes/unwrapping-data-observables',
contentPath: 'guide/pipes/unwrapping-data-observables',
},
],
},
{
label: 'Http Client',
children: [
{
label: 'Overview',
path: 'guide/http',
contentPath: 'guide/http/overview',
},
{
label: 'Setting up HttpClient',
path: 'guide/http/setup',
contentPath: 'guide/http/setup',
},
{
label: 'Making requests',
path: 'guide/http/making-requests',
contentPath: 'guide/http/making-requests',
},
{
label: 'Intercepting requests and responses',
path: 'guide/http/interceptors',
contentPath: 'guide/http/interceptors',
},
{
label: 'Security',
path: 'guide/http/security',
contentPath: 'guide/http/security',
},
{
label: 'Testing',
path: 'guide/http/testing',
contentPath: 'guide/http/testing',
},
],
},
{
label: 'Animations',
children: [
@@ -694,11 +639,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
],
},
{
label: 'Custom Elements',
path: 'guide/elements',
contentPath: 'guide/elements',
},
],
},
{
@@ -830,9 +770,9 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'best-practices/style-guide',
},
{
label: 'Keeping up-to-date',
path: 'update',
contentPath: 'best-practices/update',
label: 'Security',
path: 'best-practices/security',
contentPath: 'guide/security', // Have not refactored due to build issues
},
{
label: 'Accessibility',
@@ -864,6 +804,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
],
},
{
label: 'Keeping up-to-date',
path: 'update',
contentPath: 'best-practices/update',
},
],
},
{
@@ -1203,9 +1148,9 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'reference/errors/NG3003',
},
{
label: 'NG5000: Hydration with unsupported Zone.js instance.',
path: 'errors/NG5000',
contentPath: 'reference/errors/NG5000',
label: 'NG05000: Hydration with unsupported Zone.js instance.',
path: 'errors/NG05000',
contentPath: 'reference/errors/NG05000',
},
{
label: 'NG6100: NgModule.id Set to module.id anti-pattern',
@@ -1349,6 +1294,81 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
],
},
{
label: 'Concepts',
children: [
{
label: 'Overview',
path: 'reference/concepts',
contentPath: 'reference/concepts/overview',
},
{
label: 'NgModule',
children: [
{
label: 'Overview',
path: 'guide/ngmodules',
contentPath: 'guide/ngmodules/overview',
},
{
label: 'JS Modules vs NgModules',
path: 'guide/ngmodules/vs-jsmodule',
contentPath: 'guide/ngmodules/vs-jsmodule',
},
{
label: 'Launching your app with a root module',
path: 'guide/ngmodules/bootstrapping',
contentPath: 'guide/ngmodules/bootstrapping',
},
{
label: 'Sharing NgModules',
path: 'guide/ngmodules/sharing',
contentPath: 'guide/ngmodules/sharing',
},
{
label: 'Frequently used NgModules',
path: 'guide/ngmodules/frequent',
contentPath: 'guide/ngmodules/frequent',
},
{
label: 'Feature modules',
path: 'guide/ngmodules/feature-modules',
contentPath: 'guide/ngmodules/feature-modules',
},
{
label: 'Types of feature modules',
path: 'guide/ngmodules/module-types',
contentPath: 'guide/ngmodules/module-types',
},
{
label: 'Providing dependencies',
path: 'guide/ngmodules/providers',
contentPath: 'guide/ngmodules/providers',
},
{
label: 'Singleton services',
path: 'guide/ngmodules/singleton-services',
contentPath: 'guide/ngmodules/singleton-services',
},
{
label: 'Lazy-loading feature modules',
path: 'guide/ngmodules/lazy-loading',
contentPath: 'guide/ngmodules/lazy-loading',
},
{
label: 'NgModule API',
path: 'guide/ngmodules/api',
contentPath: 'guide/ngmodules/api',
},
{
label: 'NgModule FAQs',
path: 'guide/ngmodules/faq',
contentPath: 'guide/ngmodules/faq',
},
],
},
],
},
];
const FOOTER_NAVIGATION_DATA: NavigationItem[] = [
+1
View File
@@ -27,6 +27,7 @@ copy_to_directory(
"//adev/src/content/introduction",
"//adev/src/content/introduction/essentials",
"//adev/src/content/reference",
"//adev/src/content/reference/concepts",
"//adev/src/content/reference/configs",
"//adev/src/content/reference/errors",
"//adev/src/content/reference/extended-diagnostics",

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before

Width:  |  Height:  |  Size: 724 KiB

After

Width:  |  Height:  |  Size: 724 KiB

Before

Width:  |  Height:  |  Size: 745 KiB

After

Width:  |  Height:  |  Size: 745 KiB

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

+6 -6
View File
@@ -17,16 +17,16 @@ Use attribute binding template syntax to control the values of accessibility-rel
When binding to ARIA attributes in Angular, you must use the `attr.` prefix. The ARIA specification depends specifically on HTML attributes rather than properties of DOM elements.
<docs-code language="html">
&lt;!-- Use attr. when binding to an ARIA attribute --&gt;
&lt;button [attr.aria-label]="myActionLabel"&gt;&hellip;&lt;/button&gt;
<!-- Use attr. when binding to an ARIA attribute -->
<button [attr.aria-label]="myActionLabel">…</button>
</docs-code>
Note: This syntax is only necessary for attribute *bindings*.
Static ARIA attributes require no extra syntax.
<docs-code language="html">
&lt;!-- Static ARIA attributes require no extra syntax --&gt;
&lt;button aria-label="Save document"&gt;&hellip;&lt;/button&gt;
<!-- Static ARIA attributes require no extra syntax -->
<button aria-label="Save document">…</button>
</docs-code>
HELPFUL: By convention, HTML attributes use lowercase names \(`tabindex`\), while properties use camelCase names \(`tabIndex`\).
@@ -103,8 +103,8 @@ The following example shows how to find and focus the main content header in the
<docs-code language="typescript">
router.events.pipe(filter(e =&gt; e instanceof NavigationEnd)).subscribe(() =&gt; {
const mainHeader = document.querySelector('&num;main-content-header')
router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => {
const mainHeader = document.querySelector('#main-content-header')
if (mainHeader) {
mainHeader.focus();
}
@@ -59,8 +59,6 @@ If Angular handles an event within a component with OnPush strategy, the framewo
As an example, if Angular handles an event within `MainComponent`, the framework will run change detection in the entire component tree. Angular will ignore the subtree with root `LoginComponent` because it has `OnPush` and the event happened outside of its scope.
<img alt="Change detection propagation from OnPush component" src="assets/images/best-practices/runtime-performance/on-push-trigger.svg">
```mermaid
graph TD;
app[AppComponent] --- header[HeaderComponent];
+38 -38
View File
@@ -151,14 +151,14 @@ Consistent conventions make it easy to quickly identify and reference assets of
| Symbol name | File name |
|:--- |:--- |
| <docs-code hideCopy language="typescript"> &commat;Component({ &hellip; }) &NewLine;export class AppComponent { } </docs-code> | app.component.ts |
| <docs-code hideCopy language="typescript"> &commat;Component({ &hellip; }) &NewLine;export class HeroesComponent { } </docs-code> | heroes.component.ts |
| <docs-code hideCopy language="typescript"> &commat;Component({ &hellip; }) &NewLine;export class HeroListComponent { } </docs-code> | hero-list.component.ts |
| <docs-code hideCopy language="typescript"> &commat;Component({ &hellip; }) &NewLine;export class HeroDetailComponent { } </docs-code> | hero-detail.component.ts |
| <docs-code hideCopy language="typescript"> &commat;Directive({ &hellip; }) &NewLine;export class ValidationDirective { } </docs-code> | validation.directive.ts |
| <docs-code hideCopy language="typescript"> &commat;NgModule({ &hellip; }) &NewLine;export class AppModule </docs-code> | app.module.ts |
| <docs-code hideCopy language="typescript"> &commat;Pipe({ name: 'initCaps' }) &NewLine;export class InitCapsPipe implements PipeTransform { } </docs-code> | init-caps.pipe.ts |
| <docs-code hideCopy language="typescript"> &commat;Injectable() &NewLine;export class UserProfileService { } </docs-code> | user-profile.service.ts |
| <docs-code hideCopy language="typescript"> @Component({ … }) <br>export class AppComponent { } </docs-code> | app.component.ts |
| <docs-code hideCopy language="typescript"> @Component({ … }) <br>export class HeroesComponent { } </docs-code> | heroes.component.ts |
| <docs-code hideCopy language="typescript"> @Component({ … }) <br>export class HeroListComponent { } </docs-code> | hero-list.component.ts |
| <docs-code hideCopy language="typescript"> @Component({ … }) <br>export class HeroDetailComponent { } </docs-code> | hero-detail.component.ts |
| <docs-code hideCopy language="typescript"> @Directive({ … }) <br>export class ValidationDirective { } </docs-code> | validation.directive.ts |
| <docs-code hideCopy language="typescript"> @NgModule({ … }) <br>export class AppModule </docs-code> | app.module.ts |
| <docs-code hideCopy language="typescript"> @Pipe({ name: 'initCaps' }) <br>export class InitCapsPipe implements PipeTransform { } </docs-code> | init-caps.pipe.ts |
| <docs-code hideCopy language="typescript"> @Injectable() <br>export class UserProfileService { } </docs-code> | user-profile.service.ts |
### Service names
@@ -186,9 +186,9 @@ Service names such as `Credit` are nouns and require a suffix and should be name
| Symbol name | File name |
|:--- |:--- |
| <docs-code hideCopy language="typescript"> &commat;Injectable() &NewLine;export class HeroDataService { } </docs-code> | hero-data.service.ts |
| <docs-code hideCopy language="typescript"> &commat;Injectable() &NewLine;export class CreditService { } </docs-code> | credit.service.ts |
| <docs-code hideCopy language="typescript"> &commat;Injectable() &NewLine;export class Logger { } </docs-code> | logger.service.ts |
| <docs-code hideCopy language="typescript"> @Injectable() <br>export class HeroDataService { } </docs-code> | hero-data.service.ts |
| <docs-code hideCopy language="typescript"> @Injectable() <br>export class CreditService { } </docs-code> | credit.service.ts |
| <docs-code hideCopy language="typescript"> @Injectable() <br>export class Logger { } </docs-code> | logger.service.ts |
### Bootstrapping
@@ -293,8 +293,8 @@ Provides a consistent way to quickly identify and reference pipes.
| Symbol name | File name |
|:--- |:--- |
| <docs-code hideCopy language="typescript"> &commat;Pipe({ standalone: true, name: 'ellipsis' }) &NewLine;export class EllipsisPipe implements PipeTransform { } </docs-code> | ellipsis.pipe.ts |
| <docs-code hideCopy language="typescript"> &commat;Pipe({ standalone: true, name: 'initCaps' }) &NewLine;export class InitCapsPipe implements PipeTransform { } </docs-code> | init-caps.pipe.ts |
| <docs-code hideCopy language="typescript"> @Pipe({ standalone: true, name: 'ellipsis' }) <br>export class EllipsisPipe implements PipeTransform { } </docs-code> | ellipsis.pipe.ts |
| <docs-code hideCopy language="typescript"> @Pipe({ standalone: true, name: 'initCaps' }) <br>export class InitCapsPipe implements PipeTransform { } </docs-code> | init-caps.pipe.ts |
### Unit test file names
@@ -354,40 +354,40 @@ project root
├── src
│ ├── app
│ │ ├── core
│ │ │ └── exception.service.ts&verbar;spec.ts
│ │ │ └── user-profile.service.ts&verbar;spec.ts
│ │ │ └── exception.service.ts|spec.ts
│ │ │ └── user-profile.service.ts|spec.ts
│ │ ├── heroes
│ │ │ ├── hero
│ │ │ │ └── hero.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ │ │ └── hero.component.ts|html|css|spec.ts
│ │ │ ├── hero-list
│ │ │ │ └── hero-list.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ │ │ └── hero-list.component.ts|html|css|spec.ts
│ │ │ ├── shared
│ │ │ │ └── hero-button.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ │ │ └── hero-button.component.ts|html|css|spec.ts
│ │ │ │ └── hero.model.ts
│ │ │ │ └── hero.service.ts&verbar;spec.ts
│ │ │ └── heroes.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ │ │ └── hero.service.ts|spec.ts
│ │ │ └── heroes.component.ts|html|css|spec.ts
│ │ │ └── heroes.routes.ts
│ │ ├── shared
│ │ │ └── init-caps.pipe.ts&verbar;spec.ts
│ │ │ └── filter-text.component.ts&verbar;spec.ts
│ │ │ └── filter-text.service.ts&verbar;spec.ts
│ │ │ └── init-caps.pipe.ts|spec.ts
│ │ │ └── filter-text.component.ts|spec.ts
│ │ │ └── filter-text.service.ts|spec.ts
│ │ ├── villains
│ │ │ ├── villain
│ │ │ │ └── &hellip;
│ │ │ │ └── …
│ │ │ ├── villain-list
│ │ │ │ └── &hellip;
│ │ │ │ └── …
│ │ │ ├── shared
│ │ │ │ └── &hellip;
│ │ │ └── villains.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ │ │ └── …
│ │ │ └── villains.component.ts|html|css|spec.ts
│ │ │ └── villains.module.ts
│ │ │ └── villains-routing.module.ts
│ │ └── app.component.ts&verbar;html&verbar;css&verbar;spec.ts
│ │ └── app.component.ts|html|css|spec.ts
│ │ └── app.routes.ts
│ └── main.ts
│ └── index.html
│ └── &hellip;
└── node_modules/&hellip;
└── &hellip;
│ └── …
└── node_modules/…
└── …
```
HELPFUL: While components in dedicated folders are widely preferred, another option for small applications is to keep components flat \(not in a dedicated folder\).
@@ -506,15 +506,15 @@ project root
├──├──app
├──├──├── shared
├──├──├──└── shared.module.ts
├──├──├──└── init-caps.pipe.ts&verbar;spec.ts
├──├──├──└── filter-text.component.ts&verbar;spec.ts
├──├──├──└── filter-text.service.ts&verbar;spec.ts
├──├──└── app.component.ts&verbar;html&verbar;css&verbar;spec.ts
├──├──├──└── init-caps.pipe.ts|spec.ts
├──├──├──└── filter-text.component.ts|spec.ts
├──├──├──└── filter-text.service.ts|spec.ts
├──├──└── app.component.ts|html|css|spec.ts
├──├──└── app.module.ts
├──├──└── app-routing.module.ts
├──└── main.ts
├──└── index.html
└── &hellip;
└── …
```
<docs-code-multifile>
@@ -761,7 +761,7 @@ An element may have more than one attribute directive applied.
**Do** be consistent in your choice.
**Why**? <br />
The property associated with `@HostBinding` or the method associated with `@HostListener` can be modified only in a single place &mdash;in the directive's class.
The property associated with `@HostBinding` or the method associated with `@HostListener` can be modified only in a single place —in the directive's class.
If you use the `host` metadata property, you must modify both the property/method declaration in the directive's class and the metadata in the decorator associated with the directive.
<docs-code header="app/shared/validator.directive.ts" path="adev/src/content/examples/styleguide/src/06-03/app/shared/validator.directive.ts"/>
@@ -811,7 +811,7 @@ In this scenario it would be better to provide the service at the component leve
<docs-code header="src/app/treeshaking/service.ts" path="adev/src/content/examples/dependency-injection/src/app/tree-shaking/service.ts"/>
### Use the &commat;Injectable() class decorator
### Use the @Injectable() class decorator
#### Style 07-04
+1 -1
View File
@@ -6,7 +6,7 @@ Keeping your Angular application up-to-date enables you to take advantage of lea
This document contains information and resources to help you keep your Angular applications and libraries up-to-date.
For information about our versioning policy and practices &mdash;including support and deprecation practices, as well as the release schedule&mdash; see [Angular versioning and releases](reference/releases "Angular versioning and releases").
For information about our versioning policy and practices —including support and deprecation practices, as well as the release schedule— see [Angular versioning and releases](reference/releases "Angular versioning and releases").
HELPFUL: If you are currently using AngularJS, see [Upgrading from AngularJS](https://angular.io/guide/upgrade "Upgrading from Angular JS").
*AngularJS* is the name for all v1.x versions of Angular.
@@ -5,6 +5,8 @@ It can improve the user experience by quickly launching a static rendered page (
This gives users a meaningful first paint of your application that appears quickly because the browser can render the HTML and CSS without the need to initialize any JavaScript.
Learn more in [The App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell).
<docs-workflow>
<docs-step title="Prepare the application">
Do this with the following Angular CLI command:
@@ -15,7 +17,7 @@ ng new my-app --routing
</docs-code>
For an existing application, you have to manually add the `RouterModule` and defining a `<router-outlet>` within your application.
For an existing application, you have to manually add the `Router` and defining a `<router-outlet>` within your application.
</docs-step>
<docs-step title="Create the application shell">
Use the Angular CLI to automatically create the application shell.
@@ -28,57 +30,24 @@ ng generate app-shell
For more information about this command, see [App shell command](cli/generate#app-shell-command).
After running this command you can see that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
The command updates the application code and adds extra files to the project structure.
<docs-code language="json">
<code-example language="text">
src
├── app
│ ├── app.config.server.ts # server application configuration
│ └── app-shell # app-shell component
│ ├── app-shell.component.html
│ ├── app-shell.component.scss
│ ├── app-shell.component.spec.ts
│ └── app-shell.component.ts
└── main.server.ts # main server application bootstrapping
</code-example>
"server": {
"builder": "&commat;angular-devkit/build-angular:server",
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/my-app/server",
"main": "src/main.server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"development": {
"outputHashing": "none",
},
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"sourceMap": false,
"optimization": true
}
}
},
"app-shell": {
"builder": "&commat;angular-devkit/build-angular:app-shell",
"defaultConfiguration": "production",
"options": {
"route": "shell"
},
"configurations": {
"development": {
"browserTarget": "my-app:build:development",
"serverTarget": "my-app:server:development",
},
"production": {
"browserTarget": "my-app:build:production",
"serverTarget": "my-app:server:production"
}
}
}
</docs-code>
</docs-step>
<docs-step title="Verify the application is built with the shell content">
Use the Angular CLI to build the `app-shell` target.
<docs-code language="shell">
@@ -28,7 +28,7 @@ The `versionUpdates` is an `Observable` property of `SwUpdate` and emits four ev
### Checking for updates
It's possible to ask the service worker to check if any updates have been deployed to the server.
The service worker checks for updates during initialization and on each navigation request &mdash;that is, when the user navigates from a different address to your application.
The service worker checks for updates during initialization and on each navigation request —that is, when the user navigates from a different address to your application.
However, you might choose to manually check for updates if you have a site that changes frequently or want updates to happen on a schedule.
Do this with the `checkForUpdate()` method:
@@ -7,7 +7,7 @@ This topic describes the properties of the service worker configuration file.
The `ngsw-config.json` JSON configuration file specifies which files and data URLs the Angular service worker should cache and how it should update the cached files and data.
The [Angular CLI](tools/cli) processes this configuration file during `ng build`.
All file paths must begin with `/`, which corresponds to the deployment directory &mdash; usually `dist/<project-name>` in CLI projects.
All file paths must begin with `/`, which corresponds to the deployment directory — usually `dist/<project-name>` in CLI projects.
Unless otherwise commented, patterns use a **limited*** glob format that internally will be converted into regex:
@@ -68,10 +68,10 @@ This field contains an array of asset groups, each of which defines a set of ass
{
"assetGroups": [
{
&hellip;
…
},
{
&hellip;
…
}
]
}
@@ -169,10 +169,10 @@ This field contains an array of data groups, each of which defines a set of data
{
"dataGroups": [
{
&hellip;
…
},
{
&hellip;
…
}
]
}
@@ -197,7 +197,7 @@ export interface DataGroup {
maxSize: number;
maxAge: string;
timeout?: string;
strategy?: 'freshness' &verbar; 'performance';
strategy?: 'freshness' | 'performance';
};
cacheQueryOptions?: {
ignoreSearch?: boolean;
@@ -226,7 +226,7 @@ Only non-mutating requests (GET and HEAD) are cached.
Occasionally APIs change formats in a way that is not backward-compatible.
A new version of the application might not be compatible with the old API format and thus might not be compatible with existing cached resources from that API.
`version` provides a mechanism to indicate that the resources being cached have been updated in a backwards-incompatible way, and that the old cache entries &mdash;those from previous versions&mdash; should be discarded.
`version` provides a mechanism to indicate that the resources being cached have been updated in a backwards-incompatible way, and that the old cache entries —those from previous versions— should be discarded.
`version` is an integer field and defaults to `1`.
@@ -306,7 +306,7 @@ In case you are not familiar, an [opaque response][https://fetch.spec.whatwg.org
One of the characteristics of an opaque response is that the service worker is not allowed to read its status, meaning it can't check if the request was successful or not.
See [Introduction to fetch()][https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types] for more details.
If you are not able to implement CORS &mdash; for example, if you don't control the origin &mdash; prefer using the `freshness` strategy for resources that result in opaque responses.
If you are not able to implement CORS — for example, if you don't control the origin — prefer using the `freshness` strategy for resources that result in opaque responses.
</docs-callout>
@@ -155,7 +155,7 @@ Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100
Last update tick: 1s496u
Last update run: never
Task queue:
&ast; init post-load (update, cleanup)
* init post-load (update, cleanup)
Debug log:
@@ -232,7 +232,7 @@ HELPFUL: This version hash is the "latest manifest hash" listed above. Both clie
Last update tick: 1s496u
Last update run: never
Task queue:
&ast; init post-load (update, cleanup)
* init post-load (update, cleanup)
</docs-code>
@@ -54,7 +54,7 @@ To simulate a network issue, disable network interaction for your application.
In Chrome:
1. Select **Tools** &gt; **Developer Tools** (from the Chrome menu located in the top right corner).
1. Select **Tools** > **Developer Tools** (from the Chrome menu located in the top right corner).
1. Go to the **Network tab**.
1. Select **Offline** in the **Throttling** dropdown menu.
@@ -110,7 +110,7 @@ Make a change to the application, and watch the service worker install the updat
<docs-code language="shell">
ng build
npx http-server -p 8080 -c-1 dist/&lt;project-name&gt;/browser
npx http-server -p 8080 -c-1 dist/<project-name>/browser
</docs-code>
@@ -14,7 +14,7 @@ Invoke push notifications by pushing a message with a valid payload.
See `SwPush` for guidance.
HELPFUL: In Chrome, you can test push notifications without a backend.
Open Devtools -&gt; Application -&gt; Service Workers and use the `Push` input to send a JSON notification payload.
Open Devtools -> Application -> Service Workers and use the `Push` input to send a JSON notification payload.
## Notification click handling
+5 -5
View File
@@ -11,7 +11,7 @@ To add a web worker to an existing project, use the Angular CLI `ng generate` co
<docs-code language="shell">
ng generate web-worker &lt;location&gt;
ng generate web-worker <location>
</docs-code>
@@ -31,8 +31,8 @@ The command performs the following actions.
<docs-code language="typescript" header="src/app/app.worker.ts">
addEventListener('message', ({ data }) =&gt; {
const response = `worker response to &dollar;{data}`;
addEventListener('message', ({ data }) => {
const response = `worker response to ${data}`;
postMessage(response);
});
@@ -45,8 +45,8 @@ The command performs the following actions.
if (typeof Worker !== 'undefined') {
// Create a new
const worker = new Worker(new URL('./app.worker', import.meta.url));
worker.onmessage = ({ data }) =&gt; {
console.log(`page got message: &dollar;{data}`);
worker.onmessage = ({ data }) => {
console.log(`page got message: ${data}`);
};
worker.postMessage('hello');
} else {
@@ -1,5 +1,5 @@
load("//aio/content/examples:examples.bzl", "docs_example")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
package(default_visibility = ["//visibility:public"])
@@ -6,32 +6,28 @@
<blockquote>
<!-- #docregion asterisk -->
@if (hero) {<div class="name">{{ hero.name }}</div>}
<div *ngIf="hero" class="name">{{hero.name}}</div>
<!-- #enddocregion asterisk -->
</blockquote>
<p>List of heroes</p>
<ul>
@for (hero of heroes; track hero) {<li>{{ hero.name }}</li>}
<li *ngFor="let hero of heroes">{{hero.name}}</li>
</ul>
<hr>
<h2 id="ngIf">NgIf</h2>
@if (true) {
<p>
Expression is true and ngIf is true.
This paragraph is in the DOM.
</p>
}
@if (false) {
<p>
Expression is false and ngIf is false.
This paragraph is not in the DOM.
</p>
}
<p *ngIf="true">
Expression is true and ngIf is true.
This paragraph is in the DOM.
</p>
<p *ngIf="false">
Expression is false and ngIf is false.
This paragraph is not in the DOM.
</p>
<p [style.display]="'block'">
Expression sets display to "block".
@@ -46,7 +42,7 @@
<p>&lt;ng-template&gt; element</p>
<!-- #docregion ngif-template -->
<ng-template [ngIf]="hero">
<div class="name">{{ hero.name }}</div>
<div class="name">{{hero.name}}</div>
</ng-template>
<!-- #enddocregion ngif-template -->
@@ -61,18 +57,18 @@
<!-- #docregion ngif-ngcontainer -->
<p>
I turned the corner
@if (hero) {
and saw {{ hero.name }}. I waved
}
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
and continued on my way.
</p>
<!-- #enddocregion ngif-ngcontainer -->
<p>
I turned the corner
@if (hero) {
<span>and saw {{ hero.name }}. I waved</span>
}
<span *ngIf="hero">
and saw {{hero.name}}. I waved
</span>
and continued on my way.
</p>
@@ -83,15 +79,11 @@
(<label for="show-sad"><input id="show-sad" type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
@for (h of heroes; track h) {
<span>
@if (showSad || h.emotion !== 'sad') {
<span>
<option [ngValue]="h">{{ h.name }} ({{ h.emotion }})</option>
</span>
}
<span *ngFor="let h of heroes">
<span *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</span>
}
</span>
</select>
<p><em>&lt;select&gt; with &lt;ng-container&gt;</em></p>
@@ -101,11 +93,11 @@
(<label for="showSad"><input id="showSad" type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
@for (h of heroes; track h) {
@if (showSad || h.emotion !== 'sad') {
<option [ngValue]="h">{{ h.name }} ({{ h.emotion }})</option>
}
}
<ng-container *ngFor="let h of heroes">
<ng-container *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</ng-container>
</ng-container>
</select>
<!-- #enddocregion select-ngcontainer -->
<br><br>
@@ -118,12 +110,11 @@
<p class="code">&lt;div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"&gt;</p>
<!--#docregion inside-ngfor -->
@for (hero of heroes; track hero; let odd = $odd; let i = $index) {
<div
[class.odd]="odd">
({{ i }}) {{ hero.name }}
</div>
}
<div
*ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById"
[class.odd]="odd">
({{i}}) {{hero.name}}
</div>
<!--#enddocregion inside-ngfor -->
<p class="code">&lt;ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/&gt;</p>
@@ -131,7 +122,7 @@
<ng-template ngFor let-hero [ngForOf]="heroes"
let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
<div [class.odd]="odd">
({{ i }}) {{ hero.name }}
({{i}}) {{hero.name}}
</div>
</ng-template>
<!--#enddocregion inside-ngfor -->
@@ -143,46 +134,35 @@
<div>Pick your favorite hero</div>
<p>
@for (h of heroes; track h) {
<label for="hero-{{ h }}">
<input id="hero-{{ h }}" type="radio" name="heroes" [(ngModel)]="hero" [value]="h">{{ h.name }}
</label>
}
<label for="hero-{{h}}" *ngFor="let h of heroes">
<input id="hero-{{h}}" type="radio" name="heroes" [(ngModel)]="hero" [value]="h">{{h.name}}
</label>
<label for="none-of-the-above"><input id="none-of-the-above" type="radio" name="heroes" (click)="hero = null">None of the above</label>
</p>
<h4>NgSwitch</h4>
<div>
@switch (hero?.emotion) {
@case ('happy') {
<app-happy-hero [hero]="hero!"></app-happy-hero>
} @case ('sad') {
<app-sad-hero [hero]="hero!"></app-sad-hero>
} @case ('confused') {
<app-confused-hero [hero]="hero!"></app-confused-hero>
} @default {
<app-unknown-hero [hero]="hero!"></app-unknown-hero>
}
}
<div [ngSwitch]="hero?.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero!"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero!"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="hero!"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="hero!"></app-unknown-hero>
</div>
<h4>NgSwitch with &lt;ng-template&gt;</h4>
<div>
@switch (hero?.emotion) {
@case ('happy') {
<app-happy-hero [hero]="hero!"></app-happy-hero>
}
@case ('sad') {
<app-sad-hero [hero]="hero!"></app-sad-hero>
}
@case ('confused') {
<app-confused-hero [hero]="hero!"></app-confused-hero>
}
@default {
<app-unknown-hero [hero]="hero!"></app-unknown-hero>
}
}
<div [ngSwitch]="hero?.emotion">
<ng-template ngSwitchCase="happy">
<app-happy-hero [hero]="hero!"></app-happy-hero>
</ng-template>
<ng-template ngSwitchCase="sad">
<app-sad-hero [hero]="hero!"></app-sad-hero>
</ng-template>
<ng-template ngSwitchCase="confused">
<app-confused-hero [hero]="hero!"></app-confused-hero>
</ng-template >
<ng-template ngSwitchDefault>
<app-unknown-hero [hero]="hero!"></app-unknown-hero>
</ng-template>
</div>
<hr>
@@ -192,12 +172,12 @@
<!-- #docregion toggle-info -->
<p>
The condition is currently
<span [ngClass]="{ 'a': !condition, 'b': condition, 'unless': true }">{{ condition }}</span>.
<span [ngClass]="{ 'a': !condition, 'b': condition, 'unless': true }">{{condition}}</span>.
<button
type="button"
(click)="condition = !condition"
[ngClass]="{ 'a': condition, 'b': !condition }" >
Toggle condition to {{ condition ? 'false' : 'true' }}
[ngClass] = "{ 'a': condition, 'b': !condition }" >
Toggle condition to {{condition ? 'false' : 'true'}}
</button>
</p>
<!-- #enddocregion toggle-info -->
@@ -1,5 +1,5 @@
load("//aio/content/examples:examples.bzl", "docs_example")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
package(default_visibility = ["//visibility:public"])
@@ -1,5 +1,5 @@
load("//aio/content/examples:examples.bzl", "docs_example")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
package(default_visibility = ["//visibility:public"])
@@ -1,5 +1,5 @@
load("//aio/content/examples:examples.bzl", "docs_example")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
package(default_visibility = ["//visibility:public"])
@@ -196,7 +196,7 @@ HELPFUL: Some additional notes on using styles within [`state`](api/animations/s
<docs-code language="typescript">
transition( 'on =&gt; off, off =&gt; void' )
transition( 'on => off, off => void' )
</docs-code>
@@ -227,7 +227,7 @@ Then, you can bind the trigger to a template expression using standard Angular p
<docs-code language="typescript">
&lt;div [&commat;triggerName]="expression"&gt;&hellip;&lt;/div&gt;;
<div [@triggerName]="expression">…</div>;
</docs-code>
@@ -266,7 +266,7 @@ Learn about more advanced features in Angular animations under the Animation sec
## Animations API summary
The functional API provided by the `@angular/animations` module provides a domain-specific language \(DSL\) for creating and controlling animations in Angular applications.
See the [API reference](api/animations) for a complete listing and syntax details of the core functions and related data structures.
See the [API reference](api#animations) for a complete listing and syntax details of the core functions and related data structures.
| Function name | What it does |
|:--- |:--- |
@@ -113,7 +113,7 @@ Adding these styles to the views animates the containers in place and prevents o
Use the `query()` method to find and animate elements within the current host component.
The `query(":enter")` statement returns the view that is being inserted, and `query(":leave")` returns the view that is being removed.
Assume that you are routing from the *Home =&gt; About*.
Assume that you are routing from the *Home => About*.
<docs-code header="src/app/animations.ts (excerpt)" path="adev/src/content/examples/animations/src/app/animations.ts" visibleRegion="query"/>
@@ -85,8 +85,8 @@ These aliases are used by several animation functions.
<docs-code hideCopy language="typescript">
transition ( ':enter', [ &hellip; ] ); // alias for void =&gt; &ast;
transition ( ':leave', [ &hellip; ] ); // alias for * =&gt; void
transition ( ':enter', [ … ] ); // alias for void => *
transition ( ':leave', [ … ] ); // alias for * => void
</docs-code>
@@ -0,0 +1,109 @@
# Function-based outputs
The `output()` function declares an output in a directive or component.
Outputs allow you to emit values to parent components.
HELPFUL: The `output()` function is currently in [developer preview](/guide/releases#developer-preview).
<docs-code language="ts" highlight="[[5], [8]]">
import {Component, output} from '@angular/core';
@Component({...})
export class MyComp {
onNameChange = output<string>() // OutputEmitterRef<string>
setNewName(newName: string) {
this.onNameChange.emit(newName);
}
}
</docs-code>
An output is automatically recognized by Angular whenever you use the `output` function as an initializer of a class member.
Parent components can listen to outputs in templates by using the event binding syntax.
```html
<my-comp (onNameChange)="showNewName($event)" />
```
## Aliasing an output
Angular uses the class member name as the name of the output.
You can alias outputs to change their public name to be different.
```typescript
class MyComp {
onNameChange = output({alias: 'ngxNameChange'});
}
```
This allows users to bind to your output using `(ngxNameChange)`, while inside your component you can access the output emitter using `this.onNameChange`.
## Subscribing programmatically
Consumers may create your component dynamically with a reference to a `ComponentRef`.
In those cases, parents can subscribe to outputs by directly accessing the property of type `OutputRef`.
```ts
const myComp = viewContainerRef.createComponent(...);
myComp.instance.onNameChange.subscribe(newName => {
console.log(newName);
});
```
Angular will automatically clean up the subscription when `myComp` is destroyed.
Alternatively, an object with a function to explicitly unsubscribe earlier is returned.
## Using RxJS observables as source
In some cases, you may want to emit output values based on RxJS observables.
Angular provides a way to use RxJS observables as source for outputs.
The `outputFromObservable` function is a compiler primitive, similar to the `output()` function, and declares outputs that are driven by RxJS observables.
<docs-code language="ts" highlight="[7]">
import {Directive} from '@angular/core';
import {outputFromObservable} from '@angular/core/rxjs-interop';
@Directive(...)
class MyDir {
nameChange$ = this.dataService.get(); // Observable<Data>
nameChange = outputFromObservable(this.nameChange$);
}
</docs-code>
Angular will forward subscriptions to the observable, but will stop forwarding values when the owning directive is destroyed.
In the example above, if `MyDir` is destroyed, `nameChange` will no longer emit values.
HELPFUL: Most of the time, using `output()` is sufficient and you can emit values imperatively.
## Converting an output to an observable
You can subscribe to outputs by calling `.subscribe` method on `OutputRef`.
In other cases, Angular provides a helper function that converts an `OutputRef` to an observable.
<docs-code language="ts" highlight="[11]">
import {outputToObservable} from '@angular/core/rxjs-interop';
@Component(...)
class MyComp {
onNameChange = output<string>();
}
// Instance reference to `MyComp`.
const myComp: MyComp;
outputToObservable(this.myComp.instance.onNameChange) // Observable<string>
.pipe(...)
.subscribe(...);
</docs-code>
## Why you should use `output()` over decorator-based `@Output()`?
The `output()` function provides numerous benefits over decorator-based `@Output` and `EventEmitter`:
1. Simpler mental model and API:
<br/>• No concept of error channel, completion channels, or other APIs from RxJS.
<br/>• Outputs are simple emitters. You can emit values using the `.emit` function.
2. More accurate types.
<br/>• `OutputEmitterRef.emit(value)` is now correctly typed, while `EventEmitter` has broken types and can cause runtime errors.
+3 -1
View File
@@ -56,7 +56,9 @@ Note: Certain triggers may require the presence of either a `@placeholder` or a
### `@loading`
The `@loading` block is an optional block that allows you to declare content that will be shown during the loading of any deferred dependencies. For example, you could show a loading spinner. Similar to `@placeholder`, the dependencies of the `@loading` block are eagerly loaded.
The `@loading` block is an optional block that allows you to declare content that will be shown during the loading of any deferred dependencies. Its dependences are eagerly loaded (similar to `@placeholder`).
For example, you could show a loading spinner. Once loading has been triggered, the `@loading` block replaces the `@placeholder` block.
The `@loading` block accepts two optional parameters to specify the `minimum` amount of time that this placeholder should be shown and amount of time to wait `after` loading begins before showing the loading template. `minimum` and `after` parameters are specified in time increments of milliseconds (ms) or seconds (s). Just like `@placeholder`, these parameters exist to prevent fast flickering of content in the case that the deferred dependencies are fetched quickly. Both the `minimum` and `after` timers for the `@loading` block begins immediately after the loading has been triggered.
@@ -1,21 +1,21 @@
# Injection context
The dependency injection (DI) system relies internally on a runtime context where the current injector is available.
This means that injectors can only work when code is executed in this context.
This means that injectors can only work when code is executed in such a context.
The injection context is available in these situations:
* Construction (via the `constructor`) of a class being instantiated by the DI system, such as an `@Injectable` or `@Component`.
* During construction (via the `constructor`) of a class being instantiated by the DI system, such as an `@Injectable` or `@Component`.
* In the initializer for fields of such classes.
* In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
* In the `factory` function specified for an `InjectionToken`.
* Within a stack frame that is run in a injection context.
* Within a stack frame that runs in an injection context.
Knowing when you are in an injection context, will allow you to use the [`inject`](api/core/inject) function to inject instances.
Knowing when you are in an injection context will allow you to use the [`inject`](api/core/inject) function to inject instances.
## Class constructors
Everytime the DI system instantiates a class, this is done in an injection context. This is being handled by the framework itself. The constructor of the class is executed in that runtime context thus allowing to inject a token using the [`inject`](api/core/inject) function.
Every time the DI system instantiates a class, it does so in an injection context. This is handled by the framework itself. The constructor of the class is executed in that runtime context, which also allows injection of a token using the [`inject`](api/core/inject) function.
<docs-code language="typescript" highlight="[[3],[6]]">
class MyComponent {
@@ -30,7 +30,7 @@ class MyComponent {
## Stack frame in context
Some APIs are designed to be run in an injection context. This is the case, for example, of the router guards. It allows the use of [`inject`](api/core/inject) to access a service within the guard function.
Some APIs are designed to be run in an injection context. This is the case, for example, with router guards. This allows the use of [`inject`](api/core/inject) within the guard function to access a service.
Here is an example for `CanActivateFn`
@@ -43,8 +43,8 @@ const canActivateTeam: CanActivateFn =
## Run within an injection context
When you want to run a given function in an injection context without being in one, you can do it with `runInInjectionContext`.
This requires to have access to a given injector like the `EnvironmentInjector` for example.
When you want to run a given function in an injection context without already being in one, you can do so with `runInInjectionContext`.
This requires access to a given injector, like the `EnvironmentInjector`, for example:
<docs-code header="src/app/heroes/hero.service.ts" language="typescript"
highlight="[9]">
@@ -66,7 +66,7 @@ Note that `inject` will return an instance only if the injector can resolve the
## Asserts the context
Angular provides `assertInInjectionContext` helper function to assert that the current context is an injection context.
Angular provides the `assertInInjectionContext` helper function to assert that the current context is an injection context.
## Using DI outside of a context
@@ -4,13 +4,13 @@ Dependency injection, or DI, is one of the fundamental concepts in Angular. DI i
Two main roles exist in the DI system: dependency consumer and dependency provider.
Angular facilitates the interaction between dependency consumers and dependency providers using an abstraction called `Injector`. When a dependency is requested, the injector checks its registry to see if there is an instance already available there. If not, a new instance is created and stored in the registry. Angular creates an application-wide injector (also known as "root" injector) during the application bootstrap process. In most cases you don't need to manually create injectors, but you should know that there is a layer that connects providers and consumers.
Angular facilitates the interaction between dependency consumers and dependency providers using an abstraction called `Injector`. When a dependency is requested, the injector checks its registry to see if there is an instance already available there. If not, a new instance is created and stored in the registry. Angular creates an application-wide injector (also known as the "root" injector) during the application bootstrap process. In most cases you don't need to manually create injectors, but you should know that there is a layer that connects providers and consumers.
This topic covers basic scenarios of how a class can act as a dependency. Angular also allows you to use functions, objects, primitive types such as string or Boolean, or any other types as dependencies. For more information, see [Dependency providers](/guide/di/dependency-injection-providers).
## Providing dependency
## Providing a dependency
Consider there is a class called `HeroService` that needs to act as a dependency in a component.
Consider a class called `HeroService` that needs to act as a dependency in a component.
The first step is to add the `@Injectable` decorator to show that the class can be injected.
@@ -24,7 +24,7 @@ A dependency can be provided in multiple places:
* [**Preferred**: At the application root level using `providedIn`.](#preferred-at-the-application-root-level-using-providedin)
* [At the Component level.](#at-the-component-level)
* [At application root level using `ApplicationConfig`.](#at-application-root-level-using-applicationconfig)
* [At the application root level using `ApplicationConfig`.](#at-the-application-root-level-using-applicationconfig)
* [`NgModule` based applications.](#ngmodule-based-applications)
### **Preferred**: At the application root level using `providedIn`
@@ -64,11 +64,11 @@ When you register a provider at the component level, you get a new instance of t
Note: Declaring a service like this causes `HeroService` to always be included in your application— even if the service is unused.
### At application root level using `ApplicationConfig`
### At the application root level using `ApplicationConfig`
You can use the `providers` field of the `ApplicationConfig` (passed to the `bootstrapApplication` function) to provide a service or other `Injectable` at the application level.
In the example below, the `HeroService` is available to all components, directives, and pipes.
In the example below, the `HeroService` is available to all components, directives, and pipes:
<docs-code language="typescript" highlight="[3]">
export const appConfig: ApplicationConfig = {
@@ -136,5 +136,5 @@ style componentConstructor text-align: left
## What's next
<docs-pill-row>
<docs-pill href="/guide/di/creating-injectable-service" title="Creating and injecting services"/>
<docs-pill href="/guide/di/creating-injectable-service" title="Creating an injectable service"/>
</docs-pill-row>
+7 -8
View File
@@ -5,7 +5,7 @@ This guide explores additional features of dependency injection in Angular.
## Custom providers with `@Inject`
Using a custom provider allows you to provide a concrete implementation for implicit dependencies, such as built-in browser APIs.
The following example uses an `InjectionToken` to provide the [localStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) browser API as a dependency in the `BrowserStorageService`.
The following example uses an `InjectionToken` to provide the [localStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) browser API as a dependency in the `BrowserStorageService`:
<docs-code header="src/app/storage.service.ts" language="typescript"
highlight="[[3,6],[12]]">
@@ -32,8 +32,8 @@ export class BrowserStorageService {
}
</docs-code>
The `factory` function returns the `localStorage` property that is attached to the browser window object.
The `Inject` decorator is a constructor parameter used to specify a custom provider of a dependency.
The `factory` function returns the `localStorage` property that is attached to the browser's window object.
The `Inject` decorator is applied to the `storage` constructor parameter and specifies a custom provider of the dependency.
This custom provider can now be overridden during testing with a mock API of `localStorage` instead of interacting with real browser APIs.
@@ -42,7 +42,7 @@ This custom provider can now be overridden during testing with a mock API of `lo
Although developers strive to avoid it, some visual effects and third-party tools require direct DOM access.
As a result, you might need to access a component's DOM element.
Angular exposes the underlying element of a `@Component` or `@Directive` via injection using the `ElementRef` injection token.
Angular exposes the underlying element of a `@Component` or `@Directive` via injection using the `ElementRef` injection token:
<docs-code language="typescript" highlight="[7]">
import { Directive, ElementRef } from '@angular/core';
@@ -66,14 +66,13 @@ You can't refer directly to a class until it's been defined.
This isn't usually a problem, especially if you adhere to the recommended *one class per file* rule.
But sometimes circular references are unavoidable.
For example, when class 'A' refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first.
For example, when class 'A' refers to class 'B' and 'B' refers to 'A', one of them has to be defined first.
The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
You face a similar problem when a class makes *a reference to itself*.
For example in its `providers` array.
The `providers` array is a property of the `@Component()` decorator function which must appear before the class definition.
For example, in its `providers` array.
The `providers` array is a property of the `@Component()` decorator function, which must appear before the class definition.
You can break such circular references by using `forwardRef`.
<docs-code header="app.component.ts" language="typescript" highlight="[4]">
@@ -30,7 +30,7 @@ The `EnvironmentInjector` can be configured in one of two ways by using:
* The `@Injectable()` `providedIn` property to refer to `root` or `platform`
* The `ApplicationConfig` `providers` array
<docs-callout title="Tree-shaking and &commat;Injectable()">
<docs-callout title="Tree-shaking and @Injectable()">
Using the `@Injectable()` `providedIn` property is preferable to using the `ApplicationConfig` `providers` array. With `@Injectable()` `providedIn`, optimization tools can perform tree-shaking, which removes services that your application isn't using. This results in smaller bundle sizes.
@@ -46,7 +46,7 @@ Provide services using `providedIn` of `@Injectable()` as follows:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // &lt;--provides this service in the root EnvironmentInjector
providedIn: 'root' // <--provides this service in the root EnvironmentInjector
})
export class ItemService {
name = 'telephone';
@@ -134,7 +134,7 @@ For example, the following `TestComponent` configures the `ElementInjector` by p
<docs-code language="typescript" highlight="[3]">
@Component({
&hellip;
…
providers: [{ provide: ItemService, useValue: { name: 'lamp' } }]
})
export class TestComponent
@@ -335,11 +335,11 @@ The following is an example of how the `<app-root>` and `<app-child>` view trees
<#VIEW>
<app-child>
<#VIEW>
&hellip;content goes here&hellip;
…content goes here…
</#VIEW>
</app-child>
<#VIEW>
<app-root>
</#VIEW>
</app-root>
</docs-code>
Understanding the idea of the `<#VIEW>` demarcation is especially significant when you configure services in the component class.
@@ -387,24 +387,24 @@ The most basic rendered view would look like nested HTML elements such as the fo
<docs-code language="html">
&lt;app-root&gt; &lt;!-- AppComponent selector --&gt;
&lt;app-child&gt; &lt;!-- ChildComponent selector --&gt;
&lt;/app-child&gt;
&lt;/app-root&gt;
<app-root> <!-- AppComponent selector -->
<app-child> <!-- ChildComponent selector -->
</app-child>
</app-root>
</docs-code>
However, behind the scenes, Angular uses a logical view representation as follows when resolving injection requests:
<docs-code language="html">
&lt;app-root&gt; &lt;!-- AppComponent selector --&gt;
&lt;#VIEW&gt;
&lt;app-child&gt; &lt;!-- ChildComponent selector --&gt;
&lt;#VIEW&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root> <!-- AppComponent selector -->
<#VIEW>
<app-child> <!-- ChildComponent selector -->
<#VIEW>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
The `<#VIEW>` here represents an instance of a template.
@@ -435,16 +435,16 @@ Emoji from FlowerService: &#x1F33A;
In the logical tree, this would be represented as follows:
<docs-code language="html" highlight="[[1],[2],[4]]">
&lt;app-root @ApplicationConfig
&commat;Inject(FlowerService) flower=&gt;"&#x1F33A;"&gt;
&lt;#VIEW&gt;
&lt;p&gt;Emoji from FlowerService: {{flower.emoji}} (&#x1F33A;)&lt;/p&gt;
&lt;app-child&gt;
&lt;#VIEW&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(FlowerService) flower=>"&#x1F33A;">
<#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (&#x1F33A;)</p>
<app-child>
<#VIEW>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -507,19 +507,19 @@ In the logical tree, this is represented as follows:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(FlowerService) flower=&gt;"&#x1F33A;"&gt;
&lt;#VIEW&gt;
&lt;p&gt;Emoji from FlowerService: {{flower.emoji}} (&#x1F33A;)&lt;/p&gt;
&lt;app-child &commat;Provide(FlowerService="&#x1F33B;")
&commat;Inject(FlowerService)=&gt;"&#x1F33B;"&gt; &lt;!-- search ends here --&gt;
&lt;#VIEW&gt; &lt;!-- search starts here --&gt;
&lt;h2&gt;Child Component&lt;/h2&gt;
&lt;p&gt;Emoji from FlowerService: {{flower.emoji}} (&#x1F33B;)&lt;/p&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(FlowerService) flower=>"&#x1F33A;">
<#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (&#x1F33A;)</p>
<app-child @Provide(FlowerService="&#x1F33B;")
@Inject(FlowerService)=>"&#x1F33B;"> <!-- search ends here -->
<#VIEW> <!-- search starts here -->
<h2>Child Component</h2>
<p>Emoji from FlowerService: {{flower.emoji}} (&#x1F33B;)</p>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -613,18 +613,18 @@ The logic tree for this example of `viewProviders` is as follows:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(AnimalService) animal=&gt;"&#x1F433;"&gt;
&lt;#VIEW&gt;
&lt;app-child&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F436;")
&commat;Inject(AnimalService=&gt;"&#x1F436;")&gt;
&lt;!-- ^^using viewProviders means AnimalService is available in &lt;#VIEW&gt;--&gt;
&lt;p&gt;Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)&lt;/p&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(AnimalService) animal=>"&#x1F433;">
<#VIEW>
<app-child>
<#VIEW @Provide(AnimalService="&#x1F436;")
@Inject(AnimalService=>"&#x1F436;")>
<!-- ^^using viewProviders means AnimalService is available in <#VIEW>-->
<p>Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)</p>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -715,31 +715,31 @@ The `AnimalService` in the logical tree would look like this:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(AnimalService) animal=&gt;"&#x1F433;"&gt;
&lt;#VIEW&gt;
&lt;app-child&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F436;")
&commat;Inject(AnimalService=&gt;"&#x1F436;")&gt;
&lt;!-- ^^using viewProviders means AnimalService is available in &lt;#VIEW&gt;--&gt;
&lt;p&gt;Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)&lt;/p&gt;
<app-root @ApplicationConfig
@Inject(AnimalService) animal=>"&#x1F433;">
<#VIEW>
<app-child>
<#VIEW @Provide(AnimalService="&#x1F436;")
@Inject(AnimalService=>"&#x1F436;")>
<!-- ^^using viewProviders means AnimalService is available in <#VIEW>-->
<p>Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)</p>
&lt;div class="container"&gt;
&lt;h3&gt;Content projection&lt;/h3&gt;
&lt;app-inspector &commat;Inject(AnimalService) animal=&gt;"&#x1F433;"&gt;
&lt;p&gt;Emoji from AnimalService: {{animal.emoji}} (&#x1F433;)&lt;/p&gt;
&lt;/app-inspector&gt;
&lt;/div&gt;
<div class="container">
<h3>Content projection</h3>
<app-inspector @Inject(AnimalService) animal=>"&#x1F433;">
<p>Emoji from AnimalService: {{animal.emoji}} (&#x1F433;)</p>
</app-inspector>
</div>
&lt;app-inspector&gt;
&lt;#VIEW &commat;Inject(AnimalService) animal=&gt;"&#x1F436;"&gt;
&lt;p&gt;Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)&lt;/p&gt;
&lt;/#VIEW&gt;
&lt;/app-inspector&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-inspector>
<#VIEW @Inject(AnimalService) animal=>"&#x1F436;">
<p>Emoji from AnimalService: {{animal.emoji}} (&#x1F436;)</p>
</#VIEW>
</app-inspector>
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -773,16 +773,16 @@ In a logical tree, this same idea might look like this:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(FlowerService) flower=&gt;"&#x1F33A;"&gt;
&lt;#VIEW&gt;
&lt;app-child &commat;Provide(FlowerService="&#x1F33B;")&gt;
&lt;#VIEW &commat;Inject(FlowerService, SkipSelf)=&gt;"&#x1F33A;"&gt;
&lt;!-- With SkipSelf, the injector looks to the next injector up the tree (app-root) --&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(FlowerService) flower=>"&#x1F33A;">
<#VIEW>
<app-child @Provide(FlowerService="&#x1F33B;")>
<#VIEW @Inject(FlowerService, SkipSelf)=>"&#x1F33A;">
<!-- With SkipSelf, the injector looks to the next injector up the tree (app-root) -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -794,15 +794,15 @@ Here's the idea in the logical tree:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(FlowerService) flower=&gt;"&#x1F33A;"&gt;
&lt;#VIEW&gt; &lt;!-- end search here with null--&gt;
&lt;app-child &commat;Provide(FlowerService="&#x1F33B;")&gt; &lt;!-- start search here --&gt;
&lt;#VIEW &commat;Inject(FlowerService, &commat;SkipSelf, &commat;Host, &commat;Optional)=&gt;null&gt;
&lt;/#VIEW&gt;
&lt;/app-parent&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(FlowerService) flower=>"&#x1F33A;">
<#VIEW> <!-- end search here with null-->
<app-child @Provide(FlowerService="&#x1F33B;")> <!-- start search here -->
<#VIEW @Inject(FlowerService, @SkipSelf, @Host, @Optional)=>null>
</#VIEW>
</app-parent>
</#VIEW>
</app-root>
</docs-code>
@@ -820,7 +820,7 @@ Instead, the injector will begin at the `<app-root>` `ElementInjector`.
@Component({
standalone: true,
selector: 'app-child',
&hellip;
…
viewProviders: [
{ provide: AnimalService, useValue: { emoji: '&#x1F436;' } },
],
@@ -831,17 +831,17 @@ The logical tree looks like this with `@SkipSelf()` in `<app-child>`:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(AnimalService=&gt;"&#x1F433;")&gt;
&lt;#VIEW&gt;&lt;!-- search begins here --&gt;
&lt;app-child&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F436;")
&commat;Inject(AnimalService, SkipSelf=&gt;"&#x1F433;")&gt;
&lt;!--Add &commat;SkipSelf --&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(AnimalService=>"&#x1F433;")>
<#VIEW><!-- search begins here -->
<app-child>
<#VIEW @Provide(AnimalService="&#x1F436;")
@Inject(AnimalService, SkipSelf=>"&#x1F433;")>
<!--Add @SkipSelf -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -857,7 +857,7 @@ You can also see `@Host()` in the constructor:
@Component({
standalone: true
selector: 'app-child',
&hellip;
…
viewProviders: [
{ provide: AnimalService, useValue: { emoji: '&#x1F436;' } },
]
@@ -871,16 +871,16 @@ export class ChildComponent {
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(AnimalService=&gt;"&#x1F433;")&gt;
&lt;#VIEW&gt;
&lt;app-child&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F436;")
&commat;Inject(AnimalService, &commat;Host=&gt;"&#x1F436;")&gt; &lt;!-- &commat;Host stops search here --&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(AnimalService=>"&#x1F433;")>
<#VIEW>
<app-child>
<#VIEW @Provide(AnimalService="&#x1F436;")
@Inject(AnimalService, @Host=>"&#x1F436;")> <!-- @Host stops search here -->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -915,7 +915,7 @@ export class ChildComponent {
and how `@Host()` works.
-->
When `@Host()` and `@SkipSelf()` were applied to the `FlowerService`, which is in the `providers` array, the result was `null` because `@SkipSelf()` starts its search in the `<app-child>` injector, but `@Host()` stops searching at `<#VIEW>` &mdash;where there is no `FlowerService`
When `@Host()` and `@SkipSelf()` were applied to the `FlowerService`, which is in the `providers` array, the result was `null` because `@SkipSelf()` starts its search in the `<app-child>` injector, but `@Host()` stops searching at `<#VIEW>` —where there is no `FlowerService`
In the logical tree, you can see that the `FlowerService` is visible in `<app-child>`, not its `<#VIEW>`.
However, the `AnimalService`, which is provided in the `AppComponent` `viewProviders` array, is visible.
@@ -924,19 +924,19 @@ The logical tree representation shows why this is:
<docs-code language="html">
&lt;app-root @ApplicationConfig
&commat;Inject(AnimalService=&gt;"&#x1F433;")&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F994;")
&commat;Inject(AnimalService, &commat;Optional)=&gt;"&#x1F994;"&gt;
&lt;!-- ^^&commat;SkipSelf() starts here, &commat;Host() stops here^^ --&gt;
&lt;app-child&gt;
&lt;#VIEW &commat;Provide(AnimalService="&#x1F436;")
&commat;Inject(AnimalService, &commat;SkipSelf, &commat;Host, &commat;Optional)=&gt;"&#x1F994;"&gt;
&lt;!-- Add &commat;SkipSelf ^^--&gt;
&lt;/#VIEW&gt;
&lt;/app-child&gt;
&lt;/#VIEW&gt;
&lt;/app-root&gt;
<app-root @ApplicationConfig
@Inject(AnimalService=>"&#x1F433;")>
<#VIEW @Provide(AnimalService="&#x1F994;")
@Inject(AnimalService, @Optional)=>"&#x1F994;">
<!-- ^^@SkipSelf() starts here, @Host() stops here^^ -->
<app-child>
<#VIEW @Provide(AnimalService="&#x1F436;")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"&#x1F994;">
<!-- Add @SkipSelf ^^-->
</#VIEW>
</app-child>
</#VIEW>
</app-root>
</docs-code>
@@ -25,7 +25,7 @@ This component contains a body and can contain an optional header.
<docs-code language="html">
<lib-card>;
<lib-header>&hellip;</lib-header>;
<lib-header>…</lib-header>;
</lib-card>;
</docs-code>
@@ -35,13 +35,13 @@ In a likely implementation, the `<lib-card>` component uses `@ContentChild()` or
<docs-code language="typescript" highlight="[12]">
@Component({
selector: 'lib-header',
&hellip;,
…,
})
class LibHeaderComponent {}
@Component({
selector: 'lib-card',
&hellip;,
…,
})
class LibCardComponent {
@ContentChild(LibHeaderComponent) header: LibHeaderComponent|null = null;
@@ -108,13 +108,13 @@ abstract class LibHeaderToken {}
providers: [
{provide: LibHeaderToken, useExisting: LibHeaderComponent}
]
&hellip;,
…,
})
class LibHeaderComponent extends LibHeaderToken {}
@Component({
selector: 'lib-card',
&hellip;,
…,
})
class LibCardComponent {
@ContentChild(LibHeaderToken) header: LibHeaderToken|null = null;
@@ -156,7 +156,7 @@ abstract class LibHeaderToken {
providers: [
{provide: LibHeaderToken, useExisting: LibHeaderComponent}
]
&hellip;,
…,
})
class LibHeaderComponent extends LibHeaderToken {
doSomething(): void {
@@ -166,7 +166,7 @@ class LibHeaderComponent extends LibHeaderToken {
@Component({
selector: 'lib-card',
&hellip;,
…,
})
class LibCardComponent implement AfterContentInit {
@ContentChild(LibHeaderToken) header: LibHeaderToken|null = null;
@@ -195,33 +195,3 @@ providers.
If a component or directive with `hostDirectives` and those host directives both provide the same
injection token, the providers defined by class with `hostDirectives` take precedence over providers
defined by the host directives.
### Performance
While the directive composition API offers a powerful tool for reusing common behaviors, excessive
use of host directives can impact your application's memory use. If you create components or
directives that use *many* host directives, you may inadvertently balloon the memory used by your
application.
The following example shows a component that applies several host directives.
```typescript
@Component({
standalone: true,
hostDirectives: [
DisabledState,
RequiredState,
ValidationState,
ColorState,
RippleBehavior,
],
})
export class CustomCheckbox { }
```
This example declares a custom checkbox component that includes five host directives. This
means that Angular will create six objects each time a `CustomCheckbox` renders— one for the
component and one for each host directive. For a few checkboxes on a page, this won't pose any
significant issues. However, if your page renders *hundreds* of checkboxes, such as in a table, then
you could start to see an impact of the additional object allocations. Always be sure to profile
your application to determine the right composition pattern for your use case.
@@ -130,7 +130,7 @@ When you write your own structural directives, use the following syntax:
<docs-code hideCopy language="typescript">
&ast;:prefix="( :let &verbar; :expression ) (';' &verbar; ',')? ( :let &verbar; :as &verbar; :keyExp )&ast;"
*:prefix="( :let | :expression ) (';' | ',')? ( :let | :as | :keyExp )*"
</docs-code>
+14 -14
View File
@@ -31,7 +31,7 @@ After you register your configured class with the browser's custom-element regis
<docs-code language="html">
&lt;my-popup message="Use Angular!"&gt;&lt;/my-popup&gt;
<my-popup message="Use Angular!"></my-popup>
</docs-code>
@@ -73,7 +73,7 @@ For more information, see Web Component documentation for [Creating custom event
Previously, when you wanted to add a component to an application at runtime, you had to define a _dynamic component_, and then you would have to load it, attach it to an element in the DOM, and wire up all of the dependencies, change detection, and event handling.
Using an Angular custom element makes the process simpler and more transparent, by providing all the infrastructure and framework automatically &mdash;all you have to do is define the kind of event handling you want.
Using an Angular custom element makes the process simpler and more transparent, by providing all the infrastructure and framework automatically —all you have to do is define the kind of event handling you want.
\(You do still have to exclude the component from compilation, if you are not going to use it in your application.\)
The following Popup Service example application defines a component that you can either load dynamically or convert to a custom element.
@@ -111,9 +111,9 @@ Assume you create a `my-dialog` custom element based on the following component:
<docs-code language="typescript">
&commat;Component(&hellip;)
@Component(…)
class MyDialog {
&commat;Input() content: string;
@Input() content: string;
}
</docs-code>
@@ -123,10 +123,10 @@ For that, use the `NgElement` and `WithProperties` types \(both exported from `@
<docs-code language="typescript">
const aDialog = document.createElement('my-dialog') as NgElement &amp; WithProperties&lt;{content: string}&gt;;
const aDialog = document.createElement('my-dialog') as NgElement & WithProperties<{content: string}>;
aDialog.content = 'Hello, world!';
aDialog.content = 123; // &lt;-- ERROR: TypeScript knows this should be a string.
aDialog.body = 'News'; // &lt;-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
</docs-code>
@@ -139,9 +139,9 @@ An alternative way, that only requires defining each custom element's type once,
declare global {
interface HTMLElementTagNameMap {
'my-dialog': NgElement &amp; WithProperties&lt;{content: string}&gt;;
'my-other-element': NgElement &amp; WithProperties&lt;{foo: 'bar'}&gt;;
&hellip;
'my-dialog': NgElement & WithProperties<{content: string}>;
'my-other-element': NgElement & WithProperties<{foo: 'bar'}>;
…
}
}
@@ -151,9 +151,9 @@ Now, TypeScript can infer the correct type the same way it does for built-in ele
<docs-code language="typescript">
document.createElement('div') //--&gt; HTMLDivElement (built-in element)
document.querySelector('foo') //--&gt; Element (unknown element)
document.createElement('my-dialog') //--&gt; NgElement &amp; WithProperties&lt;{content: string}&gt; (custom element)
document.querySelector('my-other-element') //--&gt; NgElement &amp; WithProperties&lt;{foo: 'bar'}&gt; (custom element)
document.createElement('div') //--> HTMLDivElement (built-in element)
document.querySelector('foo') //--> Element (unknown element)
document.createElement('my-dialog') //--> NgElement & WithProperties<{content: string}> (custom element)
document.querySelector('my-other-element') //--> NgElement & WithProperties<{foo: 'bar'}> (custom element)
</docs-code>
@@ -42,7 +42,7 @@ The following code from the example shows the setup in the root module.
## Create a form object model
A dynamic form requires an object model that can describe all scenarios needed by the form functionality.
The example hero-application form is a set of questions &mdash; that is, each control in the form must ask a question and accept an answer.
The example hero-application form is a set of questions — that is, each control in the form must ask a question and accept an answer.
The data model for this type of form must represent a question.
The example includes the `DynamicFormQuestionComponent`, which defines a question as the fundamental object in the model.
@@ -61,11 +61,11 @@ The same built-in validators that are available as attributes in template-driven
For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
To update the actor form to be a reactive form, use some of the same
built-in validators &mdash;this time, in function form, as in the following example.
built-in validators —this time, in function form, as in the following example.
<docs-code header="reactive/actor-form-reactive.component.ts (validator functions)" path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.1.ts" visibleRegion="form-group"/>
In this example, the `name` control sets up two built-in validators &mdash;`Validators.required` and `Validators.minLength(4)`&mdash; and one custom validator, `forbiddenNameValidator`.
In this example, the `name` control sets up two built-in validators —`Validators.required` and `Validators.minLength(4)`— and one custom validator, `forbiddenNameValidator`.
All of these validators are synchronous, so they are passed as the second argument.
Notice that you can support multiple validators by passing the functions in as an array.
@@ -129,7 +129,7 @@ For example:
<docs-code header="template/actor-form-template.component.html (forbidden-name-input)" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="name-input"/>
HELPFUL: Notice that the custom validation directive is instantiated with `useExisting` rather than `useClass`.
The registered validator must be *this instance* of the `ForbiddenValidatorDirective` &mdash;the instance in the form with its `forbiddenName` property bound to "bob".
The registered validator must be *this instance* of the `ForbiddenValidatorDirective` —the instance in the form with its `forbiddenName` property bound to "bob".
If you were to replace `useExisting` with `useClass`, then you'd be registering a new class instance, one that doesn't have a `forbiddenName`.
@@ -253,8 +253,8 @@ The following example shows how to achieve this in a template-driven form.
<docs-code language="html">
&lt;input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator&gt;
&lt;app-spinner *ngIf="model.pending"&gt;&lt;/app-spinner&gt;
<input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator>
<app-spinner *ngIf="model.pending"></app-spinner>
</docs-code>
+5 -5
View File
@@ -110,7 +110,7 @@ The view-to-model diagram shows how data flows when an input field's value is ch
```mermaid
flowchart TB
U{User}
I("&lt;input&gt;")
I("<input>")
CVA(ControlValueAccessor)
FC(FormControl)
O(Observers)
@@ -130,14 +130,14 @@ The model-to-view diagram shows how a programmatic change to the model is propag
```mermaid
flowchart TB
U{User}
I(&lt;input&gt;)
I(<input>)
CVA(ControlValueAccessor)
FC(FormControl)
O(Observers)
U-->|"Calls setValue() on the FormControl"|FC
FC-->|Notifies the ControlValueAccessor|CVA
FC-.->|Fires a 'valueChanges' event to observers|O
CVA-->|"Updates the value of the &lt;input&gt;"|I
CVA-->|"Updates the value of the <input>"|I
```
### Data flow in template-driven forms
@@ -157,7 +157,7 @@ The view-to-model diagram shows how data flows when an input field's value is ch
```mermaid
flowchart TB
U{User}
I(&lt;input&gt;)
I(<input>)
CVA(ControlValueAccessor)
FC(FormControl)
M(NgModel)
@@ -207,7 +207,7 @@ flowchart TB
FC2(FormControl)
O(Observers)
CVA(ControlValueAccessor)
I("&lt;input&gt;")
I("<input>")
FC2-.->|Fires a 'valueChanges' event to observers|O
O-->|ControlValueAccessor receives valueChanges event|CVA
CVA-->|Sets the value in the control|I
@@ -10,7 +10,7 @@ Angular supports two design approaches for interactive forms. Template-driven fo
Template-driven forms are a great choice for small or simple forms, while reactive forms are more scalable and suitable for complex forms. For a comparison of the two approaches, see [Choosing an approach](guide/forms#choosing-an-approach)
</docs-callout>
You can build almost any kind of form with an Angular template &mdash;login forms, contact forms, and pretty much any business form.
You can build almost any kind of form with an Angular template —login forms, contact forms, and pretty much any business form.
You can lay out the controls creatively and bind them to the data in your object model.
You can specify validation rules and display validation errors, conditionally allow input from specific controls, trigger built-in visual feedback, and much more.
@@ -339,11 +339,11 @@ You will bind the form property that indicates its overall validity to the **Sub
</docs-step>
<docs-step title="Run the application">
Notice that the button is enabled &mdash;although it doesn't do anything useful yet.
Notice that the button is enabled —although it doesn't do anything useful yet.
</docs-step>
<docs-step title="Delete the Name value">
This violates the "required" rule, so it displays the error message &mdash;and notice that it also disables the **Submit** button.
This violates the "required" rule, so it displays the error message —and notice that it also disables the **Submit** button.
You didn't have to explicitly wire the button's enabled state to the form's validity.
The `FormsModule` did this automatically when you defined a template reference variable on the enhanced form element, then referred to that variable in the button control.
+8 -8
View File
@@ -83,11 +83,11 @@ HELPFUL: Prefer using [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/C
### `withXsrfConfiguration(...)`
Including this option allows for customization of `HttpClient`'s built-in XSRF security functionality. See the [security guide](guide/http/security) for more information.
Including this option allows for customization of `HttpClient`'s built-in XSRF security functionality. See the [security guide](best-practices/security) for more information.
### `withNoXsrfProtection()`
Including this option disables `HttpClient`'s built-in XSRF security functionality. See the [security guide](guide/http/security) for more information.
Including this option disables `HttpClient`'s built-in XSRF security functionality. See the [security guide](best-practices/security) for more information.
## `HttpClientModule`-based configuration
@@ -95,12 +95,12 @@ Some applications may configure `HttpClient` using the older API based on NgModu
This table lists the NgModules available from `@angular/common/http` and how they relate to the provider configuration functions above.
| **NgModule** | `provideHttpClient()` equivalent |
| - | - |
| `HttpClientModule` | `provideHttpClient(withInterceptorsFromDi())` |
| `HttpClientJsonpModule` | `withJsonpSupport()` |
| `HttpClientXsrfModule.withOptions(...)` | `withXsrfConfiguration(...)` |
| `HttpClientXsrfModule.disable()` | `withNoXsrfProtection()` |
| **NgModule** | `provideHttpClient()` equivalent |
| --------------------------------------- | --------------------------------------------- |
| `HttpClientModule` | `provideHttpClient(withInterceptorsFromDi())` |
| `HttpClientJsonpModule` | `withJsonpSupport()` |
| `HttpClientXsrfModule.withOptions(...)` | `withXsrfConfiguration(...)` |
| `HttpClientXsrfModule.disable()` | `withNoXsrfProtection()` |
<docs-callout important title="Use caution when using HttpClientModule in multiple injectors">
When `HttpClientModule` is present in multiple injectors, the behavior of interceptors is poorly defined and depends on the exact options and provider/import ordering.
+3 -1
View File
@@ -8,12 +8,13 @@ At the end, tests can verify that the app made no unexpected requests.
## Setup for testing
To begin testing usage of `HttpClient`, configure `TestBed` and include `provideHttpClientTesting` in your test's setup. This configures `HttpClient` to use a test backend instead of the real network. It also provides `HttpTestingController`, which you'll use to interact with the test backend, set expectations about which requests have been made, and flush responses to those requests. `HttpTestingController` can be injected from `TestBed` once configured.
To begin testing usage of `HttpClient`, configure `TestBed` and include `provideHttpClient()` and `provideHttpClientTesting` in your test's setup. This configures `HttpClient` to use a test backend instead of the real network. It also provides `HttpTestingController`, which you'll use to interact with the test backend, set expectations about which requests have been made, and flush responses to those requests. `HttpTestingController` can be injected from `TestBed` once configured.
<docs-code language="ts">
TestBed.configureTestingModule({
providers: [
// ... other test providers
provideHttpClient(),
provideHttpClientTesting(),
],
});
@@ -31,6 +32,7 @@ For example, you can write a test that expects a GET request to occur and provid
TestBed.configureTestingModule({
providers: [
ConfigService,
provideHttpClient(),
provideHttpClientTesting(),
],
});
@@ -18,7 +18,7 @@ To display the current date in the format for the current locale, use the follow
<docs-code language="typescript">
{{ today &verbar; date }}
{{ today | date }}
</docs-code>
@@ -32,7 +32,7 @@ To force the currency to use American English \(`en-US`\), use the following for
<docs-code language="typescript">
{{ amount &verbar; currency : 'en-US' }}
{{ amount | currency : 'en-US' }}
</docs-code>
@@ -32,7 +32,7 @@ The following example defines the `introductionHeader` custom ID for a variable.
<docs-code language="typescript">
variableText1 = &dollar;localize `:&commat;&commat;introductionHeader:Hello i18n!`;
variableText1 = $localize `:@@introductionHeader:Hello i18n!`;
</docs-code>
@@ -58,7 +58,7 @@ The following example defines the `introductionHeader` custom ID and description
<docs-code language="typescript">
variableText2 = &dollar;localize `:An introduction header for this sample&commat;&commat;introductionHeader:Hello i18n!`;
variableText2 = $localize `:An introduction header for this sample@@introductionHeader:Hello i18n!`;
</docs-code>
@@ -72,7 +72,7 @@ The following example defines the `introductionHeader` custom ID for a variable.
<docs-code language="typescript">
variableText3 = &dollar;localize `:site header|An introduction header for this sample&commat;&commat;introductionHeader:Hello i18n!`;
variableText3 = $localize `:site header|An introduction header for this sample@@introductionHeader:Hello i18n!`;
</docs-code>
+10 -10
View File
@@ -11,7 +11,7 @@ To prepare your project for translation, complete the following actions.
In a component template, the i18n metadata is the value of the `i18n` attribute.
<docs-code language="html">
&lt;element i18n="{i18n_metadata}"&gt;{string_to_translate}&lt;/element&gt;
<element i18n="{i18n_metadata}">{string_to_translate}</element>
</docs-code>
Use the `i18n` attribute to mark a static text message in your component templates for translation.
@@ -44,7 +44,7 @@ The following example shows the `<ng-container>` element transformed into a non-
In a component template, the i18n metadata is the value of the `i18n-{attribute_name}` attribute.
<docs-code language="html">
&lt;element i18n-{attribute_name}="{i18n_metadata}" {attribute_name}="{attribute_value}" /&gt;
<element i18n-{attribute_name}="{i18n_metadata}" {attribute_name}="{attribute_value}" />
</docs-code>
The attributes of HTML elements include text that should be translated along with the rest of the displayed text in the component template.
@@ -55,7 +55,7 @@ Use the following syntax to assign a meaning, description, and custom ID.
<!--todo: replace with docs-code -->
<docs-code language="html">
i18n-{attribute_name}="{meaning}|{description}&commat;&commat;{id}"
i18n-{attribute_name}="{meaning}|{description}@@{id}"
</docs-code>
### `i18n-title` example
@@ -82,7 +82,7 @@ Use the [`$localize`][ApiLocalizeInitLocalize] tagged message string to mark a s
<!--todo: replace with docs-code -->
<docs-code language="typescript">
&dollar;localize `string_to_translate`;
$localize `string_to_translate`;
</docs-code>
The i18n metadata is surrounded by colon \(`:`\) characters and prepends the translation source text.
@@ -90,7 +90,7 @@ The i18n metadata is surrounded by colon \(`:`\) characters and prepends the tra
<!--todo: replace with docs-code -->
<docs-code language="typescript">
&dollar;localize `:{i18n_metadata}:string_to_translate`
$localize `:{i18n_metadata}:string_to_translate`
</docs-code>
### Include interpolated text
@@ -100,13 +100,13 @@ Include [interpolations](guide/templates/interpolation) in a [`$localize`][ApiLo
<!--todo: replace with docs-code -->
<docs-code language="typescript">
&dollar;localize `string_to_translate &dollar;{variable_name}`;
$localize `string_to_translate ${variable_name}`;
</docs-code>
### Name the interpolation placeholder
<docs-code language="typescript">
&dollar;localize `string_to_translate &dollar;{variable_name}:placeholder_name:`;
$localize `string_to_translate ${variable_name}:placeholder_name:`;
</docs-code>
## i18n metadata for translation
@@ -114,7 +114,7 @@ Include [interpolations](guide/templates/interpolation) in a [`$localize`][ApiLo
<!--todo: replace with docs-code -->
<docs-code language="html">
{meaning}|{description}&commat;&commat;{custom_id}
{meaning}|{description}@@{custom_id}
</docs-code>
The following parameters provide context and additional information to reduce confusion for your translator.
@@ -143,7 +143,7 @@ The following example shows the value of the [`$localize`][ApiLocalizeInitLocali
<docs-code language="typescript">
&dollar;localize `:An introduction header for this sample:Hello i18n!`;
$localize `:An introduction header for this sample:Hello i18n!`;
</docs-code>
@@ -166,7 +166,7 @@ The following code example shows the value of the [`$localize`][ApiLocalizeInitL
<docs-code language="typescript">
&dollar;localize `:site header|An introduction header for this sample:Hello i18n!`;
$localize `:site header|An introduction header for this sample:Hello i18n!`;
</docs-code>
@@ -88,7 +88,7 @@ To create a translation file for a locale or language, complete the following ac
<docs-code language="file">
messages.xlf --&gt; messages.{locale}.xlf
messages.xlf --> messages.{locale}.xlf
</docs-code>
@@ -132,20 +132,20 @@ The following actions describe the translation process for French.
1. Open `messages.fr.xlf` and find the first `<trans-unit>` element.
This is a *translation unit*, also known as a *text node*, that represents the translation of the `<h1>` greeting tag that was previously marked with the `i18n` attribute.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-hello-before"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-hello-before"/>
The `id="introductionHeader"` is a [custom ID][GuideI18nOptionalManageMarkedText], but without the `@@` prefix required in the source HTML.
1. Duplicate the `<source>... </source>` element in the text node, rename it to `target`, and then replace the content with the French text.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;, after translation)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-hello"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>, after translation)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-hello"/>
In a more complex translation, the information and context in the [description and meaning elements][GuideI18nCommonPrepareAddHelpfulDescriptionsAndMeanings] help you choose the right words for translation.
1. Translate the other text nodes.
The following example displays the way to translate.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-other-nodes"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-other-nodes"/>
IMPORTANT: Don't change the IDs for translation units.
Each `id` attribute is generated by Angular and depends on the content of the component text and the assigned meaning.
@@ -169,7 +169,7 @@ To translate a `plural`, translate the ICU format match values.
The following example displays the way to translate.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-plural"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-plural"/>
## Translate alternate expressions
@@ -184,18 +184,18 @@ The following example displays a `select` ICU expression in the component templa
In this example, Angular extracts the expression into two translation units.
The first contains the text outside of the `select` clause, and uses a placeholder for `select` \(`<x id="ICU">`\):
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-select-1"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-select-1"/>
IMPORTANT: When you translate the text, move the placeholder if necessary, but don't remove it.
If you remove the placeholder, the ICU expression is removed from your translated application.
The following example displays the second translation unit that contains the `select` clause.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-select-2"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-select-2"/>
The following example displays both translation units after translation is complete.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-select"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translated-select"/>
## Translate nested expressions
@@ -206,15 +206,15 @@ Angular extracts the expression into two translation units.
The following example displays the first translation unit that contains the text outside of the nested expression.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested-1"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested-1"/>
The following example displays the second translation unit that contains the complete nested expression.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested-2"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested-2"/>
The following example displays both translation units after translating.
<docs-code header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested"/>
<docs-code header="src/locale/messages.fr.xlf (<trans-unit>)" path="adev/src/content/examples/i18n/doc-files/messages.fr.xlf.html" visibleRegion="translate-nested"/>
## What's next
+22 -22
View File
@@ -52,7 +52,7 @@ To activate the `NgOptimizedImage` directive, replace your image's `src` attribu
<docs-code language="typescript">
&lt;img ngSrc="cat.jpg"&gt;
<img ngSrc="cat.jpg">
</docs-code>
@@ -63,7 +63,7 @@ Always mark the [LCP image](https://web.dev/lcp/#what-elements-are-considered) o
<docs-code language="typescript">
&lt;img ngSrc="cat.jpg" width="400" height="200" priority&gt;
<img ngSrc="cat.jpg" width="400" height="200" priority>
</docs-code>
@@ -80,7 +80,7 @@ In order to prevent [image-related layout shifts](https://web.dev/css-web-vitals
<docs-code language="typescript">
&lt;img ngSrc="cat.jpg" width="400" height="200"&gt;
<img ngSrc="cat.jpg" width="400" height="200">
</docs-code>
@@ -100,7 +100,7 @@ When you add the `fill` attribute to your image, you do not need and should not
<docs-code language="typescript">
&lt;img ngSrc="cat.jpg" fill&gt;
<img ngSrc="cat.jpg" fill>
</docs-code>
@@ -118,17 +118,17 @@ IMPORTANT: For the "fill" image to render properly, its parent element **must**
NgOptimizedImage can display an automatic low-resolution placeholder for your image if you're using a CDN or image host that provides automatic image resizing. Take advantage of this feature by adding the `placeholder` attribute to your image:
<code-example format="typescript" language="typescript">
<docs-code format="typescript" language="typescript">
&lt;img ngSrc="cat.jpg" width="400" height="200" placeholder&gt;
<img ngSrc="cat.jpg" width="400" height="200" placeholder>
</code-example>
</docs-code>
Adding this attribute automatically requests a second, smaller version of the image using your specified image loader. This small image will be applied as a `background-image` style with a CSS blur while your image loads. If no image loader is provided, no placeholder image can be generated and an error will be thrown.
The default size for generated placeholders is 30px wide. You can change this size by specifying a pixel value in the `IMAGE_CONFIG` provider, as seen below:
<code-example format="typescript" language="typescript">
<docs-code format="typescript" language="typescript">
providers: [
{
provide: IMAGE_CONFIG,
@@ -137,7 +137,7 @@ providers: [
}
},
],
</code-example>
</docs-code>
If you want sharp edges around your blurred placeholder, you can wrap your image in a containing `<div>` with the `overflow: hidden` style. As long as the `<div>` is the same size as the image (such as by using the `width: fit-content` style), the "fuzzy edges" of the placeholder will be hidden.
@@ -145,11 +145,11 @@ If you want sharp edges around your blurred placeholder, you can wrap your image
You can also specify a placeholder using a base64 [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) without an image loader. The data url format is `data:image/[imagetype];[data]`, where `[imagetype]` is the image format, just as `png`, and `[data]` is a base64 encoding of the image. That encoding can be done using the command line or in JavaScript. For specific commands, see [the MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs#encoding_data_into_base64_format). An example of a data URL placeholder with truncated data is shown below:
<code-example format="typescript" language="typescript">
<docs-code format="typescript" language="typescript">
&lt;img ngSrc="cat.jpg" width="400" height="200" placeholder="data:image/png;base64,iVBORw0K..."&gt;
<img ngSrc="cat.jpg" width="400" height="200" placeholder="data:image/png;base64,iVBORw0K...">
</code-example>
</docs-code>
However, large data URLs increase the size of your Angular bundles and slow down page load. If you cannot use an image loader, the Angular team recommends keeping base64 placeholder images smaller than 4KB and using them exclusively on critical images. In addition to decreasing placeholder dimensions, consider changing image formats or parameters used when saving images. At very low resolutions, these parameters can have a large effect on file size.
@@ -157,11 +157,11 @@ However, large data URLs increase the size of your Angular bundles and slow dow
By default, NgOptimizedImage applies a CSS blur effect to image placeholders. To render a placeholder without blur, provide a `placeholderConfig` argument with an object that includes the `blur` property, set to false. For example:
<code-example format="typescript" language="typescript">
<docs-code format="typescript" language="typescript">
&lt;img ngSrc="cat.jpg" width="400" height="200" placeholder [placeholderConfig]="{blur: false}"&gt;
<img ngSrc="cat.jpg" width="400" height="200" placeholder [placeholderConfig]="{blur: false}">
</code-example>
</docs-code>
## Adjusting image styling
@@ -181,7 +181,7 @@ You can add a [`preconnect` resource hint](https://web.dev/preconnect-and-dns-pr
<docs-code language="html">
&lt;link rel="preconnect" href="https://my.cdn.origin" /&gt;
<link rel="preconnect" href="https://my.cdn.origin" />
</docs-code>
@@ -236,7 +236,7 @@ If you would like to manually define a `srcset` attribute, you can provide your
<docs-code language="html">
&lt;img ngSrc="hero.jpg" ngSrcset="100w, 200w, 300w"&gt;
<img ngSrc="hero.jpg" ngSrcset="100w, 200w, 300w">
</docs-code>
@@ -244,7 +244,7 @@ If the `ngSrcset` attribute is present, `NgOptimizedImage` generates and sets th
<docs-code language="html">
&lt;img ngSrc="hero.jpg" ngSrcset="100w, 200w, 300w" sizes="50vw"&gt;
<img ngSrc="hero.jpg" ngSrcset="100w, 200w, 300w" sizes="50vw">
</docs-code>
@@ -254,7 +254,7 @@ To disable srcset generation for a single image, you can add the `disableOptimiz
<docs-code language="html">
&lt;img ngSrc="about.jpg" disableOptimizedSrcset&gt;
<img ngSrc="about.jpg" disableOptimizedSrcset>
</docs-code>
@@ -264,7 +264,7 @@ By default, `NgOptimizedImage` sets `loading=lazy` for all images that are not m
<docs-code language="html">
&lt;img ngSrc="cat.jpg" width="400" height="200" loading="eager"&gt;
<img ngSrc="cat.jpg" width="400" height="200" loading="eager">
</docs-code>
@@ -274,7 +274,7 @@ You may want to have images displayed at varying widths on differently-sized scr
<docs-code language="html">
&lt;img ngSrc="cat.jpg" width="400" height="200" sizes="(max-width: 768px) 100vw, 50vw"&gt;
<img ngSrc="cat.jpg" width="400" height="200" sizes="(max-width: 768px) 100vw, 50vw">
</docs-code>
@@ -371,7 +371,7 @@ Note that in the above example, we've invented the 'roundedCorners' property nam
<docs-code language="html">
&lt;img ngSrc="profile.jpg" width="300" height="300" [loaderParams]="{roundedCorners: true}"&gt;
<img ngSrc="profile.jpg" width="300" height="300" [loaderParams]="{roundedCorners: true}">
</docs-code>
+2 -2
View File
@@ -31,10 +31,10 @@ The following table summarizes the `@NgModule` metadata properties.
| Property | Details |
|:--- |:--- |
| `declarations` | A list of [declarable](/guide/ngmodules/faq#what-is-a-declarable?) classes (*components*, *directives*, and *pipes*) that *belong to this module*. <ol> <li> When compiling a template, you need to determine a set of selectors which should be used for triggering their corresponding directives. </li> <li> The template is compiled within the context of an NgModule &mdash;the NgModule within which the template's component is declared&mdash; which determines the set of selectors using the following rules: <ul> <li> All selectors of directives listed in `declarations`. </li> <li> All selectors of directives exported from imported NgModules. </li> </ul> </li> </ol> Components, directives, and pipes must belong to *exactly* one module. The compiler emits an error if you try to declare the same class in more than one module. Be careful not to re-declare a class that is imported directly or indirectly from another module. |
| `declarations` | A list of [declarable](/guide/ngmodules/faq#what-is-a-declarable?) classes (*components*, *directives*, and *pipes*) that *belong to this module*. <ol> <li> When compiling a template, you need to determine a set of selectors which should be used for triggering their corresponding directives. </li> <li> The template is compiled within the context of an NgModule —the NgModule within which the template's component is declared— which determines the set of selectors using the following rules: <ul> <li> All selectors of directives listed in `declarations`. </li> <li> All selectors of directives exported from imported NgModules. </li> </ul> </li> </ol> Components, directives, and pipes must belong to *exactly* one module. The compiler emits an error if you try to declare the same class in more than one module. Be careful not to re-declare a class that is imported directly or indirectly from another module. |
| `providers` | A list of dependency-injection providers. <br /> Angular registers these providers with the NgModule's injector. If it is the NgModule used for bootstrapping then it is the root injector. <br /> These services become available for injection into any component, directive, pipe or service which is a child of this injector. <br /> A lazy-loaded module has its own injector which is typically a child of the application root injector. <br /> Lazy-loaded services are scoped to the lazy module's injector. If a lazy-loaded module also provides the `UserService`, any component created within that module's context (such as by router navigation) gets the local instance of the service, not the instance in the root application injector. <br /> Components in external modules continue to receive the instance provided by their injectors. <br /> For more information on injector hierarchy and scoping, see [Providers](/guide/ngmodules/providers) and the [DI Guide](/guide/di). |
| `imports` | A list of modules which should be folded into this module. Folded means it is as if all the imported NgModule's exported properties were declared here. <br /> Specifically, it is as if the list of modules whose exported components, directives, or pipes are referenced by the component templates were declared in this module. <br /> A component template can [reference](/guide/ngmodules/faq#how-does-angular-find-components,-directives,-and-pipes-in-a-template?-what-is-a-template-reference?) another component, directive, or pipe when the reference is declared in this module or if the imported module has exported it. For example, a component can use the `NgIf` and `NgFor` directives only if the module has imported the Angular `CommonModule` (perhaps indirectly by importing `BrowserModule`). <br /> You can import many standard directives from the `CommonModule` but some familiar directives belong to other modules. For example, you can use `[(ngModel)]` only after importing the Angular `FormsModule`. |
| `exports` | A list of declarations &mdash;*component*, *directive*, and *pipe* classes&mdash; that an importing module can use. <br /> Exported declarations are the module's *public API*. A component in another module can use *this* module's `UserComponent` if it imports this module and this module exports `UserComponent`. <br /> Declarations are private by default. If this module does *not* export `UserComponent`, then only the components within *this* module can use `UserComponent`. <br /> Importing a module does *not* automatically re-export the imported module's imports. Module 'B' can't use `ngIf` just because it imported module 'A' which imported `CommonModule`. Module 'B' must import `CommonModule` itself. <br /> A module can list another module among its `exports`, in which case all of that module's public components, directives, and pipes are exported. <br /> [Re-export](/guide/ngmodules/faq#what-should-i-export?) makes module transitivity explicit. If Module 'A' re-exports `CommonModule` and Module 'B' imports Module 'A', Module 'B' components can use `ngIf` even though 'B' itself didn't import `CommonModule`. |
| `exports` | A list of declarations —*component*, *directive*, and *pipe* classes— that an importing module can use. <br /> Exported declarations are the module's *public API*. A component in another module can use *this* module's `UserComponent` if it imports this module and this module exports `UserComponent`. <br /> Declarations are private by default. If this module does *not* export `UserComponent`, then only the components within *this* module can use `UserComponent`. <br /> Importing a module does *not* automatically re-export the imported module's imports. Module 'B' can't use `ngIf` just because it imported module 'A' which imported `CommonModule`. Module 'B' must import `CommonModule` itself. <br /> A module can list another module among its `exports`, in which case all of that module's public components, directives, and pipes are exported. <br /> [Re-export](/guide/ngmodules/faq#what-should-i-export?) makes module transitivity explicit. If Module 'A' re-exports `CommonModule` and Module 'B' imports Module 'A', Module 'B' components can use `ngIf` even though 'B' itself didn't import `CommonModule`. |
| `bootstrap` | A list of components that are automatically bootstrapped. <br /> Usually there's only one component in this list, the *root component* of the application. <br /> Angular can launch with multiple bootstrap components, each with its own location in the host web page. |
## More on NgModules

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