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
This change makes the `createUrlTreeFromSnapshot` added in #45877 the
default and only behavior in the Router. This now addreses #42191, #38276, #22763,
and #48472 without needing to create custom handling to
call `createUrlTreeFromSnapshot` (since it's now called by `Router.createUrlTree`).
BREAKING CHANGE: Tests which mock `ActivatedRoute` instances may need to be adjusted
because Router.createUrlTree now does the right thing in more
scenarios. This means that tests with invalid/incomplete ActivatedRoute mocks
may behave differently than before. Additionally, tests may now navigate
to a real URL where before they would navigate to the root. Ensure that
tests provide expected routes to match.
There is rarely production impact, but it has been found that relative
navigations when using an `ActivatedRoute` that does not appear in the
current router state were effectively ignored in the past. By creating
the correct URLs, this sometimes resulted in different navigation
behavior in the application. Most often, this happens when attempting to
create a navigation that only updates query params using an empty
command array, for example `router.navigate([], {relativeTo: route,
queryParams: newQueryParams})`. In this case, the `relativeTo` property
should be removed.
PR Close#48508
This commit moves the `TransferState` class implementation to `@angular/core`. This class will be needed in core in followup changes and `core` can not depend on `platform-browser` package.
The `core` package does **not** export those symbols into public API surface.
The `platform-browser` package re-exports the symbols under same names for backwards-compatibility.
PR Close#49222
This commit updates the `TNode` to include a reference to the previous sibling node. Currently, TNode has references to the next sibling and parent nodes, but in followup changes we'd need to have access to previous TNodes (to determine position of the current node).
PR Close#49223
This commit exposes `signal`, `computed`, `effect` and various helpers from
the `@angular/core` entrypoint.
These APIs are marked as `@developerPreview` and are still prototypes in
active development. Their final shapes will be subject to our internal
design reviews as well as one or more community RFCs. We're exporting them
now to allow for experimentation using 16.0.0 next and RC releases.
PR Close#49150
This commit checks in (but does not export) a prototype implementation of
Angular Signals, along with its unit test suite and a README explaining the
algorithms used.
Signals are not a new concept in the framework space, but there are many
different flavors of implementations. These differ radically both in terms
of public API as well as behavioral details (such as eager vs lazy
computation, batching behavior, equality, cleanup, nesting, etc).
This commit comprises a bespoke implementation that we've designed to best
meet Angular's needs, especially when it comes to compatibility and
flexibility of use within existing applications.
Many of the API features of this implementation of signals, as well as the
larger direction of reactivity in Angular, will be discussed in future RFCs.
Co-Authored-By: Pawel Kozlowski <pkozlowski.opensource@gmail.com>
PR Close#49091
While unlikely, prior to this change it was possible to provide a nested array of styles to the render. This required the framework to handle this by doing a flatten operation. This change also renames the `flattenStyles` method as it no longer flattens the styles.
BREAKING CHANGE: `RendererType2.styles` no longer accepts a nested arrays.
Closes#48317
PR Close#49072
Currently style of components using `encapsulation`, `None` or `Emulated` will not be removed from the DOM once the component get destroyed.
This change addresses this by keeping track of the number of times a component is rendered, when the component is destroyed the counter is decreased and once this reaches zero the style element is removed from the DOM.
Currently, this new behaviour is on opt-in bases, but it will be changed in the next major version.
To opt-in, set the `REMOVE_STYLES_ON_COMPONENT_DESTROY` DI token to `true`.
Example
```ts
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule
],
providers: [
{ provide: REMOVE_STYLES_ON_COMPONENT_DESTROY, useValue: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
```
Closes#16670
PR Close#48298
The terser update included various changes, including changes to the
inline optimizations. Constants like for the bloom filter are no longer
inlined multiple times, but instead the constant is preserved and used
with a shortened/mangled name.
Note this also applies to other symbols like `SimpleChanges` too.
This means that such new variables now show up in the symbols goldens.
Concrete examples of inlined & no-longer inlined constants can be
seen in the PR description: https://github.com/angular/angular/pull/49000#issue-1576416106
PR Close#49000
v16 will have a breaking change to the way `UrlTree`s are constructed.
This change is actually a bug fix that makes `UrlTree` creation correct
in more scenarios (see #48508). However, this can affect applications that are
relying on the current incorrect behavior. This commit adds a dev mode
warning when the target of a navigation will change once #48508 is
submitted.
PR Close#48688
the easing "prop" used to specify the easing function to
apply to animations isn't a valid css property, it is thus
considered not animatable but different values for such
property shouldn't cause non-animatable warnings
resolves#48571
PR Close#48583
Warn the user in the console in case the `ngSrcset` is present and no
loader is configured. In this case, the default loader is used and
it ignores this attribute.
PR Close#48804
Fixes that the expression converter was producing code that throws a runtime error if a non-null assertion is used as a part of a safe read, write or call.
Fixes#48742.
PR Close#48801