This commit implements a simple tracker of the pending tasks during initial rendering. The class allows adding and removing tasks from the set. The class also exposes a promise that gets resolved once the last task is removed.
This tracker is needed to keep track of ongoing processes like Router navigation (and potentially HTTP requests) and acts as a signaling mechanism to SSR and hydration that the application is in the "stable" state and a serialization can be performed.
This class would also act as a future replacement for the `ApplicationRef.isStable` for zoneless applications.
PR Close#49576
When navigating in the Router, the current approach does the redirects
and the creation of the `RouterStateSnapshot` in two separate steps
(applyRedirects and recognize). These two steps duplicate the route
matching logic, resulting in user code on routes being executing twice
(custom `UrlMatcher` and `canMatch` guards). This also duplicates the
complex matching logic in two places, which increases the bundle size
and maintenance burden.
This commit combines the `applyRedirects` and `recognize` steps into a
single matching algorithm.
fixes#26081
PR Close#49163
`ComponentRef.setInput` currently sets the input on the component regardless
of the previous value the method was called with. This results in
different behavior from bindings in templates, which only set inputs
when the value differs in the `Object.is` check from its previous value.
BREAKING CHANGE: ComponentRef.setInput will only set the input on the
component if it is different from the previous value (based on `Object.is`
equality). If code relies on the input always being set, it should be
updated to copy objects or wrap primitives in order to ensure the input
value differs from the previous call to `setInput`.
PR Close#49607
Currently we are unsafely unquoting CSS values which in some cases causes valid values to become invalid and invalid values to become valid.
Example:
```html
<div style="width:"1px;""></div>
```
In the above case, `width` has an invalid value of `"1px"`, however the compiler will transform it to `1px` which makes it valid.
On the other hand, in the below case
```html
<div style="content:"foo""></div>
```
`content` has a valid value of `"foo"`, but since the compiler unwraps it to `foo` it becomes invalid. For correctness, we should not remove quotes.
```js
const div = document.createElement('div');
div.style.width='"1px"';
div.style.content='foo';
div.style.width; // ''
div.style.content; // ''
div.style.width='1px';
div.style.content='"foo"';
div.style.width; // '1px'
div.style.content; // '"foo"'
```
More information about values can be found https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
PR Close#49460
This commit updates the `LView` in Angular to be a `Consumer` of
signals. If a signal is read when executing a template, it marks the
view dirty. In addition, if a signal is read when executing host
bindings, it also marks views dirty.
One interesting thing about signal reads in host bindings
is that they perform a bit better than what we can do with today's
APIs. In order to re-execute host bindings for an `OnPush` component that
might have changed, you would probably inject `ChangeDetectorRef` and call
`markForCheck`. This will mark the _current component_ and parents
dirty. However, host bindings are executed as part of refreshing the
_parent_ so there is really no need to re-execute the current component
if the only thing that changed is the host bindings. When a signal is
read in host bindings, it marks the parent dirty and not the component
that defined the host binding.
Additionally, this commit avoids allocating a full consumer for each
`LView` by re-using a consumer until template execution results in a
signal read. At this point, we assign that consumer to the `LView` and
create a new consumer to "tentatively" use for the future `LView`
template executions.
Co-authored-by: Dylan Hunn <github@dylanhunn.com>
PR Close#49153
`entryComponents` have been deprecated since version 9, because with Ivy they weren't necessary. These changes remove any remaining references.
BREAKING CHANGE:
* `entryComponents` has been deleted from the `@NgModule` and `@Component` public APIs. Any usages can be removed since they weren't doing anyting.
* `ANALYZE_FOR_ENTRY_COMPONENTS` injection token has been deleted. Any references can be removed.
PR Close#49484
This commit adds serialization and hydration logic for content projection.
While hydration for regular elements relies on their location in the TNode tree, the content projection may move elements around, so in order to hydrate them correcty, the runtime needs some extra information. This commit adds a serialization logic that adds element locations (instructions on how to navigate to a particular element from another known location of other element) into the hydration state for the following cases:
- when a TNode is a first element in projection segment (other nodes are linked from that node)
- when a TNode's next sibling is different before and after projection (we serialize extra info about the template-based sibling)
- when a TNode's previous sibling was a content projection (i.e. `<ng-content>` slot), because we can not rely on the previous element in this case (projection happens at a later point)
PR Close#49454
This change makes it possible to remove a previously registered destroy
callback - to do so it is enough to call the unregistration function
returned from the onDestroy method call.
PR Close#49493
This commit adds serialization and hydration logic for content projection.
While hydration for regular elements relies on their location in the TNode tree, the content projection may move elements around, so in order to hydrate them correcty, the runtime needs some extra information. This commit adds a serialization logic that adds element locations (instructions on how to navigate to a particular element from another known location of other element) into the hydration state for the following cases:
- when a TNode is a first element in projection segment (other nodes are linked from that node)
- when a TNode's next sibling is different before and after projection (we serialize extra info about the template-based sibling)
- when a TNode's previous sibling was a content projection (i.e. `<ng-content>` slot), because we can not rely on the previous element in this case (projection happens at a later point)
PR Close#49454
Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
at isCssClassMatching (core.mjs:8726:35)
at isNodeMatchingSelector (core.mjs:8814:22)
at isNodeMatchingSelectorList (core.mjs:8931:13)
at matchingProjectionSlotIndex (core.mjs:14179:13)
at Module.ɵɵprojectionDef (core.mjs:14222:49)
at MatButton_Template (button.mjs:113:99)
at executeTemplate (core.mjs:10534:9)
at renderView (core.mjs:10356:13)
at renderComponent (core.mjs:11529:5)
at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.
Created a testcase for the original fix. It causes an exception without the fix.
fix(core): add a check code to avoid an exception inside isCssClassMatching
Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Add `import { MatButtonModule } from '@angular/material/button'`,
and also MatButtonModule inside @NgModule imports in src/app/app.module.ts to use MatButtonModule.
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
at isCssClassMatching (core.mjs:8726:35)
at isNodeMatchingSelector (core.mjs:8814:22)
at isNodeMatchingSelectorList (core.mjs:8931:13)
at matchingProjectionSlotIndex (core.mjs:14179:13)
at Module.ɵɵprojectionDef (core.mjs:14222:49)
at MatButton_Template (button.mjs:113:99)
at executeTemplate (core.mjs:10534:9)
at renderView (core.mjs:10356:13)
at renderComponent (core.mjs:11529:5)
at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.
PR Close#48888
Adds support for marking a directive input as required. During template type checking, the compiler will verify that all required inputs have been specified and will raise a diagnostic if one or more are missing. Some specifics:
* Inputs are marked as required by passing an object literal with a `required: true` property to the `Input` decorator or into the `inputs` array.
* Required inputs imply that the directive can't work without them. This is why there's a new check that enforces that all required inputs of a host directive are exposed on the host.
* Required input diagnostics are reported through the `OutOfBandDiagnosticRecorder`, rather than generating a new structure in the TCB, because it allows us to provide a better error message.
* Currently required inputs are only supported during AOT compilation, because knowing which bindings are present during JIT can be tricky and may lead to increased bundle sizes.
Fixes#37706.
PR Close#49468
When having a recursive circle of imports on standalone components, `queueTypesFromModulesArrayRecur` triggered a `Maximum call stack size exceeded` error.
This commit fixes this.
Fixes#49469
PR Close#49473
This reverts commit 13dd614cd1.
This breaks a g3 Typescript compilation tests where diagnostics are
expected for a missing input in the component.
PR Close#49467
Angular uses inline styles to insert the styles associated with a component. This violates the strict styles [Content Security Policy](https://web.dev/strict-csp/) which doesn't allow inline styles by default. One way to allow the styles to be applied is to set a `nonce` attribute on them, but because the code for inserting the stylesheets is deep inside the framework, users weren't able to provide it without accessing private APIs.
These changes add a new `CSP_NONCE` injection token that will allow users to provide a nonce, if their app is using CSP. If the token isn't provided, the framework will look for an `ngCspNonce` attribute on the app's root node instead. The latter approach is provided as a convenience for apps that render the `index.html` through a server, e.g. `<app ngCspNonce="{% randomNonceAddedByTheServer %}"></app>`.
This PR addresses adding the nonce to framework-generated styles. There will be follow-up PRs that add support for it in critical CSS tags in the CLI, and in Angular Material.
Fixes#6361.
PR Close#49444
Adds support for marking a directive input as required. During template type checking, the compiler will verify that all required inputs have been specified and will raise a diagnostic if one or more are missing. Some specifics:
* Inputs are marked as required by passing an object literal with a `required: true` property to the `Input` decorator or into the `inputs` array.
* Required inputs imply that the directive can't work without them. This is why there's a new check that enforces that all required inputs of a host directive are exposed on the host.
* Required input diagnostics are reported through the `OutOfBandDiagnosticRecorder`, rather than generating a new structure in the TCB, because it allows us to provide a better error message.
* Currently required inputs are only supported during AOT compilation, because knowing which bindings are present during JIT can be tricky and may lead to increased bundle sizes.
Fixes#37706.
PR Close#49453
The mentioned 2 classes have been combined since it is no longer required to have a separate `SharedStylesHost` for SSR. This changes also reduces the memory usage footprint as remove 1 Map that stores the CSS strings.
PR Close#49424
This reverts commit 1a6ca68154.
This breaks tests in google3 which might be depending on private APIs. We
need to update these tests before we can land this PR.
PR Close#49449
During DOM serialization, empty text nodes are lost when the client parses the DOM. To solve this problem comment nodes are added where the empty nodes are located right before serialization. Those comments then get replaced during hydration with the proper empty text nodes.
PR Close#49419
Prior to this change, a random application ID was generated each time which forced users using server rendering to provide an application ID themselves. This was needed to handle rare cases when multiple Angular applications are rendered on the same page.
With this change the application ID is no longer generated randomly and instead it is hard coded.
BREAKING CHANGE:
The `APP_ID` token value is no longer randomly generated. If you are bootstrapping multiple application on the same page you will need to set to provide the `APP_ID` yourself.
```ts
bootstrapApplication(ComponentA, {
providers: [
{ provide: APP_ID, useValue: 'app-a' },
// ... other providers ...
]
});
```
PR Close#49422
This commit deprecated ` BrowserModule.withServerTransition` instead `APP_ID` should be used instead to configure the app id.
DEPRECATED: `BrowserModule.withServerTransition` has been deprecated. `APP_ID` should be used instead to set the application ID.
NB: Unless, you render multiple Angular applications on the same page, setting an application ID is not necessary.
Before:
```ts
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
...
]
```
After:
```ts
imports: [
BrowserModule,
{ provide: APP_ID, useValue: 'serverApp' },
...
],
```
PR Close#49422
Adds support for marking a directive input as required. During template type checking, the compiler will verify that all required inputs have been specified and will raise a diagnostic if one or more are missing. Some specifics:
* Inputs are marked as required by passing an object literal with a `required: true` property to the `Input` decorator or into the `inputs` array.
* Required inputs imply that the directive can't work without them. This is why there's a new check that enforces that all required inputs of a host directive are exposed on the host.
* Required input diagnostics are reported through the `OutOfBandDiagnosticRecorder`, rather than generating a new structure in the TCB, because it allows us to provide a better error message.
* Currently required inputs are only supported during AOT compilation, because knowing which bindings are present during JIT can be tricky and may lead to increased bundle sizes.
Fixes#37706.
PR Close#49304
This commit implements hydration support for view containers, which should make `*ngIf`, `*ngFor` and other structural directive work with hydration.
The logic also respects the `ngSkipHydration` flag and skips hydration in such cases.
PR Close#49382
This commit updates the serialization logic to include information about templates that were used to create embedded views. The information is needed during hydration to pick a correct instance of dehydrated view (this logic will be implemented in a followup commit).
PR Close#49382
With the introduction of `EnvironmentInjector`, we added an operation to run
a function with access to `inject` tokens from that injector. This operation
only worked for `EnvironmentInjector`s and not for element/node injectors.
This commit deprecates `EnvironmentInjector.runInContext` in favor of a
standalone API `runInInjectionContext`, which supports any type of injector.
DEPRECATED: `EnvironmentInjector.runInContext` is now deprecated, with
`runInInjectionContext` functioning as a direct replacement:
```typescript
// Previous method version (deprecated):
envInjector.runInContext(fn);
// New standalone function:
runInInjectionContext(envInjector, fn);
```
PR Close#49396
This commit moves the providers for `NgZone`-based change detection to a
single provider function. This function is currently called by default
in all places where `NgZone` was provided
(`bootstrapApplication`, `bootstrapModule`, and `TestBed`).
When we want to make Angular applications zoneless by default, we
can make a public provider method that has to be used in order to enable
the zone change detection features. When this method is not called,
Angular would use `NoopNgZone` by default and not initialize any
subscriptions to the `NgZone` stability events.
Side note: There are actually two places that `NgZone` is provided for `TestBed`
(providers in `compileTestModule` and `BrowserTestingModule`). This
likely doesn't need to be in both locations.
PR Close#49373
Prior to this change component styles generated on the server where removed prior to the client side component being rendered and attached it's own styles. In some cases this caused flickering. To mitigate this `initialNavigation: enabledBlocking'` was introduced which allowed the remove of server styles to be defer to a latter stage when the application has finished initialization.
This commit changes the need for this, by not removing the server generated component styles and reuse them for client side rendering.
PR Close#48253
Prior to this change the component IDs where generated based on a counter. This is problematic as there is no guarantee that a component will get the same ID that was assigned on the server when generated on the client side.
This is paramount to be able to re-use the component styles generated on the server.
PR Close#48253
Before this change `ɵɵdefineDirective` called `ɵɵdefineComponent` under the hood. This is problematic for the consistent component id generation as it could result in hash collisions for certain directives. Directives however do not require an id.
This changes moves common definition generation logic into a separate function that is re-used in `ɵɵdefineDirective` and `ɵɵdefineComponent`.
PR Close#49350
The private util `isObservable` was actually just testing the same thing as`isSubscribable()`. As the implementation is closer to the function's name, let's only keep ``isSubscribable`.
PR Close#49295
This commit incrementally builds on top of https://github.com/angular/angular/pull/49271 and adds the logic to hydrate elements and text nodes that don't have any Angular features (like *ngIf/*ngFor, etc) and are not content-projected.
The subsequent commits will extend the logic further to support more complex scenarios.
Co-authored-by: Jessica Janiuk <jessicajaniuk@google.com>
Co-authored-by: Andrew Kushnir <akushnir@google.com>
PR Close#49285
Close#45698
KeyEventsPlugin now has different behavior with EventsPlugin, it will
always run inside ngZone with ngZone.runGuarded() no matter the
component is initialized inside or outside of NgZone, this PR
make sure KeyEventsPlugin bahave the same with other events.
PR Close#49330
Previously (at the early days of Ivy) a TNode used to keep an array of TViews, but the logic was changed since that time, but the `tViews` field remained on TNode interface (+ corresponding typings).
This commit renames TNode.tViews to TNode.tView and cleans up typings.
PR Close#49313
**Important note**: this is a first commit in a series of commits that will be needed
to support non-destructive hydration. Stay tuned for further updates!
This commit lays the foundation on top of which more hydration logic will be
added in follow up PRs. This PR includes:
* Initial serialization of hydration data
* Data transfer of hydration annotations from server side to client
* Accessing hydration info and populating internal data structures
* Initial APIs (currently private) that enable hydration (in a tree-shakable manner)
* Cleanup of annotations post hydration
* Initial test infrastructure and basic test cases
This commit does **not** expose any public APIs. They'll be exposed later, when
more hydration logic is implemented to a state when it can cover most common
use-cases.
Co-authored-by: Jessica Janiuk <jessicajaniuk@google.com>
Co-authored-by: Andrew Kushnir <akushnir@google.com>
PR Close#49271
* With tsc improvements some types can be refined.
* Removing unnecessary non-null assertions.
* Using std native methods instand of custom functions
* Using @ts-expect-error instead of `any` type assertion to keep code navigation.
PR Close#49206
This commits add a utility method to merge multiple `ApplicationConfiguration` into one from left to right. This is useful for server rendering were an application might have several configurations.
Usage Example:
```ts
const config = mergeApplicationConfig(appConfig, appServerConfig);
```
PR Close#49253
DestroyRef represents a concept of lifecycle scope where destroy
callbacks can be registered. Such callbacks are automatically
executed when a given scope ends it lifecycle.
In practice the most common lifecycle scopes would be represented by:
- a component or en embedded view;
- instance of `EnvironnementInjector`.
PR Close#49158