mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b46819ed1 | |||
| 7a090eee96 | |||
| da68bead56 | |||
| 543b98e1f8 | |||
| 03619ada72 | |||
| d8e5316eca | |||
| 77efeb5933 | |||
| 252b111d1e | |||
| 1c41633483 | |||
| 9f977e0f34 | |||
| 3e19890967 | |||
| 074fd4a5ec | |||
| 094d733654 | |||
| 2fff344cd6 | |||
| 951ef93aa6 | |||
| f30a49e3de | |||
| c48e1cc4ed | |||
| 198834b731 | |||
| 6a8c030ab1 | |||
| 232a8c1b8d | |||
| 8f944e0752 | |||
| af0c8b8fa8 | |||
| ee19395672 |
+1
-53
@@ -249,6 +249,7 @@ jobs:
|
||||
- ./ng
|
||||
- ./bazel_repository_cache
|
||||
|
||||
# TODO: Remove test job after confirmation that the GHA version is stable.
|
||||
test:
|
||||
executor:
|
||||
name: test-browser-executor
|
||||
@@ -312,48 +313,6 @@ jobs:
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
test_aio:
|
||||
executor:
|
||||
name: test-browser-executor
|
||||
resource_class: 2xlarge+
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
|
||||
# Run all aio tests
|
||||
- run:
|
||||
command: yarn --cwd aio test:ci
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size
|
||||
|
||||
deploy_aio:
|
||||
executor:
|
||||
name: test-browser-executor
|
||||
resource_class: 2xlarge+
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run: yarn --cwd aio deploy-production
|
||||
|
||||
test_aio_local:
|
||||
executor:
|
||||
name: test-browser-executor
|
||||
resource_class: 2xlarge+
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Run all aio tests (with local Angular packages)
|
||||
- run:
|
||||
command: yarn --cwd aio test-local:ci
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local
|
||||
|
||||
# The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and
|
||||
# tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh).
|
||||
# Having multiple jobs that independently build in this manner duplicates some work; we build
|
||||
@@ -593,15 +552,6 @@ workflows:
|
||||
- legacy-unit-tests-saucelabs:
|
||||
requires:
|
||||
- setup
|
||||
- test_aio:
|
||||
requires:
|
||||
- setup
|
||||
- deploy_aio:
|
||||
requires:
|
||||
- test_aio
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- setup
|
||||
- publish_packages_as_artifacts:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
@@ -610,8 +560,6 @@ workflows:
|
||||
requires:
|
||||
# Only publish if tests and integration tests pass
|
||||
- test
|
||||
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
||||
- test_aio_local
|
||||
# Get the artifacts to publish from the build-packages-dist job
|
||||
# since the publishing script expects the legacy outputs layout.
|
||||
- build-npm-packages
|
||||
|
||||
@@ -89,3 +89,83 @@ jobs:
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Test all windows CI targets
|
||||
run: bazel test --test_tag_filters="-browser:chromium-local" //packages/compiler-cli/...
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run CI tests for framework
|
||||
run: yarn test:ci
|
||||
|
||||
aio:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Install node modules for aio
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Run AIO tests with upstream packages
|
||||
run: yarn --cwd aio test:ci
|
||||
- name: Check generated bundle sizes
|
||||
run: yarn --cwd aio payload-size
|
||||
|
||||
aio-local:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Install node modules
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Run AIO tests with local packages
|
||||
run: yarn --cwd aio test-local:ci
|
||||
- name: Check generated bundle sizes
|
||||
run: yarn --cwd aio payload-size aio-local
|
||||
|
||||
aio-deploy:
|
||||
needs: [aio]
|
||||
if: needs.aio.result == success() && github.event_name == 'push'
|
||||
runs-on:
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@45de46d9ba0e0689b7a846fd31ec9e241807ca71
|
||||
- name: Install node modules for aio
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Set the stable branch environment variable
|
||||
run: export CI_STABLE_BRANCH=$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')
|
||||
- name: Deploy aio to production
|
||||
run: yarn --cwd aio deploy-production
|
||||
|
||||
@@ -58,7 +58,6 @@ version: 3
|
||||
|
||||
availability:
|
||||
users_unavailable:
|
||||
- atscott
|
||||
- devversion
|
||||
|
||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<a name="16.2.1"></a>
|
||||
# 16.2.1 (2023-08-16)
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [232a8c1b8d](https://github.com/angular/angular/commit/232a8c1b8dadf3f886b4bd0142613d116c865759) | fix | Apply named outlets to children empty paths not appearing in the URL ([#51292](https://github.com/angular/angular/pull/51292)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="16.2.0"></a>
|
||||
# 16.2.0 (2023-08-09)
|
||||
### benchpress
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Your first Angular app</title>
|
||||
</head>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
@name HTML content was altered after server-side rendering
|
||||
@category runtime
|
||||
@shortDescription HTML content was altered after server-side rendering
|
||||
|
||||
@description
|
||||
Angular throws this error when it detects that the content generated by server-side rendering (SSR) was altered after the rendering. The process of hydration relies on the content to be untouched after SSR, which also includes whitespaces and comment nodes. Those whitespaces and comment nodes must be retained in the HTML generated by the SSR process. Learn more in the [Hydration guide](guide/hydration#constraints).
|
||||
|
||||
@debugging
|
||||
|
||||
Typically this happens in the following cases:
|
||||
* Some CDN providers have a built-in feature to remove whitespaces and comment nodes from HTML as an optimization. Please verify if there is such an option in CDN configuration and turn it off.
|
||||
* If you use custom post-processing of HTML generated by SSR (as a build step), make sure that this process doesn't remove whitespaces and comment nodes.
|
||||
+4
@@ -1,18 +1,22 @@
|
||||
<div class="tax-return">
|
||||
<div class="msg" [class.canceled]="message==='Canceled'">{{message}}</div>
|
||||
<fieldset>
|
||||
<legend>Tax Return</legend>
|
||||
<span id=name>{{taxReturn.name}}</span>
|
||||
<span id=tid>TID: {{taxReturn.tid}}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Tax Return</legend>
|
||||
<label for="income">
|
||||
Income: <input id="income" [(ngModel)]="taxReturn.income" class="num">
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Tax Return</legend>
|
||||
<span>Tax: {{taxReturn.tax}}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Tax Return</legend>
|
||||
<button type="button" (click)="onSaved()">Save</button>
|
||||
<button type="button" (click)="onCanceled()">Cancel</button>
|
||||
<button type="button" (click)="onClose()">Close</button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-compose-message',
|
||||
@@ -12,7 +12,7 @@ export class ComposeMessageComponent {
|
||||
message = '';
|
||||
sending = false;
|
||||
|
||||
constructor(private router: Router) {}
|
||||
constructor(private router: Router, private route: ActivatedRoute) {}
|
||||
|
||||
send() {
|
||||
this.sending = true;
|
||||
@@ -32,7 +32,7 @@ export class ComposeMessageComponent {
|
||||
closePopup() {
|
||||
// Providing a `null` value to the named outlet
|
||||
// clears the contents of the named outlet
|
||||
this.router.navigate([{ outlets: { popup: null }}]);
|
||||
this.router.navigate([{outlets: {popup: null}}], {relativeTo: this.route.parent});
|
||||
}
|
||||
// #enddocregion closePopup
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Angular App</title>
|
||||
@@ -6,4 +7,4 @@
|
||||
<body>
|
||||
<app-root>loading</app-root>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Injection context
|
||||
|
||||
The dependency injection (DI) system relies internaly on a runtime context where the current injector is available.
|
||||
The dependency injection (DI) system relies internally on a runtime context where the current injector is available.
|
||||
This means that injectors can only work when code is executed in this context.
|
||||
|
||||
The injection context is available in these situations:
|
||||
@@ -9,7 +9,7 @@ The injection context is available in these situations:
|
||||
* In the initializer for fields of such classes.
|
||||
* In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
|
||||
* In the `factory` function specified for an `InjectionToken`.
|
||||
* Within a stack frame that is run in a injection context.
|
||||
* Within a stack frame that is run in an injection context.
|
||||
|
||||
Knowing when your are in an injection context, will allow you to use the [`inject`](api/core/inject) function to inject instances.
|
||||
|
||||
@@ -60,4 +60,4 @@ Calling [`inject`](api/core/inject) or calling `assertInInjectionContext` outsid
|
||||
|
||||
|
||||
|
||||
@reviewed 2023-04-11
|
||||
@reviewed 2023-04-11
|
||||
|
||||
@@ -97,7 +97,7 @@ Angular components, templates, and styles go here.
|
||||
| `app/app.component.html` | Defines the HTML template associated with the root `AppComponent`. |
|
||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
||||
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
||||
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here.<br><br>_Only generated when using the `--standalone` option._ |
|
||||
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here.<br><br>_This file is not generated when using the `--standalone` option._ |
|
||||
|
||||
### Application configuration files
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ For example, in the following `SelfComponent`, notice the injected `LeafService`
|
||||
|
||||
<code-example header="src/app/self-no-data/self-no-data.component.ts" path="resolution-modifiers/src/app/self-no-data/self-no-data.component.ts" region="self-no-data-component"></code-example>
|
||||
|
||||
In this example, there is a parent provider and injecting the service will return the value, however, injecting the service with `@Self()` and `@Optional()` will return `null` because `@Self()` tells the injector to stop searching in the current host element.
|
||||
In this example, there is a parent provider and injecting the service will return the value, however, injecting the service with `@Self()` and `@Optional()` will return `null` because `@Self()` tells the injector to only search in the current host element.
|
||||
|
||||
Another example shows the component class with a provider for `FlowerService`.
|
||||
In this case, the injector looks no further than the current `ElementInjector` because it finds the `FlowerService` and returns the tulip <code>🌷</code>.
|
||||
|
||||
@@ -125,6 +125,13 @@ If you choose to set this setting in your tsconfig, we recommend to set it only
|
||||
|
||||
</div>
|
||||
|
||||
<a id="cdn-configuration"></a>
|
||||
### CDN Optimizations
|
||||
|
||||
Many CDNs offer a feature that will try to optimize your rendered application by stripping all nodes from the rendered DOM that it thinks are unnecessary, which includes comment nodes. Comment nodes are an essential part of Angular's functioning and are critical for hydration to work. You will need to disable this CDN feature in order to ensure your application loads and hydrates.
|
||||
|
||||
If CDN optimization is enabled and you have hydration enabled, when you attempt to load the page you will encounter error [NG0507](https://angular.io/errors/NG0507). If you see this error, you should go and disable the CDN optimization.
|
||||
|
||||
### Custom or Noop Zone.js are not yet supported
|
||||
|
||||
Hydration relies on a signal from Zone.js when it becomes stable inside an application, so that Angular can start the serialization process on the server or post-hydration cleanup on the client to remove DOM nodes that remained unclaimed.
|
||||
@@ -184,4 +191,4 @@ re-rendering those components from scratch.
|
||||
|
||||
There are a number of third party libraries that depend on DOM manipulation to be able to render. D3 charts is a prime example. These libraries worked without hydration, but they may cause DOM mismatch errors when hydration is enabled. For now, if you encounter DOM mismatch errors using one of these libraries, you can add the `ngSkipHydration` attribute to the component that renders using that library.
|
||||
|
||||
@reviewed 2023-06-21
|
||||
@reviewed 2023-08-14
|
||||
|
||||
@@ -1623,6 +1623,29 @@ This time, the value of `'popup'` is `null`.
|
||||
That's not a route, but it is a legitimate value.
|
||||
Setting the popup `RouterOutlet` to `null` clears the outlet and removes the secondary popup route from the current URL.
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
**Note:** All commands in the array passed to `Router.navigate()` target a _specific segment_ in the `UrlTree`.
|
||||
We specify the parent of the `ActivatedRoute` as the `relativeTo` option because we want to remove `'popup'` from the segment which holds its reference.
|
||||
It's important to always be aware of which segments the commands will be applied to.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When `relativeTo` is not provided to the `Router.navigate()` method, the commands are processed starting at the root.
|
||||
We could omit the `relativeTo` option in this particular example because the `'popup'` outlet appears at the root level of the configuration.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If you want to close an outlet which appears at any segment depth, you could accomplish
|
||||
this by creating a `UrlTree` from the current URL, recursively clearing segment `children` matching the outlet name, and finally
|
||||
calling `Router.navigateByUrl()` with the `root` segment of the current `UrlTree`.
|
||||
|
||||
</div>
|
||||
|
||||
<a id="guards"></a>
|
||||
<a id="milestone-5-route-guards"></a>
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Congratulations, your application has been converted to standalone 🎉. These a
|
||||
* Find and remove any remaining `NgModule` declarations: since the ["Remove unnecessary NgModules" step](#remove-unnecessary-ngmodules) cannot remove all modules automatically, you may have to remove the remaining declarations manually.
|
||||
* Run the project's unit tests and fix any failures.
|
||||
* Run any code formatters, if the project uses automatic formatting.
|
||||
* Run any linters in your project and fix new warnings. Some linters support a `--fix` flag that may resolve so warnings automatically.
|
||||
* Run any linters in your project and fix new warnings. Some linters support a `--fix` flag that may resolve some of your warnings automatically.
|
||||
|
||||
## Migration modes
|
||||
The migration has the following modes:
|
||||
|
||||
@@ -86,7 +86,7 @@ To clarify how changes are detected and values updated, consider the following c
|
||||
value = v;
|
||||
// call detectChange manually
|
||||
detectChange();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Example 5: some other asynchronous APIs
|
||||
document.getElementById('canvas').toBlob(blob => {
|
||||
|
||||
@@ -574,6 +574,11 @@
|
||||
"desc": "NGX Cookie Service is an Angular Library that can be used to read, set and delete browser cookies",
|
||||
"title": "NGX Cookie Service",
|
||||
"url": "https://github.com/stevermeister/ngx-cookie-service"
|
||||
},
|
||||
"flowbite": {
|
||||
"desc": "Open-source UI component library built with the utility-classes from Tailwind CSS and with official support for Angular.",
|
||||
"title": "Flowbite",
|
||||
"url": "https://flowbite.com/docs/getting-started/angular/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ set -eu -o pipefail
|
||||
readonly thisDir=$(cd $(dirname $0); pwd)
|
||||
readonly parentDir=$(dirname $thisDir)
|
||||
readonly target=${1:-aio}
|
||||
readonly PROJECT_ROOT=$(realpath "$(dirname ${thisDir})/..")
|
||||
|
||||
# Track payload size functions
|
||||
source ../scripts/ci/payload-size.sh
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
</code-example>
|
||||
</td>
|
||||
</tr>
|
||||
{%- if option.description or option.usageNotes %}
|
||||
<tr>
|
||||
<td>
|
||||
{%- if option.description %}
|
||||
@@ -53,6 +54,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
|
||||
@@ -85,7 +85,7 @@ export const enum RuntimeErrorCode {
|
||||
// (undocumented)
|
||||
MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP = 402,
|
||||
// (undocumented)
|
||||
MISSING_SSR_CONTENT_INTEGRITY_MARKER = 507,
|
||||
MISSING_SSR_CONTENT_INTEGRITY_MARKER = -507,
|
||||
// (undocumented)
|
||||
MISSING_ZONEJS = 908,
|
||||
// (undocumented)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "16.2.0",
|
||||
"version": "16.2.1",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
|
||||
@@ -33,6 +33,7 @@ import {HttpEvent} from './response';
|
||||
* @publicApi
|
||||
*
|
||||
* @see [HTTP Guide](guide/http-intercept-requests-and-responses)
|
||||
* @see {@link HttpInterceptorFn}
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
@@ -85,8 +86,35 @@ export type HttpHandlerFn = (req: HttpRequest<unknown>) => Observable<HttpEvent<
|
||||
* request) is provided. Most interceptors will delegate to this function, but that is not required
|
||||
* (see `HttpHandlerFn` for more details).
|
||||
*
|
||||
* `HttpInterceptorFn`s have access to `inject()` via the `EnvironmentInjector` from which they were
|
||||
* configured.
|
||||
* `HttpInterceptorFn`s are executed in an [injection context](/guide/dependency-injection-context).
|
||||
* They have access to `inject()` via the `EnvironmentInjector` from which they were configured.
|
||||
*
|
||||
* @see [HTTP Guide](guide/http-intercept-requests-and-responses)
|
||||
* @see {@link withInterceptors}
|
||||
*
|
||||
* @usageNotes
|
||||
* Here is a noop interceptor that passes the request through without modifying it:
|
||||
* ```typescript
|
||||
* export const noopInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next:
|
||||
* HttpHandlerFn) => {
|
||||
* return next(modifiedReq);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* If you want to alter a request, clone it first and modify the clone before passing it to the
|
||||
* `next()` handler function.
|
||||
*
|
||||
* Here is a basic interceptor that adds a bearer token to the headers
|
||||
* ```typescript
|
||||
* export const authenticationInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next:
|
||||
* HttpHandlerFn) => {
|
||||
* const userToken = 'MY_TOKEN'; const modifiedReq = req.clone({
|
||||
* headers: req.headers.set('Authorization', `Bearer ${userToken}`),
|
||||
* });
|
||||
*
|
||||
* return next(modifiedReq);
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export type HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) =>
|
||||
Observable<HttpEvent<unknown>>;
|
||||
|
||||
@@ -190,6 +190,7 @@ export const DATE_PIPE_DEFAULT_OPTIONS =
|
||||
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
|
||||
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
|
||||
* {{ dateObj | date:'mm:ss' }} // output is '43:11'
|
||||
* {{ dateObj | date:"MMM dd, yyyy 'at' hh:mm a" }} // output is 'Jun 15, 2015 at 09:43 PM'
|
||||
* ```
|
||||
*
|
||||
* ### Usage example
|
||||
|
||||
@@ -155,7 +155,7 @@ export function inject<T>(token: ProviderToken<T>, options: InjectOptions&{optio
|
||||
export function inject<T>(token: ProviderToken<T>, options: InjectOptions): T|null;
|
||||
/**
|
||||
* Injects a token from the currently active injector.
|
||||
* `inject` is only supported in a [injection context](/guide/dependency-injection-context). It can
|
||||
* `inject` is only supported in an [injection context](/guide/dependency-injection-context). It can
|
||||
* be used during:
|
||||
* - Construction (via the `constructor`) of a class being instantiated by the DI system, such
|
||||
* as an `@Injectable` or `@Component`.
|
||||
|
||||
@@ -78,7 +78,7 @@ export const enum RuntimeErrorCode {
|
||||
INVALID_SKIP_HYDRATION_HOST = -504,
|
||||
MISSING_HYDRATION_ANNOTATIONS = -505,
|
||||
HYDRATION_STABLE_TIMEDOUT = -506,
|
||||
MISSING_SSR_CONTENT_INTEGRITY_MARKER = 507,
|
||||
MISSING_SSR_CONTENT_INTEGRITY_MARKER = -507,
|
||||
|
||||
// Signal Errors
|
||||
SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT = 600,
|
||||
|
||||
@@ -177,11 +177,15 @@ export function withDomHydration(): EnvironmentProviders {
|
||||
const appRef = inject(ApplicationRef);
|
||||
const injector = inject(Injector);
|
||||
return () => {
|
||||
// Wait until an app becomes stable and cleanup all views that
|
||||
// were not claimed during the application bootstrap process.
|
||||
// The timing is similar to when we start the serialization process
|
||||
// on the server.
|
||||
//
|
||||
// Note: the cleanup task *MUST* be scheduled within the Angular zone
|
||||
// to ensure that change detection is properly run afterward.
|
||||
whenStable(appRef, injector).then(() => {
|
||||
// Wait until an app becomes stable and cleanup all views that
|
||||
// were not claimed during the application bootstrap process.
|
||||
// The timing is similar to when we start the serialization process
|
||||
// on the server.
|
||||
NgZone.assertInAngularZone();
|
||||
cleanupDehydratedViews(appRef);
|
||||
|
||||
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
||||
@@ -237,7 +241,7 @@ function verifySsrContentsIntegrity(): void {
|
||||
typeof ngDevMode !== 'undefined' && ngDevMode &&
|
||||
'Angular hydration logic detected that HTML content of this page was modified after it ' +
|
||||
'was produced during server side rendering. Make sure that there are no optimizations ' +
|
||||
'that remove comment nodes from HTML are enabled on your CDN. Angular hydration ' +
|
||||
'that remove comment nodes from HTML enabled on your CDN. Angular hydration ' +
|
||||
'relies on HTML produced by the server, including whitespaces and comment nodes.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,23 +900,35 @@ export const Output: OutputDecorator = makePropDecorator('Output', (alias?: stri
|
||||
*/
|
||||
export interface HostBindingDecorator {
|
||||
/**
|
||||
* Decorator that marks a DOM property as a host-binding property and supplies configuration
|
||||
* metadata.
|
||||
* Angular automatically checks host property bindings during change detection, and
|
||||
* if a binding changes it updates the host element of the directive.
|
||||
* Decorator that marks a DOM property or an element class, style or attribute as a host-binding
|
||||
* property and supplies configuration metadata. Angular automatically checks host bindings during
|
||||
* change detection, and if a binding changes it updates the host element of the directive.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* The following example creates a directive that sets the `valid` and `invalid`
|
||||
* properties on the DOM element that has an `ngModel` directive on it.
|
||||
* class, a style color, and an id on the DOM element that has an `ngModel` directive on it.
|
||||
*
|
||||
* ```typescript
|
||||
* @Directive({selector: '[ngModel]'})
|
||||
* class NgModelStatus {
|
||||
* constructor(public control: NgModel) {}
|
||||
* // class bindings
|
||||
* @HostBinding('class.valid') get valid() { return this.control.valid; }
|
||||
* @HostBinding('class.invalid') get invalid() { return this.control.invalid; }
|
||||
* }
|
||||
*
|
||||
* // style binding
|
||||
* @HostBinding('style.color') get color() { return this.control.valid ? 'green': 'red'; }
|
||||
*
|
||||
* // style binding also supports a style unit extension
|
||||
* @HostBinding('style.width.px') @Input() width: number = 500;
|
||||
*
|
||||
* // attribute binding
|
||||
* @HostBinding('attr.aria-required')
|
||||
* @Input() required: boolean = false;
|
||||
*
|
||||
* // property binding
|
||||
* @HostBinding('id') get id() { return this.control.value?.length ? 'odd': 'even'; }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
@@ -940,6 +952,10 @@ export interface HostBindingDecorator {
|
||||
export interface HostBinding {
|
||||
/**
|
||||
* The DOM property that is bound to a data property.
|
||||
* This field also accepts:
|
||||
* * classes, prefixed by `class.`
|
||||
* * styles, prefixed by `style.`
|
||||
* * attributes, prefixed by `attr.`
|
||||
*/
|
||||
hostPropertyName?: string;
|
||||
}
|
||||
|
||||
@@ -338,10 +338,10 @@ function updateSegmentGroupChildren(
|
||||
} else {
|
||||
const outlets = getOutlets(commands);
|
||||
const children: {[key: string]: UrlSegmentGroup} = {};
|
||||
// If the set of commands does not apply anything to the primary outlet and the child segment is
|
||||
// an empty path primary segment on its own, we want to apply the commands to the empty child
|
||||
// path rather than here. The outcome is that the empty primary child is effectively removed
|
||||
// from the final output UrlTree. Imagine the following config:
|
||||
// If the set of commands applies to anything other than the primary outlet and the child
|
||||
// segment is an empty path primary segment on its own, we want to apply the commands to the
|
||||
// empty child path rather than here. The outcome is that the empty primary child is effectively
|
||||
// removed from the final output UrlTree. Imagine the following config:
|
||||
//
|
||||
// {path: '', children: [{path: '**', outlet: 'popup'}]}.
|
||||
//
|
||||
@@ -359,8 +359,8 @@ function updateSegmentGroupChildren(
|
||||
// `UrlSegmentGroup` that is created from an "unsquashed"/expanded `ActivatedRoute` tree.
|
||||
// This code effectively "squashes" empty path primary routes when they have no siblings on
|
||||
// the same level of the tree.
|
||||
if (!outlets[PRIMARY_OUTLET] && segmentGroup.children[PRIMARY_OUTLET] &&
|
||||
segmentGroup.numberOfChildren === 1 &&
|
||||
if (Object.keys(outlets).some(o => o !== PRIMARY_OUTLET) &&
|
||||
segmentGroup.children[PRIMARY_OUTLET] && segmentGroup.numberOfChildren === 1 &&
|
||||
segmentGroup.children[PRIMARY_OUTLET].segments.length === 0) {
|
||||
const childrenOfEmptyChild =
|
||||
updateSegmentGroupChildren(segmentGroup.children[PRIMARY_OUTLET], startIndex, commands);
|
||||
|
||||
@@ -231,6 +231,39 @@ describe('createUrlTree', async () => {
|
||||
.toEqual('/case/(foo:foo)');
|
||||
});
|
||||
|
||||
it('can change both primary and named outlets under an empty path', async () => {
|
||||
router.resetConfig([{
|
||||
path: 'foo',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: class {},
|
||||
children: [
|
||||
{path: 'bar', component: class {}},
|
||||
{path: 'baz', component: class {}, outlet: 'other'},
|
||||
],
|
||||
},
|
||||
]
|
||||
}]);
|
||||
|
||||
await router.navigateByUrl('/foo/(bar//other:baz)');
|
||||
expect(router.url).toEqual('/foo/(bar//other:baz)');
|
||||
expect(router
|
||||
.createUrlTree(
|
||||
[
|
||||
{
|
||||
outlets: {
|
||||
other: null,
|
||||
primary: ['bar'],
|
||||
},
|
||||
},
|
||||
],
|
||||
// relative to the root '' route
|
||||
{relativeTo: router.routerState.root.firstChild})
|
||||
.toString())
|
||||
.toEqual('/foo/bar');
|
||||
});
|
||||
|
||||
describe('absolute navigations', () => {
|
||||
it('with and pathless root', async () => {
|
||||
router.resetConfig([
|
||||
|
||||
Reference in New Issue
Block a user