Compare commits

...

110 Commits

Author SHA1 Message Date
Joey Perrott 98de185e53 WIP 2024-05-07 19:21:23 +00:00
Tom Wilkinson 0fb455f72e refactor(core): Move global dispatch behavior into Dispatcher. (#55692)
This behavior is now implemented by calling `dispatch` whether or not
the `action` is populated. The `Dispatcher` then does global dispatch
and early returns if there's no action.

PR Close #55692
2024-05-07 12:00:40 -07:00
Thomas Nguyen 67bb310376 refactor(core): Fix timing of removal of jsaction attribute to be after event replay. (#55696)
This otherwise leads to bugs where, by the time replay needs the attribute, hydration
happens and it's gone.

PR Close #55696
2024-05-07 08:16:16 -07:00
arturovt a9460d08a0 fix(zone.js): remove abort listener on a signal when actual event is removed (#55339)
This commit updates the implementation of the `addEventListener` patcher.

We're currently creating an abort event listener on the signal (when it's provided)
and never remove it. The abort event listener creates a closure which captures `task`
(tasks capture zones and other stuff too) and prevent `task`, zones and signals from
being garbage collected.

We now store the function which removes the abort event listener when the actual event
listener is being removed. The function is stored on task data since task data is already
being used to store different types of information that's necessary to be shared between
`addEventListener` and `removeEventListener`.

Closes #54739

PR Close #55339
2024-05-07 08:07:33 -07:00
Paul Gschwendtner afc057ee92 docs: properly link to developer preview section (#55711)
Fixes an invalid link to the developer preview section.
PR Close #55711
2024-05-07 08:06:42 -07:00
Angular Robot 0fffe5389f build: update all non-major dependencies (#55531)
See associated pull request for more information.

PR Close #55531
2024-05-07 08:05:57 -07:00
AleksanderBodurri 77ae5a391b refactor(devtools): remove git sha stamping from devtools build (#55694)
This stamping is interfering with publishing to the Firefox addons store by brining in the entirety of the `.git` directory as part of the source code necessary for a reproducible build, which Firefox requires as part of it's approval process.

In it's place, we are now using the extension version pulled from the manifest.

PR Close #55694
2024-05-06 16:02:36 -07:00
Andrew Scott 76f2c4fe1a refactor(core): private export token that indicates if zone scheduling is provided (#55690)
This is needed internally to determine whether to provide zone or
zoneless by default.

PR Close #55690
2024-05-06 16:01:48 -07:00
Andrew Scott 9366e170e9 refactor(router): resolve view transition promise in a timeout when unsupported (#55327)
Related to #51131, this change ensures that the router navigation exits
the current event loop before rendering the route when the view transition
feature is enabled, when the browser does not support view transitions.

PR Close #55327
2024-05-06 16:00:53 -07:00
Rahat Ahmed 879537466e refactor(core): Remove unused JSNAMESPACE_SUPPORT from event-dispatch (#55619)
The usage of this option has been removed from google3 code, so we don't
need to keep it around anymore.

PR Close #55619
2024-05-06 14:58:25 -07:00
Andrew Scott 7330b6944d fix(core): TestBed should not override NgZone from initTestEnvironment (#55226)
Prior to this change, `NgZone` was provided by default in TestBed in a
location that would override anything configured in
`TestBed.initTestEnvironment`. This change moves the default `NgZone`
provider to the `RootScopeModule` and these providers can be overridden
by the ones in `additionalModuleTypes`, which are assigned from the
first argument of `initTestEnvironment`. This makes it possible to
configure Zone globally for all tests as opposed to needing to repeat it
in `configureTestingModule` of each suite.

PR Close #55226
2024-05-06 13:33:16 -07:00
Alan Agius 3e1d6e9d6e fix(compiler): maintain multiline CSS selectors during CSS scoping (#55509)
Previously, multiline selectors were being converted into single lines, resulting in sourcemap disruptions due to shifts in line numbers.

Closes #55508

PR Close #55509
2024-05-06 12:39:50 -07:00
Andrew Kushnir 9d3cd9bc83 refactor(animations): make async animations code compatible with Closure compiler (#55686)
Closure compiler optimizations in g3 require `.then` to be present for a dynamic import (or an import should be `await`ed) to detect the set of imported symbols. Currently, the `.then` is located at a later stage in the file, which confuses static code analysis. This change adds the `.then((m) => m)` workaround to satisfy Closure compiler constraints.

PR Close #55686
2024-05-06 12:35:23 -07:00
Alan Agius b3e61ba63f ci: update Renovate configuration: replace matchPackagePatterns with matchDepPatterns (#55683)
This commit addresses a warning in Renovate regarding a deprecated behavior that will be phased out in the future.

```
WARNING: To prevent future issues, replace the usage of matchPackagePatterns with matchDepPatterns (repository=angular/angular, baseBranch=main)
       "packageRule": {
         "matchPackagePatterns": ["^@bazel/.*", "^build_bazel.*"],
         "groupName": "bazel setup",
         "schedule": ["at any time"]
       },
       "packageName": "bazelbuild/rules_nodejs",
       "depName": "build_bazel_rules_nodejs"
```

PR Close #55683
2024-05-06 12:34:09 -07:00
Alan Agius 15e0600521 ci: remove stopUpdatingLabel setting (#55683)
This commit removes the 'stopUpdatingLabel' option from Renovate settings. This option caused pull requests to not automatically rebase when  conflicting.

PR Close #55683
2024-05-06 12:34:09 -07:00
Alan Agius cf84acf44c fix(platform-server): remove event dispatch script from HTML when hydration is disabled (#55681)
Prior to this commit, the included event dispatcher remained in the HTML even when hydration was disabled.

PR Close #55681
2024-05-06 12:33:09 -07:00
Angular Robot 7898957b9d build: lock file maintenance (#55680)
See associated pull request for more information.

PR Close #55680
2024-05-06 12:32:18 -07:00
Alan Agius 73a17904aa build: remove mermaid from deps (#55664)
This dependency is now pulled via `@angular/docs`

PR Close #55664
2024-05-06 12:31:27 -07:00
Alan Agius 9ddb003b6c fix(http): resolve withRequestsMadeViaParent behavior with withFetch (#55652)
This commit addresses dependency injection defects when using the `withFetch` API. Formerly, utilizing `withFetch` led to the automatic setting of `HttpBackend` to `FetchBackend`, which proved problematic in certain scenarios. Notably, conflicts arose when integrating `withRequestsMadeViaParent` and manually overriding tokens, as observed in instances like `InMemoryWebApiModule`.

PR Close #55652
2024-05-06 12:29:53 -07:00
cexbrayat 7a2efd442d fix(migrations): handle more cases in HttpClientModule migration (#55640)
This commit handles two cases that were breaking applications when using the new migration:

- tests using `HttpClientModule` in `TestBed.configureTestingModule` were broken as the import was removed, but the module is still present in the test configuration. It now properly adds `provideHttpClient(withInterceptorsFromDi())` and related imports to the test.
- tests using `HttpClientTestingModule` were migrated to use `provideHttpClient(withInterceptorsFromDi())` but the necessary imports were not added. They are now added by the migration.

PR Close #55640
2024-05-06 12:29:17 -07:00
Andrew Scott 464dae94ee docs: small updates to zoneless docs (#55636)
PR Close #55636
2024-05-06 12:28:36 -07:00
iteriani 8f273ce7a8 refactor(core): Allow the container and the listenable element to be configurable for early event contract. (#55586)
This will allow a multi-app application to listen to early events from different elements and place them
on a separate field on the window.

PR Close #55586
2024-05-06 08:15:54 -07:00
Angular Robot 1872fcd8e0 build: update cross-repo angular dependencies (#55422)
See associated pull request for more information.

PR Close #55422
2024-05-03 13:00:18 -07:00
Alan Agius 6e1e70d3f1 ci: use application builder for integration tests (#55660)
This updates the integration tests to use the application builder.

PR Close #55660
2024-05-03 11:21:06 -07:00
Alan Agius 554a1be51b ci: delate aio-payloads.json (#55659)
This file is no longer used.

PR Close #55659
2024-05-03 11:20:13 -07:00
Alan Agius b80b46215c ci: add adev build as part of the CI process (#55662)
Currently, this is not which caused main to be broken because breakages where not caught during PR

PR Close #55662
2024-05-03 10:20:58 -07:00
Alan Agius b5ab2ef269 build: fix adev build (#55662)
adev is broken at head

PR Close #55662
2024-05-03 10:20:58 -07:00
Alan Agius 46acb55e76 build: update integration tests lock files (#55661)
This should fix CI.

PR Close #55661
2024-05-03 09:17:16 -07:00
Alan Agius aeb3fe26cf build: remove tslint and codelyzer deps from integration tests (#55658)
These are redundant.

PR Close #55658
2024-05-03 08:15:41 -07:00
cexbrayat 14d5094177 docs: typo in RedirectFunction description (#55653)
PR Close #55653
2024-05-03 08:08:12 -07:00
Matthieu Riegler e5331107fd fix(platform-browser): Use the right namespace for mathML. (#55622)
Prior to this change, MathML element were created with the wrong namespace resulting in regular DOM `Element`.

This commit fixes this.

Related to #55608 (but doesn't fix it entirely).

PR Close #55622
2024-05-03 08:07:31 -07:00
Angular Robot 3a930a59f6 build: update dependency typescript to v5.4.5 (#55042)
See associated pull request for more information.

PR Close #55042
2024-05-03 08:06:34 -07:00
Alan Agius 72108bad4d build: update cross-repo angular dependencies (#55654)
See associated pull request for more information.

PR Close #55654
2024-05-03 08:04:40 -07:00
arturovt 9e07b621ea fix(zone.js): add missing APIs to Node.js fs patch (#54396)
This commit updates the list of Node.js `fs` APIs to be patched because
they haven't been updated for a long time. It adds `opendir,lutimes,writev`.
For example, the `opendir` method was added to Node.js in version 12.12.0 in
2019, causing some of the APIs to potentially be always called within the
`<root>` context.

**Note:** There are missing unit tests for these changes because in unit tests,
`fs` is patched by Bazel's Node.js rules and its `node_patches.cjs`. However,
the APIs are successfully patched in the real production code and are called
with the correct context.

PR Close #54396
2024-05-03 08:03:25 -07:00
Doug Parker 58a8f124b0 refactor(devtools): set key in manifest.chrome.json (#55639)
This key comes from the release build of the Chrome extension. Setting it here configures the extension ID of a local build to match the extension ID of the public release. This makes Chrome recognize that local builds are the same extension leading to more intuitive behavior. It's also useful for allowlists of extension IDs to keep the local ID consistent.

PR Close #55639
2024-05-02 14:46:24 -07:00
Andrew Scott 76e7aa8add refactor(core): Update coalescing to just use patched timers in root zone (#55366)
Rather than attempting to use the native timing functions, this commit
simplifies the logic significantly by using the global timer functions
as they are, either patched or unpatched. When Zone is defined, we run
the timers in the root zone. This has more predictable behavior and
timing than (a) using both patched and unpatched versions of timers in
different places (b) trying to get an unpatched timer and failing due to
environment specifics and patches that aren't ZoneJS.

PR Close #55366
2024-05-02 14:43:10 -07:00
Andrew Kushnir 7645325d46 refactor(core): mark @defer APIs as stable (#55625)
This commit removes the `@developerPreview` annotation from the `@defer` APIs, effectively promoting them (and the entire feature!) to stable.

PR Close #55625
2024-05-02 14:42:00 -07:00
Doug Parker b44e35f472 release: bump Angular DevTools to 1.0.15 (#55643)
PR Close #55643
2024-05-02 13:37:34 -07:00
Rahat Ahmed afd87acde4 refactor(core): Turn off JSNAMESPACE_SUPPORT by default (#55598)
All usages in google3 have been deleted so this should be a no-op. Next I'll clean up all the configurations turning this off and then delete the option entirely.

PR Close #55598
2024-05-02 12:34:24 -07:00
twerske c320aa6705 docs: style the nav based on version type (#55628)
PR Close #55628
2024-05-02 12:33:27 -07:00
Kristiyan Kostadinov 2e891ad72a fix(compiler): add math elements to schema (#55631)
Fixes that we didn't have the MathML elements in the schema. Note that we can't discover which tag names are available by looking at globally-available classes, because all MathML elements are `MathMLElement` rather than something like `SVGCircleElement`. As such, I ended up having to hardcode the currently-available tags.

Fixes #55608.

PR Close #55631
2024-05-02 11:12:14 -07:00
cexbrayat 8b5921547c docs: typos in NG0956 (#55630)
PR Close #55630
2024-05-02 11:10:58 -07:00
Angular Robot 45b58059f0 build: update io_bazel_rules_sass digest to 9f8be59 (#55602)
See associated pull request for more information.

PR Close #55602
2024-05-02 11:06:46 -07:00
iteriani 95bf0c85f8 refactor(core): Remove jsaction from element after handling the event. (#55549)
This also adds a test to make sure that the event contract is still listening to other events, especially in the case where we may want partial hydration in the future.

PR Close #55549
2024-05-02 11:04:59 -07:00
Andrew Scott 8cabb7a85d Revert "fix(core): render hooks should not specifically run outside the Angular zone (#55399)" (#55624)
This reverts commit 7e89753eef.

Running render hooks inside the zone is specifically problematic for
`afterRender` hooks. If the callback has async task, it would cause an
infinite change detection. In addition, updating state in render hooks
is generally discourages and certainly should update state in a way that
notifies Angular of the change (either via signal or with `markForCheck`) rather
than relying on ZoneJS to pick it up (which would only work if the
change is done inside an async task).

PR Close #55624
2024-05-02 11:00:29 -07:00
Andrew Scott 7c1b4a49ae fix(core): afterRender hooks registered outside change detection can mark views dirty (#55623)
This commit fixes an error in the looping logic of `ApplicationRef.tick`
when the tick skips straight to render hooks. In this case, if a render
hook makes a state update that requires a view refresh, we would never
actually refresh the view and just loop until we hit the loop limit.

PR Close #55623
2024-05-02 10:58:28 -07:00
Payam Valadkhan 1f2e791c57 refactor(compiler-cli): optimize extra import generation in local compilation mode (#55548)
Currently we add global extra imports to all the files in the compilation unit. However not all the files need extra imports. For example non-Angular files definitely do not need such extra imports, and in some cases these extra imports causes problems as the file is meant to be run the Node but it has Angular dependencies which are not compatible with Node. This change tries to limit extra import generation to a subset of files. Wit hthis change we create extra imports only for the files that contain at least one component whose NgModule is in a different file. This is because all other files do not need extra imports since they are either not Angular files or they already have all the imports that the components need.

PR Close #55548
2024-05-02 10:57:41 -07:00
Kristiyan Kostadinov a0ec2d8915 fix(core): don't schedule timer triggers on the server (#55605)
Fixes that even though we weren't rendering the deferred block the server, we were still triggering the timeout which can delay the response.

Fixes #55475.

PR Close #55605
2024-05-01 15:10:26 -07:00
Emma Twersky fc9602087a docs: update press kit to reduce confusion (#55611)
PR Close #55611
2024-05-01 15:09:33 -07:00
Matthieu Riegler 7ff7dfaee7 docs: fix missing example (#55584)
preview is hidding was responsible for hidding the content.

fixes #55583

PR Close #55584
2024-05-01 15:07:58 -07:00
Angular Robot a33fdfd8da build: update actions/checkout digest to 0ad4b8f (#55537)
See associated pull request for more information.

PR Close #55537
2024-05-01 15:07:18 -07:00
Tom Wilkinson bf25dfae58 refactor(core): Add a boostrapCustomEvent method. (#55603)
This can replace CUSTOM_EVENT_SUPPORT by allowing users to bootstrap
custom event support separately from the `EventContract`.

PR Close #55603
2024-05-01 13:43:27 -07:00
Tom Wilkinson b9a997c467 refactor(core): Remove STOP_PROPAGATION define. (#55582)
This define has been removed at head, and now legacy stop propagation
behavior can be opted into at the LegacyDispatcher level, if necessary.

PR Close #55582
2024-05-01 11:55:31 -07:00
Tom Wilkinson 5903c3752b refactor(core): Move action resolution into a standalone class. (#55523)
This refactor will enable moving this functionality into the dispatcher.

PR Close #55523
2024-05-01 11:54:34 -07:00
Rahat Ahmed cda8bfa8be docs(core): Add event-dispatch README.md (#55521)
PR Close #55521
2024-05-01 11:53:25 -07:00
Andrew Kushnir 379242c910 docs: release notes for the v18.0.0-rc.0 release 2024-05-01 11:50:28 -07:00
Andrew Kushnir 874d33286e docs: release notes for the v17.3.7 release 2024-05-01 11:42:03 -07:00
Doug Parker 7cea897132 release: bump DevTools to 1.0.14 (#55545)
PR Close #55545
2024-04-30 17:01:54 -07:00
Andrew Scott 024e9bf54d refactor(core): Ensure animations are flushed before running render hooks (#55564)
This commit ensures we flush animations by calling renderFactory
begin/end in cases where the ApplicationRef._tick happens in a mode that
skips straight to the render hooks.

PR Close #55564
2024-04-30 15:39:56 -07:00
Doug Parker 3312727aec refactor(zone.js): use Object.prototype.toString directly for improved tree shakability (#55412)
These lines were not tree shakable by Closure Compiler because `.toString()` is special cased as a "pure" function eligible to eliminated if it's return value is unused. However `.toString.call` circumvents this and makes Closure Compiler think the function may have side effects. Switching to `.toString()` should be fine here as `process.toString()` in Node outputs `[object process]` so this should be safe. Presumably the original motivation for this roundabout approach was for type safety reasons which no longer apply as `_global` is `any`.

PR Close #55412
2024-04-30 14:06:43 -07:00
Ben Hong 9f777b692c docs: add README for adev site (#55578)
PR Close #55578
2024-04-30 14:04:56 -07:00
Angular Robot 2ac54f8327 build: lock file maintenance (#55575)
See associated pull request for more information.

PR Close #55575
2024-04-30 14:03:59 -07:00
Andrew Scott 99cf450d38 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:04 -07:00
twerske 73d999f0c7 docs: fix formatting for signal inputs guide (#55275)
PR Close #55275
2024-04-30 14:02:13 -07:00
Serge K Lebedev 7eaa407c22 docs(changelog): remove duplicate mutate removal entry in 17.0.0 (#53471)
PR Close #53471
2024-04-30 14:00:43 -07:00
Andrew Kushnir ff656691c3 release: cut the zone.js-0.14.5 release (#55599)
PR Close #55599
2024-04-30 13:51:16 -07:00
Paul Gschwendtner 0a77825042 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:38 -07:00
Pawel Kozlowski a4a82af865 docs(core): add documentation for errors NG0955 and NG0956 (#55591)
This commit adds detailed description for the errors NG0955 and NG0956.
Those errors correspond to the check introduced in the built-in for loop.

PR Close #55591
2024-04-30 09:21:33 -07:00
Angular Robot 375e9a7c78 build: update scorecard action dependencies (#55589)
See associated pull request for more information.

PR Close #55589
2024-04-30 09:20:06 -07:00
Andrew Scott 66ffeca2de 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:06 -07:00
Joey Perrott fd544159e3 ci: complete migration to prettier formatting (#55580)
Format the remaining unformatted files in the repository

PR Close #55580
2024-04-29 14:00:16 -07:00
Mikal Callahan 49d3062c0e docs(docs-infra): allow file renaming in code editor (#54989)
Any filename but the main.ts is now editable.

PR Close #54989
2024-04-29 13:42:26 -07:00
Matthieu Riegler 9160a21624 docs: add mention of the HTTP client for interceptors (#55551)
fixes #53911

PR Close #55551
2024-04-29 13:41:04 -07:00
Matthieu Riegler 0650981a54 docs: fix broken link to error doc (#55547)
PR Close #55547
2024-04-29 13:40:03 -07:00
ahmadhakeem18 b87a4c40bf docs: update NgClass example description to match the example's behavior (#55209)
Co-authored-by: Sonu Kapoor <sonukapoor@users.noreply.github.com>

PR Close #55209
2024-04-29 13:39:18 -07:00
ahmadhakeem18 4f9084e514 docs: show the difference between pseudo classes angular uses and native ones in runtime (#53819)
PR Close #53819
2024-04-29 13:35:33 -07:00
Doug Parker 292c987791 refactor(compiler): add handler attribute to XMB output (#54865)
This allows tracking of which tools generated which XMB files and helps attribute Angular usage.

PR Close #54865
2024-04-29 11:56:31 -07:00
Joey Perrott 0d78a92431 refactor: migrate compiler-cli to prettier formatting (#55485)
Migrate formatting to prettier for compiler-cli from clang-format

PR Close #55485
2024-04-29 10:25:43 -07:00
Joey Perrott f307e95459 refactor: migrate zone.js to prettier formatting (#55427)
Migrate formatting to prettier for zone.js from clang-format

PR Close #55427
2024-04-29 09:52:05 -07:00
Joey Perrott 31fdf0fbea refactor: migrate core to prettier formatting (#55488)
Migrate formatting to prettier for core from clang-format

PR Close #55488
2024-04-29 09:49:19 -07:00
Andrew Scott be17de53d4 refactor(core): Permit disabling autoDetect for zoneless fixture (#55494)
The caveat here is that this needs to be done before creating the
fixture. There are some technical issues to overcome with disabling it
after it was already enabled, related to detaching from `ApplicationRef`
without other side effects.

PR Close #55494
2024-04-26 09:42:15 -07:00
Kristiyan Kostadinov 91b1f24d03 fix(migrations): resolve multiple structural issues with HttpClient migration (#55557)
Fixes several issues with the `HttpClient` migration that showed up after I tried updating the Material docs site to the latest v18 release. Includes:
* The migration was assuming that all Angular decorators have at least one argument. This led to a null pointer error that broke the v18 update process when I was testing it.
* The migration incorrectly reimplemented the detection of classes with Angular decorators. This can cause code to be migrated incorrectly and doesn't handle cases like import aliases. I've switched it to use the existing tooling for detecting decorated classes.
* The migration was trying to migrate directives, even though they don't support the `imports` field.
* The migration was detecting `TestBed.configureTestingModule` calls using a raw string match which is very fragile and can be broken by the user's formatting.
* There were syntax errors in the unit tests.
* There were type checking errors in the unit tests, for example none of them were importing the Angular decorators that they were migrating.

There's more room for improvement, but this should resolve the most glaring issues without having to rewrite too much.

PR Close #55557
2024-04-26 09:32:31 -07:00
Matthieu Riegler 4a7402f75d docs: update ChangeDetectionStrategy links (#55553)
fixes #54227
fixes #50943

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

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

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

PR Close #55550
2024-04-26 09:29:33 -07:00
iteriani 5b4970bbaa refactor(platform-server): Update event_replay_spec to more match production code. (#55517)
Notably, do evals where in SSR we evaluate a contract binary and evaluate scripts.

PR Close #55517
2024-04-25 14:58:11 -07:00
Matthieu Riegler 96972b45ec 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:19 -07:00
Matthieu Riegler e0096ef945 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:18 -07:00
Matthieu Riegler 54de0f3f0b 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:18 -07:00
Matthieu Riegler 8a6027de86 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:18 -07:00
Andrew Scott 356ec6508b refactor(core): Do not duplicate change detection with run coalescing (part 2) (#55403)
This commit prevents doubling change detections when the zoneless
scheduler is notified first, followed by the zone becomeing unstable
(effectively "scheduling" zone-based change detection). When run
coalescing is enabled, this would otherwise result in the zoneless
scheduler running change detection first and then change detection
running again because of the run coalescing since both scheduler use the
same timing function (and then it would be FIFO).

PR Close #55403
2024-04-25 14:31:17 -07:00
Andrew Scott bf8814c6c3 refactor(core): Omit listeners from out-of-zone scheduling when using ZoneJS (#55492) (#55525)
In Angular today, a bound listener automatically marks the view for
check. When using ZoneJS, these listeners are most often executed in the
Angular Zone as well, so synchronization (`ApplicationRef.tick`) will
eventually happen. _However_, developers can opt out of zone-patching
for events in several ways, and often do this for very frequent
listeners like `mousemove`, `resize`, and `scroll`. We do not want to
break existing expectations that these are now "safe" events to have
listeners for by automatically scheduling change detection regardless of
whether the listener executed inside or outside the Angular zone.

In contrast, in order for developers to more easily transition to zoneless,
we need to be able to ensure that components which are using `OnPush`
are, for the most part, compatible with zoneless as well. Because listeners
automatically mark the component for check, developers using `OnPush`
did not/do not need to also call `ChangeDetectorRef.markForCheck` or a
similar API. Unfortunately, this means that we need to consider the
listener callbacks as a notification to schedule a `tick` when Zoneless
is enabled. In the future, we would like to have an opt-out for this
(i.e. signal components) since it's not really how we _want_ things to work.

Also includes the fix for #54919 that got reverted only because it was
easier to revert the set of conflicting commits

PR Close #55525
2024-04-25 12:56:37 -07:00
Kirk Larkin 0dc4df8296 docs: Update di-in-action.md (#55489)
PR Close #55489
2024-04-25 12:55:13 -07:00
Alan Agius 2bd166537b 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:21 -07:00
Kirk Larkin f4f1d32f7e docs: Update what-is-angular.md (#55536)
PR Close #55536
2024-04-25 12:52:30 -07:00
Matthieu Riegler 5926bf452d 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:41 -07:00
Matthieu Riegler ae7af8d23b docs: fix the file naming for the NG05000 error (#55519)
PR Close #55519
2024-04-25 12:49:51 -07:00
Andrew Scott 5a52facef7 docs: Add some zoneless documentation (#55500)
Probably needs more work, but it's a start

PR Close #55500
2024-04-25 12:49:16 -07:00
Tom Wilkinson f1d6f867b9 refactor(core): Remove A11Y_SUPPORT_IN_DISPATCHER behavior. (#55497)
The only use case internally has been removed and this can be
accomplished with A11Y_CLICK_SUPPORT_DEFERRED.

PR Close #55497
2024-04-25 10:57:58 -07:00
Andrew Scott 7e89753eef 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:34 -07:00
Kristiyan Kostadinov 97eea8d50e fix(core): resolve error for multiple component instances that use fallback content (#55478)
Currently fallback content for `ng-content` gets declared and rendered out in one go. This breaks down if multiple instances of the same component are used where one doesn't render the fallback content while the other one does, because the `TNode` for the content has to be created during the first creation pass.

These changes resolve the issue by always _declaring_ the template, but only rendering it if the slot is empty.

Fixes #55466.

PR Close #55478
2024-04-25 09:04:01 -07:00
ice-blaze e54c950e92 refactor(forms): replace errors with hasError() (#55379)
Material documentation use hasError. For homogeneity reason, this commit
will adopt material style.

Fixes #55378

PR Close #55379
2024-04-25 08:57:29 -07:00
Kirk Larkin 03934f674e docs: Update lightweight-injection-tokens.md (#55490)
PR Close #55490
2024-04-25 08:55:46 -07:00
yuichi.imori e723635058 docs: Correct code references in Tutorial 01. (#55506)
PR Close #55506
2024-04-25 08:55:07 -07:00
Ben Hong a45a640474 docs: update security guide link references (#55514)
PR Close #55514
2024-04-25 08:54:31 -07:00
Ben Hong 0b53fdb3b4 fix(docs-infra): add cookie consent gtag event default state (#54574)
This PR sets a default state for cookie consent of 'denied'. The other part of the PR exists in the angular/dev-infra repo which will grant permission when the user accepts the cookie banner.

PR Close #54574
2024-04-25 08:53:06 -07:00
Pawel Kozlowski 90389add7a fix(core): hide implementation details of ExperimentalPendingTasks (#55516)
The ExperimentalPendingTasks service was accidently exposing one of its
internal fields as a public one. This commit fixes this by marking the
field in question as private.

PR Close #55516
2024-04-25 08:51:01 -07:00
Andrew Kushnir 3667352ce5 docs: release notes for the v18.0.0-next.6 release 2024-04-25 08:37:59 -07:00
Andrew Kushnir 4cec9fc67a release: bump the next branch to v18.1.0-next.0 2024-04-25 08:37:59 -07:00
1487 changed files with 151984 additions and 125265 deletions
@@ -8,7 +8,6 @@
"devondcarew.bazel-code",
"gkalpak.aio-docs-utils",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
//"angular.ng-template",
//"dbaeumer.vscode-eslint",
+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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build //adev:build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
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@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # 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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -13,9 +13,9 @@ jobs:
assistant_to_the_branch_manager:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: angular/dev-infra/github-actions/branch-manager@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
id: comment-branch
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
@@ -38,7 +38,7 @@ jobs:
- uses: ./.github/actions/yarn-install
- uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
bazelrc: ./.bazelrc.user
+9 -188
View File
@@ -5,7 +5,7 @@ on:
branches:
- main
- '[0-9]+.[0-9]+.x'
- 'remove-aio-stuff'
- 'browser-update-test'
pull_request:
types: [opened, synchronize, reopened]
@@ -20,155 +20,6 @@ defaults:
shell: bash
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./aio/node_modules
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Check code lint
run: yarn -s tslint
- name: Check for circular dependencies
run: yarn -s ts-circular-deps:check
- name: Validate pull approve configuration
run: yarn -s ng-dev pullapprove verify
- name: Validate angular robot configuration
run: yarn -s ng-dev ngbot verify
- name: Confirm code builds with typescript as expected
run: yarn -s check-tooling-setup
- name: Check commit message
# Commit message validation is only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev commit-message validate-range ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
- name: Check code format
# Code formatting checks are only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
devtools:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run unit tests
run: yarn devtools:test
- name: Test build
run: yarn devtools:build:chrome
test:
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
run: yarn test:ci
adev:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel test //adev:test
publish-snapshots:
if: github.event_name == 'push'
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
- run: yarn build
- run: ./scripts/ci/publish-build-artifacts.sh
zone-js:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile
- run: |
yarn bazel build \
//packages/zone.js/bundles:zone.umd.js \
//packages/zone.js:npm_package \
//packages/zone.js/test/closure:closure_js \
- run: |
rm -Rf packages/zone.js/build
rm -Rf packages/zone.js/test/extra/*.umd.js
mkdir -p packages/zone.js/build/
mkdir -p packages/zone.js/build/test/
cp dist/bin/packages/zone.js/bundles/zone.umd.js packages/zone.js/build/zone.umd.js
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/test/closure/zone.closure.mjs ./packages/zone.js/build/test/zone.closure.mjs
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisefinallytest
- run: yarn --cwd packages/zone.js jest:test
- run: yarn --cwd packages/zone.js jest:nodetest
- run: yarn --cwd packages/zone.js electrontest
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
- run: yarn --cwd packages/zone.js/test/typings test
bazel-saucelabs:
# TODO: Replace legacy job with Bazel saucelabs job.
if: github.event_name == 'push'
@@ -177,7 +28,7 @@ jobs:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
# Checking out the pull request commit is intended here as we need to run the changed code tests.
@@ -185,11 +36,11 @@ jobs:
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/saucelabs@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Set up Sauce Tunnel Daemon
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
@@ -205,15 +56,15 @@ jobs:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/saucelabs@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- 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
@@ -245,34 +96,4 @@ jobs:
- 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'
env:
DEPLOY_PROJECT: angular-dev-site
DEPLOY_SITE: angular-dev-site
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build //adev:build
- name: Move generated project files into deployment directory
run: cp -r dist/bin/adev/build adev/build
- name: Setup firebase context
run: |
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs ${{ env.DEPLOY_SITE }}
- name: Deploy to firebase
uses: FirebaseExtended/action-hosting-deploy@120e124148ab7016bec2374e5050f15051255ba2 # v0.7.1
with:
entryPoint: './adev'
firebaseServiceAccount: ${{ secrets.ANGULAR_DEV_SITE_DEPLOY }}
projectId: ${{ env.DEPLOY_PROJECT }}
channelId: live
+4 -4
View File
@@ -12,14 +12,14 @@ jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: angular/dev-infra/github-actions/commit-message-based-labels@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/commit-message-based-labels@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: angular/dev-infra/github-actions/post-approval-changes@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/post-approval-changes@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -13,8 +13,8 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: angular/dev-infra/github-actions/google-internal-tests@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/google-internal-tests@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
- uses: angular/dev-infra/github-actions/unified-status-check@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
node-module-directories: |
./aio/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- 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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
node-module-directories: |
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
@@ -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@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2
uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3
with:
sarif_file: results.sarif
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
@@ -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@f122b83d2a49e24eb9dfcd54fca9fc97612b832d
uses: angular/dev-infra/github-actions/create-pr-for-changes@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
branch-prefix: update-cli-help
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
+5 -73
View File
@@ -7,32 +7,7 @@ export const format: FormatConfig = {
'prettier': {
'matchers': [
'**/*.{yaml,yml}',
'adev/**/*.{js,ts}',
'devtools/**/*.{js,ts}',
'integration/**/*.{js,ts}',
'tools/**/*.{js,ts}',
'modules/**/*.{js,ts}',
'scripts/**/*.{js,ts}',
'packages/animations/**/*.{js,ts}',
'packages/bazel/**/*.{js,ts}',
'packages/benchpress/**/*.{js,ts}',
'packages/common/**/*.{js,ts}',
'packages/compiler/**/*.{js,ts}',
'packages/core/primitives/**/*.{js,ts}',
'packages/docs/**/*.{js,ts}',
'packages/elements/**/*.{js,ts}',
'packages/examples/**/*.{js,ts}',
'packages/forms/**/*.{js,ts}',
'packages/language-service/**/*.{js,ts}',
'packages/localize/**/*.{js,ts}',
'packages/platform-browser/**/*.{js,ts}',
'packages/platform-browser-dynamic/**/*.{js,ts}',
'packages/platform-server/**/*.{js,ts}',
'packages/misc/**/*.{js,ts}',
'packages/private/**/*.{js,ts}',
'packages/router/**/*.{js,ts}',
'packages/service-worker/**/*.{js,ts}',
'packages/upgrade/**/*.{js,ts}',
'**/*.{js,ts}',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
@@ -40,58 +15,15 @@ export const format: FormatConfig = {
// not be modified.
'!third_party/**',
'!.yarn/**',
],
},
'clang-format': {
'matchers': [
'**/*.{js,ts}',
// TODO: burn down format failures and remove aio and integration exceptions.
'!aio/**',
'!integration/**',
// Both third_party and .yarn are directories containing copied code which should
// not be modified.
'!third_party/**',
'!.yarn/**',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
// Do not format generated ng-dev script
'!dev-infra/ng-dev.js',
'!dev-infra/build-worker.js',
// Do not format compliance test-cases since they must match generated code
'!packages/compiler-cli/test/compliance/test_cases/**/*.js',
// Do not format the locale files which are checked-in for Google3, but generated using
// the `generate-locales-tool` from `packages/common/locales`.
'!packages/core/src/i18n/locale_en.ts',
'!packages/common/locales/closure-locale.ts',
'!packages/common/src/i18n/currencies.ts',
// Temporarily disable formatting for adev
'!adev/**',
// Migrated to prettier
'!devtools/**/*.{js,ts}',
'!tools/**/*.{js,ts}',
'!modules/**/*.{js,ts}',
'!scripts/**/*.{js,ts}',
'!packages/animations/**/*.{js,ts}',
'!packages/bazel/**/*.{js,ts}',
'!packages/benchpress/**/*.{js,ts}',
'!packages/common/**/*.{js,ts}',
'!packages/compiler/**/*.{js,ts}',
'!packages/core/primitives/**/*.{js,ts}',
'!packages/docs/**/*.{js,ts}',
'!packages/elements/**/*.{js,ts}',
'!packages/examples/**/*.{js,ts}',
'!packages/forms/**/*.{js,ts}',
'!packages/language-service/**/*.{js,ts}',
'!packages/localize/**/*.{js,ts}',
'!packages/platform-browser/**/*.{js,ts}',
'!packages/platform-browser-dynamic/**/*.{js,ts}',
'!packages/platform-server/**/*.{js,ts}',
'!packages/misc/**/*.{js,ts}',
'!packages/private/**/*.{js,ts}',
'!packages/router/**/*.{js,ts}',
'!packages/service-worker/**/*.{js,ts}',
'!packages/upgrade/**/*.{js,ts}',
// Test cases contain non valid code.
'!packages/compiler-cli/test/compliance/test_cases/**/*.{js,ts}',
// Ignore AIO as its pending removal.
'!aio/**/*.{js,ts}',
],
},
'buildifier': true,
-1
View File
@@ -6,6 +6,5 @@
"recommendations": [
"BazelBuild.vscode-bazel",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
],
}
-6
View File
@@ -1,16 +1,10 @@
{
// Format js and ts files on save with `clang-format.executable`
// If `clang-format.executable` is not being used, these two settings should be removed otherwise it will break existing formatting.
// You can instead run `yarn gulp format` to manually format your code.
"[javascript]": {
"editor.formatOnSave": true,
},
"[typescript]": {
"editor.formatOnSave": true,
},
// Please install https://marketplace.visualstudio.com/items?itemName=xaver.clang-format to take advantage of `clang-format` in VSCode.
// (See https://clang.llvm.org/docs/ClangFormat.html for more info `clang-format`.)
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
// Exclude third party modules and build artifacts from the editor watchers/searches.
"files.watcherExclude": {
"**/.git/objects/**": true,
+108 -15
View File
@@ -1,3 +1,111 @@
<a name="18.0.0-rc.0"></a>
# 18.0.0-rc.0 (2024-05-01)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [44c0ed83a6](https://github.com/angular/angular/commit/44c0ed83a6499fa96f65a27bc5c926579c06b6d2) | fix | hide implementation details of ExperimentalPendingTasks ([#55516](https://github.com/angular/angular/pull/55516)) |
| [a177abef1b](https://github.com/angular/angular/commit/a177abef1b0a0edc46dcca2d8c5c67669c55ff8f) | fix | render hooks should not specifically run outside the Angular zone ([#55399](https://github.com/angular/angular/pull/55399)) |
| [a5c57c7484](https://github.com/angular/angular/commit/a5c57c7484f1dc3afab4ece4e969a4a7308cdeca) | fix | resolve error for multiple component instances that use fallback content ([#55478](https://github.com/angular/angular/pull/55478)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [f93e5180be](https://github.com/angular/angular/commit/f93e5180be1e20a59ff68f12853653f4f3282846) | fix | resolve multiple structural issues with HttpClient migration ([#55557](https://github.com/angular/angular/pull/55557)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [aefee87074](https://github.com/angular/angular/commit/aefee87074f4a5b2531f989a1876ace6e6616da4) | 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.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="18.0.0-next.6"></a>
# 18.0.0-next.6 (2024-04-25)
## Breaking Changes
### animations
- Deprecated `matchesElement` method has been removed from `AnimationDriver` as it is unused.
### core
- `async` has been removed, use `waitForAsync` instead.
### http
- By default we now prevent caching of HTTP requests that require authorization . To opt-out from this behaviour use the `includeRequestsWithAuthHeaders` option in `withHttpTransferCache`.
Example:
```ts
withHttpTransferCache({
includeRequestsWithAuthHeaders: true,
})
```
### platform-browser
- Deprecated `StateKey`, `TransferState` and `makeStateKey` have been removed from `@angular/platform-browser`, use the same APIs from `@angular/core`.
## Deprecations
### common
- `getCurrencySymbol`, `getLocaleCurrencyCode`, `getLocaleCurrencyName`, `getLocaleCurrencySymbol`, `getLocaleDateFormat`, `getLocaleDateTimeFormat`, `getLocaleDayNames`, `getLocaleDayPeriods`, `getLocaleDirection`, `getLocaleEraNames`, `getLocaleExtraDayPeriodRules`, `getLocaleExtraDayPeriods`, `getLocaleFirstDayOfWeek`, `getLocaleId`, `getLocaleMonthNames`, `getLocaleNumberFormat`, `getLocaleNumberSymbol`, `getLocalePluralCase`, `getLocaleTimeFormat`, `getLocaleWeekEndRange`, `getNumberOfCurrencyDigits`
### http
- `HttpClientModule`, `HttpClientXsrfModule` and `HttpClientJsonpModule`
As mentionned, those modules can be replaced by provider function only.
### animations
| Commit | Type | Description |
| -- | -- | -- |
| [bcce85af72](https://github.com/angular/angular/commit/bcce85af72a82634f60b31d66a5ef42ecd844ce8) | refactor | remove deprecated `matchesElement` from `AnimationDriver` ([#55479](https://github.com/angular/angular/pull/55479)) |
### common
| Commit | Type | Description |
| -- | -- | -- |
| [d34c033902](https://github.com/angular/angular/commit/d34c033902b4e7543796de6c57d0cfd09093eb43) | refactor | Deprecate Local Data API functions ([#54483](https://github.com/angular/angular/pull/54483)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [7a16d7e969](https://github.com/angular/angular/commit/7a16d7e969eaf5a9475ffdd21a4bf637ce523856) | fix | don't type check the bodies of control flow nodes in basic mode ([#55360](https://github.com/angular/angular/pull/55360)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [a730f09ae9](https://github.com/angular/angular/commit/a730f09ae9e729da79a3e0951e15e0139ef67713) | feat | Add a public API to establish events to be replayed and an attribute to mark an element with an event handler. ([#55356](https://github.com/angular/angular/pull/55356)) |
| [5f06ca8f55](https://github.com/angular/angular/commit/5f06ca8f5539ed208bae0b110887b5538ac4041f) | feat | add HOST_TAG_NAME token ([#54751](https://github.com/angular/angular/pull/54751)) |
| [ac863ded48](https://github.com/angular/angular/commit/ac863ded4818af3426ef5888c706a2bd8c79c0be) | feat | provide ExperimentalPendingTasks API ([#55487](https://github.com/angular/angular/pull/55487)) |
| [5a10f405d3](https://github.com/angular/angular/commit/5a10f405d315a28b9a000c669e9b1cb3fa24a7f1) | fix | complete the removal of deprecation `async` function ([#55491](https://github.com/angular/angular/pull/55491)) |
| [c175bca364](https://github.com/angular/angular/commit/c175bca36432b26851adb4ea9d850c2d07fba864) | fix | DeferBlockFixture.render should not wait for stability ([#55271](https://github.com/angular/angular/pull/55271)) |
| [9894278e71](https://github.com/angular/angular/commit/9894278e712a50079af87898a63e1d19a462d015) | fix | make `ActivatedRoute` inject correct instance inside `@defer` blocks ([#55374](https://github.com/angular/angular/pull/55374)) |
| [5948193e13](https://github.com/angular/angular/commit/5948193e1368eec86647b4c1c0b337a387b27087) | fix | skip defer timers on the server ([#55480](https://github.com/angular/angular/pull/55480)) |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [6f88d80758](https://github.com/angular/angular/commit/6f88d8075895bd80592b1b7e0fba8202a58a5417) | feat | allow caching requests with different origins between server and client ([#55274](https://github.com/angular/angular/pull/55274)) |
| [8eacb6e4b9](https://github.com/angular/angular/commit/8eacb6e4b982a5aa23cfbf9078dc4e19d9466d73) | feat | exclude caching for authenticated HTTP requests ([#55034](https://github.com/angular/angular/pull/55034)) |
| [ef665a40a5](https://github.com/angular/angular/commit/ef665a40a580ff4df79617084ac83738f28ae924) | refactor | Deprecate `HttpClientModule` & related modules ([#54020](https://github.com/angular/angular/pull/54020)) |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [f914f6a362](https://github.com/angular/angular/commit/f914f6a3628847c06cbdde9c90cd417fb2f4c61f) | feat | Migration schematics for `HttpClientModule` ([#54020](https://github.com/angular/angular/pull/54020)) |
### platform-browser
| Commit | Type | Description |
| -- | -- | -- |
| [cba336d4f1](https://github.com/angular/angular/commit/cba336d4f1badd601b24a58fc51bde995f45682d) | refactor | remove deprecated transfer state APIs ([#55474](https://github.com/angular/angular/pull/55474)) |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [5674c644ab](https://github.com/angular/angular/commit/5674c644abf51ae8764befd3011742ff1febdf29) | fix | add `nonce` attribute to event record script ([#55495](https://github.com/angular/angular/pull/55495)) |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [4a42961393](https://github.com/angular/angular/commit/4a42961393b3abf40f34374df059d3959dadecc0) | feat | `withNavigationErrorHandler` can convert errors to redirects ([#55370](https://github.com/angular/angular/pull/55370)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="17.3.6"></a>
# 17.3.6 (2024-04-25)
### core
@@ -970,21 +1078,6 @@ Live long and prosper 🖖🏻
Example after:
```typescript
items.update(itemsArray => [itemsArray, …newItem]);
```
- The `mutate` method was removed from the `WritableSignal` interface and completely
dropped from the public API surface. As an alternative please use the update method and
make immutable changes to the object.
Example before:
```typescript
items.mutate(itemsArray => itemsArray.push(newItem));
```
Example after:
```typescript
items.update(itemsArray => [itemsArray, …newItem]);
```
+1 -1
View File
@@ -207,7 +207,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
* All public API methods **must be documented**.
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#formatting-your-source-code).
## <a name="commit"></a> Commit Message Format
+1 -16
View File
@@ -63,22 +63,7 @@ Get started with Angular, learn the fundamentals and explore advanced topics on
### Local Development
To contribute to Angular docs, you can setup a local environment with the following commands:
```bash
# Clone Angular repo
git clone https://github.com/angular/angular.git
# Navigate to project directory
cd angular
# Install dependencies
yarn
# Build and run local dev server
# Note: Initial build will take some time
yarn docs
```
To contribute to the Angular Docs, check out the [Angular.dev README](adev/README.md)
## Development Setup
+4 -4
View File
@@ -75,7 +75,7 @@ yarn_install(
"//:.yarnrc",
"//:tools/npm-patches/@bazel+jasmine+5.8.1.patch",
"//tools:postinstall-patches.js",
"//tools/esm-interop:patches/npm/@angular+build-tooling+0.0.0-e0ec7b60641d7f6369be45d8d02663fd50f320be.patch",
"//tools/esm-interop:patches/npm/@angular+build-tooling+0.0.0-56d0b1f597b9e4b5d99f289f6904a220802164c1.patch",
"//tools/esm-interop:patches/npm/@bazel+concatjs+5.8.1.patch",
"//tools/esm-interop:patches/npm/@bazel+esbuild+5.7.1.patch",
"//tools/esm-interop:patches/npm/@bazel+protractor+5.7.1.patch",
@@ -167,10 +167,10 @@ cldr_xml_data_repository(
# sass rules
http_archive(
name = "io_bazel_rules_sass",
sha256 = "ca27b3dcd294b134ccc9d0a1c6b63b810e115204e87a2b3d4cc247f5676d0a85",
strip_prefix = "rules_sass-c3f163fd9f570846c2aae4bf16ac69b4217e6f3b",
sha256 = "845061fad5cb42a353ac394fe6450ec9f21d337acf5b98011ef29ddcb33e3671",
strip_prefix = "rules_sass-9f8be59f5175d0dd3438d7da994b2782299e0c2c",
urls = [
"https://github.com/bazelbuild/rules_sass/archive/c3f163fd9f570846c2aae4bf16ac69b4217e6f3b.zip",
"https://github.com/bazelbuild/rules_sass/archive/9f8be59f5175d0dd3438d7da994b2782299e0c2c.zip",
],
)
+4
View File
@@ -42,6 +42,7 @@ APPLICATION_ASSETS = [
]
APPLICATION_DEPS = [
"@npm//@angular/build",
"@npm//@angular-devkit/build-angular",
"@npm//@angular/animations",
"@npm//@angular/cdk",
@@ -78,8 +79,11 @@ APPLICATION_DEPS = [
"@npm//@lezer/javascript",
"@npm//@lezer/common",
"@npm//@xterm/xterm",
"@npm//algoliasearch",
"@npm//xterm",
"@npm//xterm-addon-fit",
"@npm//angular-split",
"@npm//zone.js",
]
TEST_DEPS = APPLICATION_DEPS + [
+37
View File
@@ -0,0 +1,37 @@
# [Angular.dev](https://www.angular.dev)
This site is built with Angular.
The content is written primarly in Markdown format located in `src/content`. For simple edits, you can directly edit the file on GitHub and generate a Pull Request.
## Local Development
For local development, [yarn](https://yarnpkg.com/) is the preferred package manager. You can setup a local environment with the following commands
:
```bash
# Clone Angular repo
git clone https://github.com/angular/angular.git
# Navigate to project directory
cd angular
# Install dependencies
yarn
# Build and run local dev server
# Note: Initial build will take some time
yarn docs
```
## Contributing
Want to report a bug, contribute some code, or improve the documentation? Excellent!
Read through our [contributing guidelines](CONTRIBUTING.md) to learn about our submission process, coding rules, and more.
And if you're new, check out one of our issues labeled as <kbd>[help wanted](https://github.com/angular/angular/labels/help%20wanted)</kbd> or <kbd>[good first issue](https://github.com/angular/angular/labels/good%20first%20issue)</kbd>.
### Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).
+1 -1
View File
@@ -17,7 +17,7 @@
"prefix": "adev",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"builder": "@angular/build:application",
"options": {
"externalDependencies": ["path"],
"outputPath": "dist/angular-dev",
@@ -61,6 +61,9 @@
<nav
class="adev-nav-primary docs-scroll-hide"
[class.adev-nav-primary--open]="isMobileNavigationOpened()"
[class.adev-nav-primary--rc]="currentDocsVersionMode === 'rc'"
[class.adev-nav-primary--next]="currentDocsVersionMode === 'next'"
[class.adev-nav-primary--deprecated]="currentDocsVersionMode === 'deprecated'"
>
<button
type="button"
@@ -122,7 +125,6 @@
</clipPath>
</defs>
</svg>
<div class="adev-beta-version">Beta Docs</div>
</a>
<!-- Version picker for v18+ -->
@@ -69,6 +69,27 @@
padding-block-end: 2rem;
box-sizing: border-box;
&.adev-nav-primary--rc {
// change nav background to indicate this is the rc docs
background: linear-gradient(
140deg,
color-mix(in srgb, var(--orange-red), transparent 60%) 0%,
color-mix(in srgb, var(--vivid-pink), transparent 40%) 15%,
color-mix(in srgb, var(--electric-violet), transparent 70%) 25%,
color-mix(in srgb, var(--bright-blue), transparent 60%) 90%,
);
}
&.adev-nav-primary--next {
// change nav background to indicate this is the rc docs
background-color: color-mix(in srgb, var(--orange-red), transparent 60%);
}
&.adev-nav-primary--deprecated {
// change nav background to indicate this is an outdated version of the docs
background-color: color-mix(in srgb, var(--symbolic-gray), transparent 30%);
}
// div containing mobile close button and adev-nav_top
> div {
display: flex;
@@ -129,13 +150,6 @@
flex-direction: row;
}
.adev-beta-version {
font-family: 'Material Symbols Outlined';
text-transform: uppercase;
text-align: center;
line-height: normal;
}
// version dropdown button
.adev-version-button {
border: 1px solid var(--senary-contrast);
@@ -102,6 +102,8 @@ export class Navigation implements OnInit {
openedMenu: MenuType | null = null;
currentDocsVersion = this.versionManager.currentDocsVersion;
currentDocsVersionMode = this.versionManager.currentDocsVersion()?.version;
// Set the values of the search label and title only on the client, because the label is user-agent specific.
searchLabel = this.isBrowser
? isApple
@@ -7,8 +7,9 @@
*/
import {Injector} from '@angular/core';
import {ENVIRONMENT, WINDOW} from '@angular/docs';
import {ENVIRONMENT, WINDOW, LOCAL_STORAGE} from '@angular/docs';
import {AnalyticsService} from './analytics.service';
import {MockLocalStorage} from '@angular/docs/testing';
describe('AnalyticsService', () => {
let service: AnalyticsService;
@@ -18,6 +19,7 @@ describe('AnalyticsService', () => {
let windowOnErrorHandler: (event: ErrorEvent) => void;
let mockWindow: any;
let mockLocalStorage = new MockLocalStorage();
beforeEach(() => {
gtagSpy = jasmine.createSpy('gtag');
@@ -39,6 +41,7 @@ describe('AnalyticsService', () => {
{provide: ENVIRONMENT, useValue: {}},
{provide: AnalyticsService, deps: [WINDOW]},
{provide: WINDOW, useFactory: () => mockWindow, deps: []},
{provide: LOCAL_STORAGE, useValue: mockLocalStorage},
],
});
@@ -8,7 +8,7 @@
import {inject, Injectable} from '@angular/core';
import {WINDOW, ENVIRONMENT} from '@angular/docs';
import {WINDOW, ENVIRONMENT, LOCAL_STORAGE, STORAGE_KEY, setCookieConsent} from '@angular/docs';
import {formatErrorEventForAnalytics} from './analytics-format-error';
@@ -28,6 +28,7 @@ interface WindowWithAnalytics extends Window {
export class AnalyticsService {
private environment = inject(ENVIRONMENT);
private window: WindowWithAnalytics = inject(WINDOW);
private readonly localStorage = inject(LOCAL_STORAGE);
constructor() {
this._installGlobalSiteTag();
@@ -64,6 +65,21 @@ export class AnalyticsService {
window.gtag = function () {
window.dataLayer?.push(arguments);
};
// Cookie banner consent initial state
// This code is modified in the @angular/docs package in the cookie-popup component.
// Docs: https://developers.google.com/tag-platform/security/guides/consent
if (this.localStorage) {
if (this.localStorage.getItem(STORAGE_KEY) === 'true') {
setCookieConsent('granted');
} else {
setCookieConsent('denied');
}
} else {
// In case localStorage is not available, we default to denying cookies.
setCookieConsent('denied');
}
window.gtag('js', new Date());
// Configure properties before loading the script. This is necessary to avoid
@@ -15,7 +15,7 @@ export interface Version {
url: string;
}
export type VersionMode = 'stable' | 'rc' | 'next' | number;
export type VersionMode = 'stable' | 'deprecated' | 'rc' | 'next' | number;
export const INITIAL_DOCS_VERSION = 17;
export const VERSION_PATTERN_PLACEHOLDER = '{{version}}';
@@ -10,7 +10,29 @@
@for (file of files(); track file) {
<mat-tab #tab>
<ng-template mat-tab-label>
@if (tab.isActive && isRenamingFile()) {
<form
(submit)="renameFile($event, file.filename)"
(docsClickOutside)="closeRenameFile()">
<input
name="rename-file"
class="adev-rename-file-input"
#renameFileInput
(keydown)="$event.stopPropagation()"
/>
</form>
} @else {
{{ file.filename.replace('src/', '') }}
}
@if (tab.isActive && canRenameFile(file.filename)) {
<button
class="docs-rename-file"
aria-label="rename file"
(click)="onRenameButtonClick()"
>
<docs-icon>edit</docs-icon>
</button>
}
@if (tab.isActive && canDeleteFile(file.filename)) {
<button
class="docs-delete-file"
@@ -79,7 +79,8 @@
}
.adev-add-file,
.docs-delete-file {
.docs-delete-file,
.docs-rename-file {
docs-icon {
color: var(--gray-400);
transition: color 0.3s ease;
@@ -92,7 +93,8 @@
}
}
.docs-delete-file {
.docs-delete-file,
.docs-rename-file {
padding-inline-start: 0.1rem;
padding-inline-end: 0;
margin-block-start: 0.2rem;
@@ -101,7 +103,8 @@
}
}
.adev-new-file-input {
.adev-new-file-input,
.adev-rename-file-input {
color: var(--primary-contrast);
border: none;
border-radius: 0;
@@ -107,7 +107,7 @@ describe('CodeEditor', () => {
const tabs = await matTabGroup.getTabs();
const expectedLabels = files.map((file, index) => {
const label = file.filename.replace('src/', '');
if (index === 0) return `${label} delete`;
if (index === 0) return `${label} editdelete`;
return label;
});
@@ -28,9 +28,13 @@ import {EmbeddedTutorialManager} from '../embedded-tutorial-manager.service';
import {CodeMirrorEditor} from './code-mirror-editor.service';
import {DiagnosticWithLocation, DiagnosticsState} from './services/diagnostics-state.service';
import {DownloadManager} from '../download-manager.service';
import {IconComponent} from '@angular/docs';
import {ClickOutside, IconComponent} from '@angular/docs';
export const REQUIRED_FILES = new Set(['src/main.ts', 'src/index.html']);
export const REQUIRED_FILES = new Set([
'src/main.ts',
'src/index.html',
'src/app/app.component.ts',
]);
@Component({
selector: 'docs-tutorial-code-editor',
@@ -38,7 +42,7 @@ export const REQUIRED_FILES = new Set(['src/main.ts', 'src/index.html']);
templateUrl: './code-editor.component.html',
styleUrls: ['./code-editor.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIf, NgFor, MatTabsModule, IconComponent],
imports: [NgIf, NgFor, MatTabsModule, IconComponent, ClickOutside],
})
export class CodeEditor implements AfterViewInit, OnDestroy {
@ViewChild('codeEditorWrapper') private codeEditorWrapperRef!: ElementRef<HTMLDivElement>;
@@ -54,6 +58,16 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
}
}
private renameFileInputRef?: ElementRef<HTMLInputElement>;
@ViewChild('renameFileInput') protected set setRenameFileInputRef(
element: ElementRef<HTMLInputElement>,
) {
if (element) {
element.nativeElement.focus();
this.renameFileInputRef = element;
}
}
private readonly destroyRef = inject(DestroyRef);
private readonly codeMirrorEditor = inject(CodeMirrorEditor);
@@ -82,6 +96,7 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
readonly errors = signal<DiagnosticWithLocation[]>([]);
readonly files = this.codeMirrorEditor.openFiles;
readonly isCreatingFile = signal<boolean>(false);
readonly isRenamingFile = signal<boolean>(false);
ngAfterViewInit() {
this.codeMirrorEditor.init(this.codeEditorWrapperRef.nativeElement);
@@ -104,6 +119,12 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
this.displayErrorsBox.set(false);
}
closeRenameFile(): void {
this.isRenamingFile.set(false);
}
canRenameFile = (filename: string) => this.canDeleteFile(filename);
canDeleteFile(filename: string) {
return !REQUIRED_FILES.has(filename);
}
@@ -118,6 +139,37 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
this.matTabGroup.selectedIndex = this.files().length;
}
onRenameButtonClick() {
this.isRenamingFile.set(true);
}
async renameFile(event: SubmitEvent, oldPath: string) {
if (!this.renameFileInputRef) return;
event.preventDefault();
const renameFileInputValue = this.renameFileInputRef.nativeElement.value;
if (renameFileInputValue) {
if (renameFileInputValue.includes('..')) {
alert('File name can not contain ".."');
return;
}
// src is hidden from users, here we manually add it to the new filename
const newFile = 'src/' + renameFileInputValue;
if (this.files().find(({filename}) => filename.includes(newFile))) {
alert('File name already exists');
return;
}
await this.codeMirrorEditor.renameFile(oldPath, newFile);
}
this.isRenamingFile.set(false);
}
async createFile(event: SubmitEvent) {
if (!this.createFileInputRef) return;
@@ -340,6 +340,34 @@ export class CodeMirrorEditor {
this.changeCurrentFile(filename);
}
async renameFile(oldPath: string, newPath: string) {
const content = await this.nodeRuntimeSandbox.readFile(oldPath).catch((error) => {
// empty content if file does not exist
if (error.message.includes('ENOENT')) return '';
else throw error;
});
await this.nodeRuntimeSandbox.renameFile(oldPath, newPath).catch((error) => {
throw error;
});
this.embeddedTutorialManager.tutorialFiles.update((files) => {
delete files[oldPath];
files[newPath] = content;
return files;
});
this.embeddedTutorialManager.openFiles.update((files) => [
...files.filter((file) => file !== oldPath),
newPath,
]);
this.setProjectFiles();
this.updateVfsEnv();
this.saveLibrariesTypes();
this.changeCurrentFile(newPath);
}
async deleteFile(deletedFile: string): Promise<void> {
await this.nodeRuntimeSandbox.deleteFile(deletedFile);
@@ -177,6 +177,20 @@ describe('NodeRuntimeSandbox', () => {
expect(writeFileSpy).toHaveBeenCalledOnceWith(path, content);
});
it('should call renameFile with proper parameters', async () => {
setValuesToInitializeProject();
const fakeWebContainer = new FakeWebContainer();
service['webContainerPromise'] = Promise.resolve(fakeWebContainer);
const renameFileSpy = spyOn(fakeWebContainer.fs, 'rename');
const oldPath = 'oldPath';
const newPath = 'newPath';
await service.renameFile(oldPath, newPath);
expect(renameFileSpy).toHaveBeenCalledOnceWith(oldPath, newPath);
});
it('should initialize the Angular CLI based on the tutorial config', async () => {
setValuesToInitializeAngularCLI();
@@ -270,6 +270,16 @@ export class NodeRuntimeSandbox {
}
}
async renameFile(oldPath: string, newPath: string): Promise<void> {
const webContainer = await this.webContainerPromise!;
try {
await webContainer.fs.rename(oldPath, newPath);
} catch (err: any) {
throw err;
}
}
async readFile(filePath: string): Promise<string> {
const webContainer = await this.webContainerPromise!;
@@ -3,8 +3,8 @@
<div class="adev-top-content">
<!-- Banner(s) can be edited here! -->
<a href="https://goo.gle/angular-dot-dev" class="adev-banner" target="_blank">
<h1 tabindex="-1">Welcome to Angular.dev &#8212; the future home for Angular!</h1>
<p class="adev-banner-cta">Read about our beta launch</p>
<h1 tabindex="-1">Welcome to Angular.dev &#8212; the new home for Angular!</h1>
<p class="adev-banner-cta">Read about our launch</p>
</a>
</div>
</div>
@@ -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);
}
+13 -3
View File
@@ -1248,6 +1248,16 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'errors/NG0912',
contentPath: 'reference/errors/NG0912',
},
{
label: 'NG0955: Track expression resulted in duplicated keys for a given collection',
path: 'errors/NG0955',
contentPath: 'reference/errors/NG0955',
},
{
label: 'NG0956: Tracking expression caused re-creation of the DOM structure',
path: 'errors/NG0956',
contentPath: 'reference/errors/NG0956',
},
{
label: 'NG1001: Argument Not Literal',
path: 'errors/NG1001',
@@ -1269,9 +1279,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',
+3 -3
View File
@@ -76,15 +76,15 @@ The following example shows how to make a progress bar accessible by using host
The ARIA attribute `aria-valuenow` is bound to the user's input.
* In the template, the `aria-label` attribute ensures that the control is accessible to screen readers.
<docs-code-multifile
path="adev/src/content/examples/accessibility/src/app/app.component.ts"
preview>
<docs-code-multifile>
<docs-code
path="adev/src/content/examples/accessibility/src/app/progress-bar.component.ts"
language="ts"
linenums
highlight="[12, 20]"/>
<docs-code
path="adev/src/content/examples/accessibility/src/app/app.component.html"
language="html"
linenums
highlight="[8, 9]"/>
</docs-code-multifile>
@@ -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];
@@ -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": "@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": "@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">
@@ -7,7 +7,7 @@
<div [hidden]="formDir.submitted">
<div class="cross-validation" [class.cross-validation-error]="actorForm.errors?.['unambiguousRole'] && (actorForm.touched || actorForm.dirty)">
<div class="cross-validation" [class.cross-validation-error]="actorForm.hasError('unambiguousRole') && (actorForm.touched || actorForm.dirty)">
<div class="form-group">
<label for="name">Name</label>
@@ -18,13 +18,13 @@
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors?.['required']">
<div *ngIf="name.hasError('required')">
Name is required.
</div>
<div *ngIf="name.errors?.['minlength']">
<div *ngIf="name.hasError('minlength')">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors?.['forbiddenName']">
<div *ngIf="name.hasError('forbiddenName')">
Name cannot be Bob.
</div>
</div>
@@ -38,14 +38,14 @@
<div *ngIf="role.pending">Validating...</div>
<div *ngIf="role.invalid" class="alert alert-danger role-errors">
<div *ngIf="role.errors?.['uniqueRole']">
<div *ngIf="role.hasError('uniqueRole')">
Role is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->
<div *ngIf="actorForm.errors?.['unambiguousRole'] && (actorForm.touched || actorForm.dirty)" class="cross-validation-error-message alert alert-danger">
<div *ngIf="actorForm.hasError('unambiguousRole') && (actorForm.touched || actorForm.dirty)" class="cross-validation-error-message alert alert-danger">
Name cannot match role or audiences will be confused.
</div>
<!-- #enddocregion cross-validation-error-message -->
@@ -59,7 +59,7 @@
</select>
<div *ngIf="skill.invalid && skill.touched" class="alert alert-danger">
<div *ngIf="skill.errors?.['required']">Skill is required.</div>
<div *ngIf="skill.hasError('required')">Skill is required.</div>
</div>
</div>
@@ -6,7 +6,7 @@
<form #actorForm="ngForm" appUnambiguousRole>
<!-- #enddocregion cross-validation-register-validator -->
<div [hidden]="actorForm.submitted">
<div class="cross-validation" [class.cross-validation-error]="actorForm.errors?.['unambiguousRole'] && (actorForm.touched || actorForm.dirty)">
<div class="cross-validation" [class.cross-validation-error]="actorForm.hasError('unambiguousRole') && (actorForm.touched || actorForm.dirty)">
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
@@ -19,13 +19,13 @@
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert">
<div *ngIf="name.errors?.['required']">
<div *ngIf="name.hasError('required')">
Name is required.
</div>
<div *ngIf="name.errors?.['minlength']">
<div *ngIf="name.hasError('minlength')">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors?.['forbiddenName']">
<div *ngIf="name.hasError('forbiddenName')">
Name cannot be Bob.
</div>
@@ -46,14 +46,14 @@
<!-- #enddocregion role-input -->
<div *ngIf="role.pending">Validating...</div>
<div *ngIf="role.invalid" class="alert role-errors">
<div *ngIf="role.errors?.['uniqueRole']">
<div *ngIf="role.hasError('uniqueRole')">
Role is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->
<div *ngIf="actorForm.errors?.['unambiguousRole'] && (actorForm.touched || actorForm.dirty)" class="cross-validation-error-message alert">
<div *ngIf="actorForm.hasError('unambiguousRole') && (actorForm.touched || actorForm.dirty)" class="cross-validation-error-message alert">
Name cannot match role.
</div>
<!-- #enddocregion cross-validation-error-message -->
@@ -14,8 +14,7 @@ import {InMemoryDataService} from './in-memory-data.service';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
// TODO: Enable using Fetch API when disabling `HttpClientInMemoryWebApiModule`.
provideHttpClient(/* withFetch()*/),
provideHttpClient(withFetch()),
provideClientHydration(),
provideProtractorTestingSupport(), // essential for e2e testing
@@ -80,4 +80,4 @@ Avoid inserting, removing, and modifying DOM elements. In particular, **never di
element's `innerHTML` property**, which can make your application vulnerable
to [cross-site scripting (XSS) exploits](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting).
Angular's template bindings, including bindings for `innerHTML`, include safeguards that help
protect against XSS attacks. See the [Security guide](guide/security) for details.
protect against XSS attacks. See the [Security guide](best-practices/security) for details.
@@ -3,7 +3,7 @@
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).
HELPFUL: The `output()` function is currently in [developer preview](/reference/releases#developer-preview).
<docs-code language="ts" highlight="[[5], [8]]">
import {Component, output} from '@angular/core';
+3 -2
View File
@@ -60,10 +60,11 @@ emulated encapsulation.
In emulated mode, Angular supports
the [`:host`](https://developer.mozilla.org/en-US/docs/Web/CSS/:host)
and [`:host-context`](https://developer.mozilla.org/en-US/docs/Web/CSS/:host-context) pseudo
and [`:host-context()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:host-context) pseudo
classes without
using [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
During compilation, the framework transforms these pseudo classes into attributes. Angular's
During compilation, the framework transforms these pseudo classes into attributes so it doesn't
comply with these native pseudo classes' rules at runtime (e.g. browser compatibility, specificity). Angular's
emulated encapsulation mode does not support any other pseudo classes related to Shadow DOM, such
as `::shadow` or `::part`.
+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]">
@@ -7,7 +7,7 @@ You can manage the dependency structure among your components and injectable ser
This normally ensures that if a provided component or service is never actually used by the application, the compiler can remove its code from the bundle.
Due to the way Angular stores injection tokens, it is possible that such an unused component or service can end up in the bundle anyway.
This page describes a dependency-injection design pattern that supports proper tree-shaking by using lightweight injection tokens.
This page describes a dependency injection design pattern that supports proper tree-shaking by using lightweight injection tokens.
The lightweight injection token design pattern is especially important for library developers.
It ensures that when an application uses only some of your library's capabilities, the unused code can be eliminated from the client's application bundle.
@@ -20,7 +20,7 @@ To prevent the retention of unused components, your library should use the light
## When tokens are retained
To better explain the condition under which token retention occurs, consider a library that provides a library-card component.
This component contains a body and can contain an optional header.
This component contains a body and can contain an optional header:
<docs-code language="html">
@@ -30,7 +30,7 @@ This component contains a body and can contain an optional header.
</docs-code>
In a likely implementation, the `<lib-card>` component uses `@ContentChild()` or `@ContentChildren()` to get `<lib-header>` and `<lib-body>`, as in the following.
In a likely implementation, the `<lib-card>` component uses `@ContentChild()` or `@ContentChildren()` to get `<lib-header>` and `<lib-body>`, as in the following:
<docs-code language="typescript" highlight="[12]">
@Component({
@@ -51,7 +51,7 @@ class LibCardComponent {
Because `<lib-header>` is optional, the element can appear in the template in its minimal form, `<lib-card></lib-card>`.
In this case, `<lib-header>` is not used and you would expect it to be tree-shaken, but that is not what happens.
This is because `LibCardComponent` actually contains two references to the `LibHeaderComponent`.
This is because `LibCardComponent` actually contains two references to the `LibHeaderComponent`:
<docs-code language="typescript">
@ContentChild(LibHeaderComponent) header: LibHeaderComponent;
@@ -60,24 +60,24 @@ This is because `LibCardComponent` actually contains two references to the `LibH
* One of these reference is in the *type position*-- that is, it specifies `LibHeaderComponent` as a type: `header: LibHeaderComponent;`.
* The other reference is in the *value position*-- that is, LibHeaderComponent is the value of the `@ContentChild()` parameter decorator: `@ContentChild(LibHeaderComponent)`.
The compiler handles token references in these positions differently.
The compiler handles token references in these positions differently:
* The compiler erases *type position* references after conversion from TypeScript, so they have no impact on tree-shaking.
* The compiler must keep *value position* references at runtime, which **prevents** the component from being tree-shaken.
In the example, the compiler retains the `LibHeaderComponent` token that occurs in the value position.
This prevents the referenced component from being tree-shaken, even if the application does not actually use `<lib-header>` anywhere.
If `LibHeaderComponent` 's code, template, and styles combined becomes too large, including it unnecessarily can significantly increase the size of the client application.
If `LibHeaderComponent` 's code, template, and styles combine to become too large, including it unnecessarily can significantly increase the size of the client application.
## When to use the lightweight injection token pattern
The tree-shaking problem arises when a component is used as an injection token.
There are two cases when that can happen.
There are two cases when that can happen:
* The token is used in the value position of a [content query](guide/components/queries#content-queries).
* The token is used as a type specifier for constructor injection.
In the following example, both uses of the `OtherComponent` token cause retention of `OtherComponent`, preventing it from being tree-shaken when it is not used.
In the following example, both uses of the `OtherComponent` token cause retention of `OtherComponent`, preventing it from being tree-shaken when it is not used:
<docs-code language="typescript" highlight="[[2],[4]]">
class MyComponent {
@@ -89,16 +89,16 @@ class MyComponent {
Although tokens used only as type specifiers are removed when converted to JavaScript, all tokens used for dependency injection are needed at runtime.
These effectively change `constructor(@Optional() other: OtherComponent)` to `constructor(@Optional() @Inject(OtherComponent) other)`.
The token is now in a value position, and causes the tree shaker to keep the reference.
The token is now in a value position, which causes the tree-shaker to keep the reference.
HELPFUL: For all services, a library should use [tree-shakable providers](guide/di/dependency-injection#providing-dependency), providing dependencies at the root level rather than in components or modules.
HELPFUL: Libraries should use [tree-shakable providers](guide/di/dependency-injection#providing-dependency) for all services, providing dependencies at the root level rather than in components or modules.
## Using lightweight injection tokens
The lightweight injection token design pattern consists of using a small abstract class as an injection token, and providing the actual implementation at a later stage.
The abstract class is retained, not tree-shaken, but it is small and has no material impact on the application size.
The following example shows how this works for the `LibHeaderComponent`.
The following example shows how this works for the `LibHeaderComponent`:
<docs-code language="typescript" language="[[1],[6],[17]]">
abstract class LibHeaderToken {}
@@ -122,14 +122,14 @@ class LibCardComponent {
</docs-code>
In this example, the `LibCardComponent` implementation no longer refers to `LibHeaderComponent` in either the type position or the value position.
This lets full tree shaking of `LibHeaderComponent` take place.
This lets full tree-shaking of `LibHeaderComponent` take place.
The `LibHeaderToken` is retained, but it is only a class declaration, with no concrete implementation.
It is small and does not materially impact the application size when retained after compilation.
Instead, `LibHeaderComponent` itself implements the abstract `LibHeaderToken` class.
You can safely use that token as the provider in the component definition, allowing Angular to correctly inject the concrete type.
To summarize, the lightweight injection token pattern consists of the following.
To summarize, the lightweight injection token pattern consists of the following:
1. A lightweight injection token that is represented as an abstract class.
1. A component definition that implements the abstract class.
@@ -144,7 +144,7 @@ The implementation of the method, with all its code overhead, resides in the inj
This lets the parent communicate with the child, if it is present, in a type-safe manner.
For example, the `LibCardComponent` now queries `LibHeaderToken` rather than `LibHeaderComponent`.
The following example shows how the pattern lets `LibCardComponent` communicate with the `LibHeaderComponent` without actually referring to `LibHeaderComponent`.
The following example shows how the pattern lets `LibCardComponent` communicate with the `LibHeaderComponent` without actually referring to `LibHeaderComponent`:
<docs-code language="typescript" highlight="[[3],[13,16],[27]]">
abstract class LibHeaderToken {
@@ -179,7 +179,7 @@ class LibCardComponent implement AfterContentInit {
}
</docs-code>
In this example the parent queries the token to get the child component, and stores the resulting component reference if it is present.
In this example, the parent queries the token to get the child component, and stores the resulting component reference if it is present.
Before calling a method in the child, the parent component checks to see if the child component is present.
If the child component has been tree-shaken, there is no runtime reference to it, and no call to its method.
@@ -63,8 +63,8 @@ Because `isSpecial` is true, `ngClass` applies the class of `special` to the `<d
<docs-code header="src/app/app.component.html" path="adev/src/content/examples/built-in-directives/src/app/app.component.html" visibleRegion="NgClass-1"/>
For this use case, Angular applies the classes on initialization and in case of changes.
The full example calls `setCurrentClasses()` initially with `ngOnInit()` and when the dependent properties change through a button click.
For this use case, Angular applies the classes on initialization and in case of changes caused by reassigning the `currentClasses` object.
The full example calls `setCurrentClasses()` initially with `ngOnInit()` when the user clicks on the `Refresh currentClasses` button.
These steps are not necessary to implement `ngClass`.
## Setting inline styles with `NgStyle`
@@ -60,7 +60,7 @@ The tabs for the example show the following:
| flying-heroes.component.html | Template with the new pipe used. |
| flying-heroes.pipe.ts | File with custom pipe that filters flying heroes. |
<docs-code-multifile path="adev/src/content/examples/pipes/src/app/flying-heroes.component.ts_FlyingHeroesComponent" preview>
<docs-code-multifile>
<docs-code header="src/app/flying-heroes.component.html" path="adev/src/content/examples/pipes/src/app/flying-heroes.component.html" visibleRegion="template-flying-heroes"/>
<docs-code header="src/app/flying-heroes.pipe.ts" path="adev/src/content/examples/pipes/src/app/flying-heroes.pipe.ts" visibleRegion="pure"/>
</docs-code-multifile>
@@ -57,7 +57,7 @@ The following code example shows two component definitions:
| `exponential-strength.pipe.ts` | Defines a custom pipe named `exponentialStrength` with the `transform` method that performs the transformation. It defines an argument to the `transform` method \(`exponent`\) for a parameter passed to the pipe. |
| `power-booster.component.ts` | Demonstrates how to use the pipe, specifying a value \(`2`\) and the exponent parameter \(`10`\). |
<docs-code-multifile preview path="adev/src/content/examples/pipes/src/app/power-booster.component.ts">
<docs-code header="src/app/exponential-strength.pipe.ts" path="adev/src/content/examples/pipes/src/app/exponential-strength.pipe.ts"/>
<docs-code header="src/app/power-booster.component.ts" path="adev/src/content/examples/pipes/src/app/power-booster.component.ts"/>
<docs-code-multifile>
<docs-code header="src/app/exponential-strength.pipe.ts" language="ts" path="adev/src/content/examples/pipes/src/app/exponential-strength.pipe.ts"/>
<docs-code header="src/app/power-booster.component.ts" language="ts" path="adev/src/content/examples/pipes/src/app/power-booster.component.ts"/>
</docs-code-multifile>
+1 -1
View File
@@ -63,7 +63,7 @@ Angular defines the following security contexts:
| URL | Used for URL properties, such as `<a href>`. |
| Resource URL | A URL that is loaded and executed as code, for example, in `<script src>`. |
Angular sanitizes untrusted values for HTML, styles, and URLs. Sanitizing resource URLs isn't possible because they contain arbitrary code.
Angular sanitizes untrusted values for HTML and URLs. Sanitizing resource URLs isn't possible because they contain arbitrary code.
In development mode, Angular prints a console warning when it has to change a value during sanitization.
### Sanitization example
+1 -2
View File
@@ -130,8 +130,7 @@ That way, you are only dealing with `boolean` inside your component when calling
<my-custom-comp disabled>
```
IMPORTANT:Do not use transforms if they change the meaning of the input, or if they are [impure](https://en.wikipedia.org/wiki/Pure_function#Impure_functions).
IMPORTANT: Do not use transforms if they change the meaning of the input, or if they are [impure](https://en.wikipedia.org/wiki/Pure_function#Impure_functions).
Instead, use `computed` for transformations with different meaning, or an `effect` for impure code that should run whenever the input changes.
## Why should we use signal inputs and not `@Input()`?
+1 -1
View File
@@ -26,7 +26,7 @@ However, because an Angular template is part of an overall webpage, and not the
IMPORTANT: To eliminate the risk of script injection attacks, Angular does not support the `<script>` element in templates.
Angular ignores the `<script>` tag and outputs a warning to the browser console.
For more information, see the [Security](guide/security) page.
For more information, see the [Security](best-practices/security) page.
## More on template syntax
+108
View File
@@ -0,0 +1,108 @@
# Angular without ZoneJS (Zoneless)
## Why use Zoneless?
The main advantages to removing ZoneJS as a dependency are:
- **Improved performance**: ZoneJS uses DOM events and async tasks as indicators of when application state _might_ have updated and subsequently triggers application synchronization to run change detection on the application's views. ZoneJS does not have any insight into whether application state actually changed and so this synchronization is triggered more frequently than necessary.
- **Improved Core Web Vitals**: ZoneJS brings a fair amount of overhead, both in payload size and in startup time cost.
- **Improved debugging experience**: ZoneJS makes debugging code more difficult. Stack traces are harder to understand with ZoneJS. It's also difficult to understand when code breaks as a result of being outside the Angular Zone.
- **Better ecosystem compatibility**: ZoneJS works by patching browser APIs but does not automatically have patches for every new browser API. Some APIs simply cannot be patched effectively, such as `async`/`await`, and have to be downleveled to work with ZoneJS. Sometimes libraries in the ecosystem are also incompatible with the way ZoneJS patches the native APIs. Removing ZoneJS as a dependency ensures better long-term compatibility by removing a source of complexity, monkey patching, and ongoing maintenance.
## Enabling Zoneless in an application
The API for enabling Zoneless is currently experimental. Neither the shape, nor the underlying behavior is stable and can change
in patch versions. There are known feature gaps, including the lack of an ergonomic API which prevents the application from serializing too early with Server Side Rendering.
```typescript
// standalone bootstrap
bootstrapApplication(MyApp, {providers: [
provideExperimentalZonelessChangeDetection(),
]});
// NgModule bootstrap requires the provider and `ngZone: 'noop'`
platformBrowser().bootstrapModule(AppModule, {ngZone: 'noop'});
@NgModule({
providers: [provideExperimentalZonelessChangeDetection()]
})
export class AppModule {}
```
## Testing
The zoneless provider function can also be used with `TestBed` to help
ensure the components under test are compatible with a Zoneless
Angular application.
```typescript
TestBed.configureTestingModule({
providers: [provideExperimentalZonelessChangeDetection()]
});
const fixture = TestBed.createComponent(MyComponent);
await fixture.whenStable();
```
To ensure tests have the most similar behavior to production code,
avoid using `fixture.detectChanges()` when possibe. This forces
change detection to run when Angular might otherwise have not
scheduled change detection. Tests should ensure these notifications
are happening and allow Angular to handle when to synchronize
state rather than manually forcing it to happen in the test.
## Requirements for Zoneless compatibility
Angular relies on notifications from core APIs in order to determine when to run change detection and on which views.
These notifications include:
- `ChangeDetectorRef.markForCheck` (called automatically by `AsyncPipe`)
- `ComponentRef.setInput`
- Updating a signal that's read in a template
- Bound host or template listeners callbacks
- Attaching a view that was marked dirty by one of the above
### `OnPush`-compatible components
One way to ensure that a component is using the correct notification mechanisms from above is to
use [ChangeDetectionStrategy.OnPush](../best-practices/skipping-subtrees#using-onpush).
The `OnPush` change detection strategy is not required, but it is a recommended step towards zoneless compatibility for application components. It is not always possible for library components to use `ChangeDetectionStrategy.OnPush`.
When a library component is a host for user-components which might use `ChangeDetectionStratey.Default`, it cannot use `OnPush` because that would prevent the child component from being refreshed if it is not `OnPush` compatible and relies on ZoneJS to trigger change detection. Components can use the `Default` strategy as long as they notify Angular when change detection needs to run (calling `markForCheck`, using signals, `AsyncPipe`, etc.).
### Remove `NgZone.onMicrotaskEmpty`, `NgZone.onUnstable`, `NgZone.isStable`, or `NgZone.onStable`
Applications and libraries need to remove uses of `NgZone.onMicrotaskEmpty`, `NgZone.onUnstable` and `NgZone.onStable`.
These observables will never emit when an Application enables zoneless change detection.
Similarly, `NgZone.isStable` will always be `true` and should not be used as a condition for code execution.
The `NgZone.onMicrotaskEmpty` and `NgZone.onStable` observables are most often used to wait for Angular to
complete change detection before performing a task. Instead, these can be replaced by `afterNextRender`
if they need to wait for a single change detection or `afterRender` if there is some condition that might span
several change detection rounds. In other cases, these observables were used because they happened to be
familiar and have similar timing to what was needed. More straightforward or direct DOM APIs can be used instead,
such as `MutationObserver` when code needs to wait for certain DOM state (rather than waiting for it indirectly
through Angular's render hooks).
<docs-callout title="NgZone.run and NgZone.runOutsideAngular are compatible with Zoneless">
`NgZone.run` and `NgZone.runOutsideAngular` do not need to be removed in order for code to be compatible with
Zoneless applications. In fact, removing these calls can lead to performance regressions for libraries that
are used in applications that still rely on ZoneJS.
</docs-callout>
### `ExperimentalPendingTasks` for Server Side Rendering (SSR)
If you are using SSR with Angular, you may know that it relies on ZoneJS to help determine when the application
is "stable" and can be serialized. If there are asynchronous tasks that should prevent serialization, an application
not using ZoneJS will need to make Angular aware of these with the `ExperimentalPendingTasks` service. Serialization
will wait for the first moment that all pending tasks have been removed.
```typescript
const taskService = inject(ExperimentalPendingTasks);
const taskCleanup = taskService.add();
await doSomeWorkThatNeedsToBeRendered();
taskCleanup();
```
The framework uses this service internally as well to prevent serialization until asynchronous tasks are complete. These include, but are not limited to,
an ongoing Router navigation and an incomplete `HttpClient` request.
@@ -25,8 +25,7 @@ system" href="guide/components" link="Get started with Components">
testable.
</docs-card>
<docs-card title="Get fast state updates with fine-grained reactivity based on Signals" href="guide/signals" link="Explore Angular Signals">
Our fine-grained reactivity model, combined with compile-time optimizations, simplify development,
and helps build faster apps by default.
Our fine-grained reactivity model, combined with compile-time optimizations, simplifies development and helps build faster apps by default.
Granularly track how and where state is used throughout an application, giving the framework the power to render fast updates via highly optimized instructions.
</docs-card>
@@ -81,7 +80,7 @@ more">
<docs-card title="Clear support policies and predictable release schedule" href="reference/releases" link="Versioning & releasing">
Angular's predictable, time-based release schedule gives your organization confidence in the
stability and backwards compatibility of the framework. Long Term Support (LTS) windows make sure
you get critical security fixes when you need them. First party update tools, guides and automated
you get critical security fixes when you need them. First-party update tools, guides and automated
migration schematics help keep your apps up-to-date with the latest advancements to the framework
and the web platform.
</docs-card>
@@ -91,10 +90,10 @@ more">
<docs-card-container>
<docs-card title="Reach users everywhere with internationalization support" href="guide/i18n" link="Internationalization">
Angular's internationalization features handles message translations and formatting, including
Angular's internationalization features handle message translations and formatting, including
support for unicode standard ICU syntax.
</docs-card>
<docs-card title="Protect your users with security by default" href="guide/security" link="Security">
<docs-card title="Protect your users with security by default" href="best-practices/security" link="Security">
In collaboration with Google's world-class security engineers, Angular aims to make development
safe by default. Built-in security features, including HTML sanitization and
trusted type support, help protect your users from common vulnerabilities like
@@ -125,20 +124,20 @@ more">
<docs-card-container>
<docs-card title="Courses, blogs and resources" href="https://devlibrary.withgoogle.com/products/angular?sort=added" link="Check out DevLibrary">
Our community is composed of talented developers, writers, instructors, podcasters and more. The Google for Developers library is just a sample of the high quality resources available for new and experienced developers to continue developing.
Our community is composed of talented developers, writers, instructors, podcasters, and more. The Google for Developers library is just a sample of the high quality resources available for new and experienced developers to continue developing.
</docs-card>
<docs-card title="Open Source" href="https://github.com/angular/angular/blob/main/CONTRIBUTING.md" link="Contribute to Angular">
We are thankful for the open source contributors who make Angular a better framework for everyone. From fixing a typo in the docs, to adding major features, we encourage anyone interested to get started on our GitHub.
</docs-card>
<docs-card title="Community partnerships" href="https://developers.google.com/community/experts/directory?specialization=angular" link="Meet the Angular GDEs">
Our team partners with individuals, educators and enterprises to ensure we consistently are supporting developers. Angular Google Developer Experts (GDEs) represent community leaders around the world educating, organizing and developing with Angular. Enterprise partnerships help ensure that Angular scales well for technology industry leaders.
Our team partners with individuals, educators, and enterprises to ensure we consistently are supporting developers. Angular Google Developer Experts (GDEs) represent community leaders around the world educating, organizing, and developing with Angular. Enterprise partnerships help ensure that Angular scales well for technology industry leaders.
</docs-card>
<docs-card title="Partnering with other Google technologies">
Angular partners closely with other Google technologies and teams to improve the web.
Our ongoing partnership with Chrome’s Aurora actively explores improvements to user experience across the web developing built-in performance optimizations like NgOptimizedImage and improvements to Angular’s Core Web Vitals.
Our ongoing partnership with Chrome’s Aurora actively explores improvements to user experience across the web, developing built-in performance optimizations like NgOptimizedImage and improvements to Angular’s Core Web Vitals.
We are also working with [Firebase](https://firebase.google.com/), [Tensorflow](https://www.tensorflow.org/), [Flutter](https://flutter.dev/), [Material Design](https://m3.material.io/) and [Google Cloud](https://cloud.google.com/) to ensure we provide meaningful integrations across the developer workflow.
We are also working with [Firebase](https://firebase.google.com/), [Tensorflow](https://www.tensorflow.org/), [Flutter](https://flutter.dev/), [Material Design](https://m3.material.io/), and [Google Cloud](https://cloud.google.com/) to ensure we provide meaningful integrations across the developer workflow.
</docs-card>
</docs-card-container>
@@ -0,0 +1,34 @@
@name Track expression resulted in duplicated keys for a given collection.
@category runtime
@shortDescription A track expression specified in the `@for` loop evaluated to duplicated keys for a given collection. Duplicate keys are problematic from the correctness point of view and the tracking expression should be updated.
@description
A track expression specified in the `@for` loop evaluated to duplicated keys for a given collection, ex.:
```typescript
@Component({
template: `@for (item of items; track item.value) {{{item.value}}}`,
})
class TestComponent {
items = [{key: 1, value: 'a'}, {key: 2, value: 'b'}, {key: 3, value: 'a'}];
}
```
In the provided example the `item.key` tracking expression will find two duplicate keys `a` (at index 0 and 2).
Duplicate keys are problematic from the correctness point of view: since the `@for` loop can't uniquely identify items it might choose DOM nodes corresponding to _another_ item (with the same key) when performing DOM moves or destroy.
There is also performance penalty associated with duplicated keys - internally Angular must use more sophisticated and slower data structures while repeating over collections with duplicated keys.
## Fixing the error
Change the tracking expression such that it uniquely identifies an item in a collection. In the discussed example the correct track expression would use the unique `key` property (`item.key`):
```typescript
@Component({
template: `@for (item of items; track item.key) {{{item.value}}}`,
})
class TestComponent {
items = [{key: 1, value: 'a'}, {key: 2, value: 'b'}, {key: 3, value: 'a'}];
}
```
@@ -0,0 +1,60 @@
@name Tracking expression caused re-creation of the DOM structure.
@category runtime
@shortDescription The configured tracking expression (track by identity) caused re-creation of the DOM structure for the entire collection, which is problematic from the performance and correctness point of view.
@description
The identity track expression specified in the `@for` loop caused re-creation of the DOM corresponding to _all_ items. This is very expensive operation that commonly occurs when working with immutable data structures, Example:
```typescript
@Component({
template: `
<button (click)="toggleAllDone()">All done!</button>
<ul>
@for (todo of todos; track todo) {
<li>{{todo.task}}</li>
}
</ul>
`,
})
export class App {
todos = [
{ id: 0, task: 'understand trackBy', done: false },
{ id: 1, task: 'use proper tracking expression', done: false },
];
toggleAllDone() {
this.todos = this.todos.map(todo => ({ ...todo, done: true }));
}
}
```
In the provided example the entire list, with all the views (DOM nodes, Angular directives, components, queries etc.) are re-created (!) after toggling the "done" status of items. Here a relatively inexpensive binding update to the `done` property would suffice.
Apart from having high performance penalty, re-creating the DOM tree results in loss of state in the DOM elements (ex.: focus, text selection, sites loaded in an iframe etc.).
## Fixing the error
Change the tracking expression such that it uniquely identifies an item in a collection, regardless of its object identity. In the discussed example the correct track expression would use the unique `id` property (`item.id`):
```typescript
@Component({
template: `
<button (click)="toggleAllDone()">All done!</button>
<ul>
@for (todo of todos; track todo.id) {
<li>{{todo.task}}</li>
}
</ul>
`,
})
export class App {
todos = [
{ id: 0, task: 'understand trackBy', done: false },
{ id: 1, task: 'use proper tracking expression', done: false },
];
toggleAllDone() {
this.todos = this.todos.map(todo => ({ ...todo, done: true }));
}
}
```
@@ -23,11 +23,13 @@
| `NG0507` | [HTML content was altered after SSR](errors/NG0507) |
| `NG0910` | [Unsafe bindings on an iframe element](errors/NG0910) |
| `NG0912` | [Component ID generation collision](errors/NG0912) |
| `NG0955` | [Track expression resulted in duplicated keys for a given collection](errors/NG0955) |
| `NG0956` | [Tracking expression caused re-creation of the DOM structure](errors/NG0956) |
| `NG01101` | [Wrong Async Validator Return Type](errors/NG01101) |
| `NG01203` | [Missing value accessor](errors/NG01203) |
| `NG02200` | [Missing Iterable Differ](errors/NG02200) |
| `NG02800` | [JSONP support in HttpClient configuration](errors/NG02800) |
| `NG5000` | [Hydration with unsupported Zone.js instance.](errors/NG5000) |
| `NG05000` | [Hydration with unsupported Zone.js instance.](errors/NG05000) |
| `NG05104` | [Root element was not found.](errors/NG05104) |
## Compiler errors
+4 -1
View File
@@ -46,7 +46,10 @@ In this example, we’ve adapted the shield to create an Angular Signals logo va
![Angular Signals logo](assets/images/press-kit/angular_signals.png#medium "Angular Signals logo")
</docs-step>
<docs-step title="Do’s and don’ts of adapting the brand">
<docs-step title="Do’s and don’ts of using Angular's brand">
If you are creating your own logo, we encourage differentiating your logo from ours to not cause confusion. When adapting the logo, you are free to change and adapt the colors and shape to make it your own.
When representing Angular with the original logo, please follow these guidelines:
![Rhubarb the small cat](assets/images/press-kit/do_and_dont.png "Rhubarb the small cat")
</docs-step>
+1 -1
View File
@@ -222,7 +222,7 @@ This section represents explorations and prototyping of potential future project
<docs-card title="Transition the Angular language service to Ivy" link="Completed Q2 2021" href="tools/language-service">
The goal of this project is to improve the experience and remove legacy dependency by transitioning the language service to Ivy. Today the language service still uses the View Engine compiler and type checking, even for Ivy apps. We want to use the Ivy template parser and improved type checking for the Angular Language service to match app behavior. This migration is also a step towards unblocking the removal of View Engine, which will simplify Angular, reduce the npm package size, and improve the maintainability of the framework.
</docs-card>
<docs-card title="Increased security with native Trusted Types in Angular" link="Completed Q2 2021" href="guide/security">
<docs-card title="Increased security with native Trusted Types in Angular" link="Completed Q2 2021" href="best-practices/security">
In collaboration with the Google security team, we are adding support for the new Trusted Types API. This web platform API helps developers build more secure web apps.
</docs-card>
<docs-card title="Optimized build speed and bundle sizes with Angular CLI webpack 5" link="Completed Q2 2021" href="tools/cli/build">
File diff suppressed because it is too large Load Diff
@@ -87,18 +87,18 @@ In your IDE:
1. In `index.html`, replace the `<title>` element with this code to update the title of the app.
<docs-code header="Replace in src/index.html" path="adev/src/content/tutorials/first-app/steps/01-hello-world/src/index.html" visibleLines="[5]"/>
<docs-code header="Replace in src/index.html" path="adev/src/content/tutorials/first-app/steps/02-HomeComponent/src/index.html" visibleLines="[5]"/>
Then, save the changes you just made to `index.html`.
1. Next, open `first-app/src/app/app.component.ts`.
1. In `app.component.ts`, in the `@Component` definition, replace the `template` line with this code to change the text in the app component.
<docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.component.ts" visibleLines="[7]"/>
<docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/02-HomeComponent/src/app/app.component.ts" visibleLines="[7,9]"/>
1. In `app.component.ts`, in the `AppComponent` class definition, replace the `title` line with this code to change the component title.
<docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/01-hello-world/src/app/app.component.ts" visibleLines="[11]"/>
<docs-code header="Replace in src/app/app.component.ts" path="adev/src/content/tutorials/first-app/steps/02-HomeComponent/src/app/app.component.ts" visibleLines="[12,14]"/>
Then, save the changes you made to `app.component.ts`.
@@ -196,6 +196,8 @@ The server is now reading data from the HTTP request but the components that rel
</docs-workflow>
Note: This lesson relies on the `fetch` browser API. For the support of interceptors, please refer to the [Http Client documentation](/guide/http)
Summary: In this lesson, you updated your app to use a local web server (`json-server`), and use asynchronous service methods to retrieve data.
Congratulations! You've successfully completed this tutorial and are ready to continue your journey with building even more complex Angular Apps.
@@ -42,8 +42,8 @@ export class HomeComponent {
return;
}
this.filteredLocationList = this.housingLocationList.filter(
(housingLocation) => housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
);
}
}
@@ -38,8 +38,8 @@ export class HomeComponent {
return;
}
this.filteredLocationList = this.housingLocationList.filter(
(housingLocation) => housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -25,6 +25,6 @@
"@angular-devkit/build-angular": "^17.3.0-rc",
"@angular/cli": "^17.3.0-rc",
"@angular/compiler-cli": "^17.3.0-rc",
"typescript": "~5.2.0"
"typescript": "~5.4.0"
}
}
@@ -6,7 +6,7 @@ load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "Lin
def _filter_external_npm_deps_impl(ctx):
problematic_paths = ["external/npm/node_modules/%s" % pkg for pkg in ctx.attr.angular_packages]
package_name = ctx.attr.target.label.package
has_problematic_transitive_dep = package_name.startswith("@angular-devkit/")
has_problematic_transitive_dep = package_name.startswith("@angular-devkit/") or package_name.startswith("@angular/build")
filtered_deps = []
# Note: to_list() is expensive; we need to invoke it here to get the path
@@ -7,7 +7,7 @@
<div
class="cross-validation"
[class.cross-validation-error]="
heroForm.errors?.['identityRevealed'] &&
heroForm.hasError('identityRevealed') &&
(heroForm.touched || heroForm.dirty)
"
>
@@ -26,11 +26,11 @@
*ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger"
>
<div *ngIf="name.errors?.['required']">Name is required.</div>
<div *ngIf="name.errors?.['minlength']">
<div *ngIf="name.hasError('required')">Name is required.</div>
<div *ngIf="name.hasError('minlength')">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors?.['forbiddenName']">
<div *ngIf="name.hasError('forbiddenName')">
Name cannot be Bob.
</div>
</div>
@@ -51,7 +51,7 @@
*ngIf="alterEgo.invalid"
class="alert alert-danger alter-ego-errors"
>
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
<div *ngIf="alterEgo.hasError('uniqueAlterEgo')">
Alter ego is already taken.
</div>
</div>
@@ -60,7 +60,7 @@
<!-- #docregion cross-validation-error-message -->
<div
*ngIf="
heroForm.errors?.['identityRevealed'] &&
heroForm.hasError('identityRevealed') &&
(heroForm.touched || heroForm.dirty)
"
class="cross-validation-error-message alert alert-danger"
@@ -82,7 +82,7 @@
</select>
<div *ngIf="power.invalid && power.touched" class="alert alert-danger">
<div *ngIf="power.errors?.['required']">Power is required.</div>
<div *ngIf="power.hasError('required')">Power is required.</div>
</div>
</div>
@@ -8,7 +8,7 @@
<div
class="cross-validation"
[class.cross-validation-error]="
heroForm.errors?.['identityRevealed'] &&
heroForm.hasError('identityRevealed') &&
(heroForm.touched || heroForm.dirty)
"
>
@@ -33,11 +33,11 @@
*ngIf="name.invalid && (name.dirty || name.touched)"
class="alert"
>
<div *ngIf="name.errors?.['required']">Name is required.</div>
<div *ngIf="name.errors?.['minlength']">
<div *ngIf="name.hasError('required')">Name is required.</div>
<div *ngIf="name.hasError('minlength')">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors?.['forbiddenName']">
<div *ngIf="name.hasError('forbiddenName')">
Name cannot be Bob.
</div>
</div>
@@ -59,7 +59,7 @@
<!-- #enddocregion alterEgo-input -->
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alter-ego-errors">
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
<div *ngIf="alterEgo.hasError('uniqueAlterEgo')">
Alter ego is already taken.
</div>
</div>
@@ -68,7 +68,7 @@
<!-- #docregion cross-validation-error-message -->
<div
*ngIf="
heroForm.errors?.['identityRevealed'] &&
heroForm.hasError('identityRevealed') &&
(heroForm.touched || heroForm.dirty)
"
class="cross-validation-error-message alert"
@@ -13,14 +13,14 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"angular": "^1.8.3",
"angular-in-memory-web-api": "~0.17.0",
"angular-route": "1.8.3",
@@ -29,9 +29,9 @@
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/angular": "^1.8.4",
"@types/angular-route": "^1.7.2",
"@types/jasmine": "~5.1.0",
@@ -46,6 +46,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -13,23 +13,23 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"angular-in-memory-web-api": "~0.17.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
"copyfiles": "^2.4.1",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -13,24 +13,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/elements": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/elements": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"angular-in-memory-web-api": "~0.17.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
"jasmine-core": "~5.1.0",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -15,24 +15,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/localize": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/localize": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"angular-in-memory-web-api": "~0.17.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
"jasmine-core": "~5.1.0",
@@ -45,6 +45,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -13,24 +13,24 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/service-worker": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"@angular/service-worker": "17.3.7",
"angular-in-memory-web-api": "~0.17.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
"jasmine-core": "~5.1.0",
@@ -43,6 +43,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -25,15 +25,15 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/router": "17.3.5",
"@angular/upgrade": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/router": "17.3.7",
"@angular/upgrade": "17.3.7",
"core-js": "^3.0.0",
"rxjs": "~7.8.0",
"systemjs": "~0.19.39",
@@ -42,7 +42,7 @@
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular/compiler-cli": "17.3.5",
"@angular/compiler-cli": "17.3.7",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^13.2.0",
"@types/angular": "^1.8.4",
@@ -63,6 +63,6 @@
"protractor": "~7.0.0",
"rollup": "^2.70.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
@@ -13,16 +13,16 @@
},
"private": true,
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/platform-server": "17.3.5",
"@angular/router": "17.3.5",
"@angular/ssr": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/platform-server": "17.3.7",
"@angular/router": "17.3.7",
"@angular/ssr": "17.3.6",
"angular-in-memory-web-api": "~0.17.0",
"express": "^4.18.1",
"rxjs": "~7.8.0",
@@ -30,9 +30,9 @@
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@types/express": "^4.17.13",
"@types/jasmine": "~5.1.0",
"@types/node": "^16.11.35",
@@ -46,6 +46,6 @@
"karma-jasmine-html-reporter": "~2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
+18 -18
View File
@@ -19,20 +19,20 @@
"license": "MIT",
"repository": {},
"dependencies": {
"@angular/animations": "17.3.5",
"@angular/common": "17.3.5",
"@angular/compiler": "17.3.5",
"@angular/core": "17.3.5",
"@angular/elements": "17.3.5",
"@angular/forms": "17.3.5",
"@angular/localize": "17.3.5",
"@angular/platform-browser": "17.3.5",
"@angular/platform-browser-dynamic": "17.3.5",
"@angular/platform-server": "17.3.5",
"@angular/router": "17.3.5",
"@angular/service-worker": "17.3.5",
"@angular/ssr": "17.3.5",
"@angular/upgrade": "17.3.5",
"@angular/animations": "17.3.7",
"@angular/common": "17.3.7",
"@angular/compiler": "17.3.7",
"@angular/core": "17.3.7",
"@angular/elements": "17.3.7",
"@angular/forms": "17.3.7",
"@angular/localize": "17.3.7",
"@angular/platform-browser": "17.3.7",
"@angular/platform-browser-dynamic": "17.3.7",
"@angular/platform-server": "17.3.7",
"@angular/router": "17.3.7",
"@angular/service-worker": "17.3.7",
"@angular/ssr": "17.3.6",
"@angular/upgrade": "17.3.7",
"angular": "^1.8.3",
"angular-in-memory-web-api": "~0.17.0",
"angular-route": "1.8.3",
@@ -45,9 +45,9 @@
"zone.js": "~0.14.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.5",
"@angular/cli": "17.3.5",
"@angular/compiler-cli": "17.3.5",
"@angular-devkit/build-angular": "17.3.6",
"@angular/cli": "17.3.6",
"@angular/compiler-cli": "17.3.7",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^13.2.0",
"@types/angular": "^1.8.4",
@@ -79,6 +79,6 @@
"rollup-plugin-terser": "^7.0.2",
"source-map-explorer": "^2.0.0",
"ts-node": "~10.9.0",
"typescript": "~4.9.3"
"typescript": "~5.4.0"
}
}
+12 -23
View File
@@ -22,40 +22,29 @@ const debugMode = false;
// https://saucelabs.com/platform/platform-configurator
const customLaunchers = {
'SL_ANDROID11': {
base: 'SauceLabs',
browserName: 'Chrome',
platformName: 'Android',
platformVersion: '11.0',
deviceName: 'Google Pixel 3a GoogleAPI Emulator',
appiumVersion: '1.20.2',
extendedDebugging: debugMode,
},
'SL_ANDROID12': {
base: 'SauceLabs',
browserName: 'Chrome',
platformName: 'Android',
platformVersion: '12.0',
deviceName: 'Google Pixel 4a (5G) GoogleAPI Emulator',
appiumVersion: '1.22.1',
extendedDebugging: debugMode,
browserName: 'Browser',
'appium:deviceName': 'Android GoogleAPI Emulator',
'appium:platformVersion': 'current_major',
'appium:automationName': 'UiAutomator2',
},
};
const sauceAliases = {
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false),
};
module.exports = {
customLaunchers : customLaunchers,
sauceAliases : sauceAliases,
customLaunchers: customLaunchers,
sauceAliases: sauceAliases,
};
function buildConfiguration(type, target, required) {
return Object.keys(config)
.filter((item) => {
const conf = config[item][type];
return conf.required === required && conf.target === target;
})
.map((item) => target + '_' + item.toUpperCase());
.filter((item) => {
const conf = config[item][type];
return conf.required === required && conf.target === target;
})
.map((item) => target + '_' + item.toUpperCase());
}
@@ -70,13 +70,6 @@ pkg_web(
":bundle",
"@npm//:node_modules/tslib/tslib.js",
],
# Currently, ibazel doesn't allow passing in flags to bazel run.
# This means we are not able to use --config snapshot-build
# to access the current commit SHA.
# Since we still want to be able to use ibazel to speed up
# local development, we supply an empty string for the SHA substitution.
# This does not effect production builds.
substitutions = {"BUILD_SCM_COMMIT_SHA": ""},
)
http_server(
@@ -8,5 +8,4 @@
export const environment = {
production: false,
LATEST_SHA: 'BUILD_SCM_COMMIT_SHA', // Stamped at build time by bazel
};
@@ -51,7 +51,7 @@ import {ComponentTreeNode} from './interfaces';
import {ngDebugDependencyInjectionApiIsSupported} from './ng-debug-api/ng-debug-api';
import {setConsoleReference} from './set-console-reference';
import {serializeDirectiveState} from './state-serializer/state-serializer';
import {runOutsideAngular} from './utils';
import {runOutsideAngular, unwrapSignal} from './utils';
import {DirectiveForestHooks} from './hooks/hooks';
export const subscribeToClientEvents = (
@@ -194,7 +194,7 @@ const getNestedPropertiesCallback =
}
let data = current.instance;
for (const prop of propPath) {
data = data[prop];
data = unwrapSignal(data[prop]);
if (!data) {
console.error('Cannot access the properties', propPath, 'of', node);
}
@@ -325,11 +325,11 @@ function getLevelDescriptorValue(
const isReadonly = isSignal(prop);
switch (type) {
case PropType.Array:
return prop.map((_: any, idx: number) =>
return value.map((_: any, idx: number) =>
continuation(value, idx, isReadonly, currentLevel + 1, level),
);
case PropType.Object:
return getKeys(prop).reduce(
return getKeys(value).reduce(
(accumulator, propName) => {
if (!ignoreList.has(propName)) {
accumulator[propName] = continuation(
@@ -6,13 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
interface Env {
LATEST_SHA: string;
}
export interface Environment {
production: boolean;
LATEST_SHA: string;
}
export const TOP_LEVEL_FRAME_ID = 0;
@@ -51,9 +51,7 @@
</span>
}
@if (latestSHA) {
| DevTools SHA: {{ latestSHA }}
}
| DevTools: {{ extensionVersion }}
</section>
}
</nav>
@@ -90,6 +90,8 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit {
return parseInt(version.toString().split('.')[0], 10);
});
extensionVersion = 'Development Build';
constructor(
public tabUpdate: TabUpdate,
public themeService: ThemeService,
@@ -111,6 +113,10 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit {
ngOnInit(): void {
this.navbar.stretchTabs = false;
if (chrome !== undefined && chrome.runtime !== undefined) {
this.extensionVersion = chrome.runtime.getManifest().version;
}
}
get tabs(): Tabs[] {
@@ -122,10 +128,6 @@ export class DevToolsTabsComponent implements OnInit, AfterViewInit {
this.navbar.disablePagination = true;
}
get latestSHA(): string {
return this.applicationEnvironment.environment.LATEST_SHA.slice(0, 8);
}
changeTab(tab: Tabs): void {
this.activeTab = tab;
this.tabUpdate.notify();
@@ -21,53 +21,57 @@
(mouseleave)="removeHighlight()"
[style.padding-left]="15 + 15 * node.level + 'px'"
>
<div class="tree-node-info">
@if (node.expandable) {
<button
[style.left]="15 * node.level + 'px'"
(click)="treeControl.toggle(node)"
[attr.aria-label]="'toggle ' + node.name"
>
<mat-icon class="mat-icon-rtl-mirror">
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
</mat-icon>
</button>
}
<span class="element-name" [class.angular-element]="isElement(node)">{{ node.name }}</span>
<div class="tree-node-info">
@if (node.expandable) {
<!-- We stop propagration on dblClick to prevent to show the element panel -->
<button
[style.left]="15 * node.level + 'px'"
(click)="treeControl.toggle(node)"
(dblclick)="stopPropagation($event)"
[attr.aria-label]="'toggle ' + node.name"
>
<mat-icon class="mat-icon-rtl-mirror">
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
</mat-icon>
</button>
}
<span class="element-name" [class.angular-element]="isElement(node)">{{ node.name }}</span>
@if (node.directives) {
<span class="dir-names">[{{ node.directives }}]</span>
}
@if (isSelected(node)) {
<span class="console-reference"> == $ng0 </span>
}
@if (node.directives) {
<span class="dir-names">[{{ node.directives }}]</span>
}
@if (isSelected(node)) {
<span class="console-reference"> == $ng0 </span>
}
@switch(node.hydration?.status) {
@case('hydrated') {
<mat-icon matTooltip="Hydrated" class="hydration">water_drop</mat-icon>
}
@case('skipped') {
<mat-icon matTooltip="Skipped" class="hydration skipped">invert_colors_off</mat-icon>
}
@case('mismatched') {
<mat-icon matTooltip="Mismatch" class="hydration mismatched">error_outline</mat-icon>
}
}
</div>
@if(node.hydration?.status === 'mismatched' && (node.hydration.expectedNodeDetails || node.hydration.actualNodeDetails)) {
<div class="hydration-error">
@if(node.hydration.expectedNodeDetails) {
<div>Expected Dom:</div>
<pre>{{node.hydration.expectedNodeDetails}}</pre>
}
@if(node.hydration.actualNodeDetails) {
<div>Actual Dom:</div>
<pre>{{node.hydration.actualNodeDetails}}</pre>
@switch (node.hydration?.status) {
@case ('hydrated') {
<mat-icon matTooltip="Hydrated" class="hydration">water_drop</mat-icon>
}
@case ('skipped') {
<mat-icon matTooltip="Hydration skipped" class="hydration skipped">invert_colors_off</mat-icon>
}
@case ('mismatched') {
<mat-icon matTooltip="Hydration mismatch" class="hydration mismatched">error_outline</mat-icon>
}
}
</div>
}
@if (
treeControl.isExpanded(node) &&
node.hydration?.status === 'mismatched' &&
(node.hydration.expectedNodeDetails || node.hydration.actualNodeDetails)
) {
<div class="hydration-error">
@if (node.hydration.expectedNodeDetails) {
<div>Expected Dom:</div>
<pre>{{node.hydration.expectedNodeDetails}}</pre>
}
@if (node.hydration.actualNodeDetails) {
<div>Actual Dom:</div>
<pre>{{node.hydration.actualNodeDetails}}</pre>
}
</div>
}
</div>
</ng-container>
</cdk-virtual-scroll-viewport>
@@ -16,6 +16,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
@@ -99,11 +100,13 @@ export class DirectiveForestComponent {
readonly itemHeight = 18;
private _initialized = false;
private resizeObserver: ResizeObserver;
constructor(
private _tabUpdate: TabUpdate,
private _messageBus: MessageBus<Events>,
private _cdr: ChangeDetectorRef,
private elementRef: ElementRef,
) {
this.subscribeToInspectorEvents();
this._tabUpdate.tabUpdate$.pipe(takeUntilDestroyed()).subscribe(() => {
@@ -114,6 +117,17 @@ export class DirectiveForestComponent {
});
}
});
// In some cases there a height changes, we need to recalculate the viewport size.
this.resizeObserver = new ResizeObserver(() => {
this.viewport.scrollToIndex(0);
this.viewport.checkViewportSize();
});
this.resizeObserver.observe(this.elementRef.nativeElement);
}
ngOnDestroy(): void {
this.resizeObserver.disconnect();
}
subscribeToInspectorEvents(): void {
@@ -330,6 +344,10 @@ export class DirectiveForestComponent {
}
}
stopPropagation(event: Event): void {
event.stopPropagation();
}
private _findMatchedNodes(): number[] {
const indexesOfMatchedNodes: number[] = [];
for (let i = 0; i < this.dataSource.data.length; i++) {
@@ -157,8 +157,8 @@ export class DirectivePropertyResolver {
propPointer = inputLabels.has(propName)
? inputProps
: outputLabels.has(propName)
? outputProps
: stateProps;
? outputProps
: stateProps;
propPointer[propName] = this.directiveProperties[propName];
});
@@ -151,5 +151,4 @@ pkg_web(
additional_root_paths = [
"projects/ng-devtools/src/lib",
],
substitutions = {"BUILD_SCM_COMMIT_SHA": "{BUILD_SCM_COMMIT_SHA}"},
)
@@ -8,5 +8,4 @@
export const environment = {
production: true,
LATEST_SHA: 'BUILD_SCM_COMMIT_SHA', // Stamped at build time by bazel
};
@@ -3,12 +3,13 @@
"short_name": "Angular DevTools",
"name": "Angular DevTools",
"description": "Angular DevTools extends Chrome DevTools adding Angular specific debugging and profiling capabilities.",
"version": "1.0.13",
"version_name": "1.0.13",
"version": "1.0.15",
"version_name": "1.0.15",
"minimum_chrome_version": "102",
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlm3JzpfmggVNDXe1uSVKq1btwWec4pOYefbB36CYj41bqUyw3xQqiAOCIlSAhl5GkQA/9PTixCNd14ix6RbI2yV8OeNt6oc0ca5BmEdGVj27lu32CgH5KbOc+AhyOAikoMq4IlgaWvqglZiyzd9KnL1wO/DqLDsZS0PV9oRwZk/8Eno2giZ1El4YQ5cNZ125JJR5z9KYAvxUs6bnDXAM71gW5sJwoUdgXQZhEIT18uSz0p2RiC0Ajjh4kx9c5Ll9naByXd3g5hjS8Ro5WbENRhxZtx1eEcgwC79l0pbJjI71i0QjbfLZn6syfHGMWsLyMc/ucv6d86tCPO8J1mU8iQIDAQAB",
"icons": {
"16": "assets/icon16.png",
"48": "assets/icon48.png",
@@ -3,7 +3,7 @@
"short_name": "Angular DevTools",
"name": "Angular DevTools",
"description": "Angular DevTools extends Firefox DevTools adding Angular specific debugging and profiling capabilities.",
"version": "1.0.13",
"version": "1.0.15",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"icons": {
"16": "assets/icon16.png",
-7
View File
@@ -73,13 +73,6 @@ pkg_web(
":bundle",
"@npm//:node_modules/tslib/tslib.js",
],
# Currently, ibazel doesn't allow passing in flags to bazel run.
# This means we are not able to use --config snapshot-build
# to access the current commit SHA.
# Since we still want to be able to use ibazel to speed up
# local development, we supply an empty string for the SHA substitution.
# This does not effect production builds.
substitutions = {"BUILD_SCM_COMMIT_SHA": ""},
)
http_server(
-1
View File
@@ -8,5 +8,4 @@
export const environment = {
production: false,
LATEST_SHA: 'BUILD_SCM_COMMIT_SHA', // Stamped at build time by bazel
};

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