This commit updates the tracking of dirty child views to be a flag
rather than a counter. This is a much more simple method and less likely
to get into the same 'always-wrong' situation that could happen with the
counter (if it is off by 1 once, it's off by 1 forever and you either
get infinite change detection or your view is never refreshed).
PR Close#51515
Fixes that, depending on the matching and import order, in some cases we weren't throwing the error saying that a directive matched multiple times on the same element.
Fixes#52072.
PR Close#52073
This commit adds the code to mark newly created embedded views (that represent `@defer` block states) as dirty to indicate that the view sgould be checked during the next change detection cycle.
Resolves#52094.
PR Close#52095
This change removes the `mutate` method from the `WritableSignal` interface and
completely drops it from the public API surface.
The initial API proposal for Angular signals included the mutate method, allowing
in-place modification of JS objects, without changing their references (identity).
This was based on the reasoning that identity change on modification is not necessary
as we can send the “modified” notification through the signals graph.
Unfortunately the signal-specific change notification is lost as soon as we read
signal value outside of a reactive context (outside of a reactive graph).
In other words - any code outside of the Angular signals library can’t know
that an object is modified.
Secondly, to make the mutate method work, we’ve defaulted the signal value equality function
to the one that considers non-primitive values as always different.
This is unfortunate for people working with immutable data structures
(this is notably the case for the popular state management libraries)
as the default equality function de-optimizes memoization in computed,
making the application less performant.
Given the above reasons we prefer to remove the mutate method in the signals library -
at least for now. There are just too many sharp edges and tradeoffs that we don’t fully
understand yet.
BREAKING CHANGE:
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]);
```
PR Close#51986
This commit reorganizes the Angular code a bit, and moves signals into a
newly defined `@angular/core/primitives` location. This will be used inside
g3 to allow non-Angular targets to depend on the signals core without
incurring a dependency on the whole framework.
PR Close#51986
This commit refactors the signals API surface of Angular out of the
//packages/core/src/signals package. This is done in preparation of moving
the core signals package into a new 'primitives' package that's decoupled
from the public API.
PR Close#51986
`provideClientHydration()` accepts new `HydrationFeature` : `HttpTransferCacheOptions`.
`withHttpTransferCacheOptions` accepts an option object:
* `includeHeaders` : list of headers entries to keep in the cache with the request
* `filter` a callback to determine if a request should be cached
* `includePostRequests`: to include POST requests in the allowed methods
Implements some of the features requested in #50117
PR Close#52029
This change removes the `mutate` method from the `WritableSignal` interface and
completely drops it from the public API surface.
The initial API proposal for Angular signals included the mutate method, allowing
in-place modification of JS objects, without changing their references (identity).
This was based on the reasoning that identity change on modification is not necessary
as we can send the “modified” notification through the signals graph.
Unfortunately the signal-specific change notification is lost as soon as we read
signal value outside of a reactive context (outside of a reactive graph).
In other words - any code outside of the Angular signals library can’t know
that an object is modified.
Secondly, to make the mutate method work, we’ve defaulted the signal value equality function
to the one that considers non-primitive values as always different.
This is unfortunate for people working with immutable data structures
(this is notably the case for the popular state management libraries)
as the default equality function de-optimizes memoization in computed,
making the application less performant.
Given the above reasons we prefer to remove the mutate method in the signals library -
at least for now. There are just too many sharp edges and tradeoffs that we don’t fully
understand yet.
BREAKING CHANGE:
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]);
```
PR Close#51821
This commit adds runtime code to support `after` and `minimum` parameters in the `@placeholder` and `@loading` blocks. The code uses the `TimerScheduler` service added earlier for `on timer` triggers.
PR Close#52009
Adds an `UnknownBlock` node to the Ivy AST to represent blocks that haven't been recognized by the compiler. This will make it easier to integrate blocks into the language service.
PR Close#52047
Revert (with improvements of): dcf18dc74c
We recently landed a change that allows `toSignal` to be called
from within reactive contexts (e.g. `effect`/`computed`). After
more thorough investigatio and consideration with the team, we
feel like allowing `toSignal` to be called in such contexts is
encouraging non-ideal / hard-to-notice code patterns.
e.g. a new subscription to an observable is made every time `toSignal`
is invoked. There is no caching done here. Additionally, multiple new
subscriptions can trigger unintended side-effects- that may slow down
the app, result in incorrect/unexpected behavior or perform unnecessary
work.
Users should instead move the `toSignal` call outside of the `computed`
or `effect` and then read the signal values from within their `computed`. e.g.
```ts
computed(() => {
const smth = toSignal(coldObservable$)
return smth() + 2;
}
```
--> should instead be:
```ts
const smth = toSignal(coldObsverable$);
computed(() => smth() + 2);
```
In cases where a new subscription for each invocation is actually intended, a manual
subscription can be made. That way it's also much more obvious to users
that they are triggering side-effects every time, or causing new
subscriptions.
PR Close#52049
Upgrade the existing warning so it now logs an error instead, when an LCP element is determined to not be usings the `priority` attribute. Error is logged, not thrown.
PR Close#52004
Currently, if there are 2 nested @defer blocks with the same dependency, Angular throws an error at runtime to indicate that there was a duplicate component def in the registry. This commit updates the logic to only append dependencies when they didn't previously exist in the registry.
PR Close#51964
This partially reverts commit a3e17190e7
and deprecates behavior added.
The context of an embedded view ref at some point was switched from a
getter to an actual assignable property. This is something we revert
as it introduces additional complexity for our generated code
(in terms of closures capturing the `ctx`), creates technical
limitations for Angular's internals and the usage pattern is rarely
used (and can be addressed via simple assignments, `Object.assign` or
the use of a proxy if replacing the full context object is still
desirable)
DEPRECATED: Swapping out the context object for `EmbeddedViewRef`
is no longer supported. Support for this was introduced with v12.0.0, but
this pattern is rarely used. There is no replacement, but you can use
simple assignments in most cases, or `Object.assign , or alternatively
still replace the full object by using a `Proxy` (see `NgTemplateOutlet`
as an example).
Also adds a warning if the deprecated
PR Close#51887
When adding a new view flag, you currently need to adjust the last number of the last
3 flags. All of these share the same number so the shifting ones can just use
the base-10 IndexWithinInitPhaseShift.
PR Close#51839
The new list reconcilation algorithm, an alternative to
the DefaultIterableListDiffer. It works by performing updates
in place instead of creating intermediate data describing changes
to apply. For lists expressed as an Array it performs additional
optimizations for the moves and swap scenarios.
The new list diffing approach is meant to be used in the new control
flow and should me much faster as compared to the ngFor with the
DefaultIterableListDiffer.
PR Close#51980
The template pipeline now supports basic forms of `defer` blocks. This includes the `loading`, `placeholder`, and `error` blocks, as well as the loading and placeholder configuration options.
Lazy dependencies and prefetch are not yet implemented.
PR Close#51942
Enables the new `@` block syntax by default by removing the `enabledBlockTypes` flags. There are still some internal flags that allow special use cases to opt out of the block syntax, like during XML parsing and when compiling older libraries (see #51979).
PR Close#51994
This is a deceptively simple fix for a deep issue. Consider the following template:
```
<button [title]="myTitle" [id]="(auth().identity() | async)" [tabindex]="1">
```
`TemplateDefinitionBuilder` allocates the following variable (binding) slots:
v[0] = [title] binding
v[1] = [id] binding
v[2] = [tabindex] binding
v[3] = pipe binding
v[4] = pipe binding
As you can see, all three top-level property bindings were assigned variable indices. Then, variables for nested expressions were assigned.
Before this change, Template Pipeline would choose the following order:
v[0] = [title] binding
v[1] = [id] binding
v[2] = pipe binding
v[3] = pipe binding
v[4] = [tabindex] binding
With this order, nested expressions have their variables counted and assigned before subsequent top-level property bindings. This results in different variable indices for `pipeBinding` expressions that are not inside the final property binding.
However, this is not just different -- it's actually incorrect! Consider a case like the following:
```
<button [p1]="c ? (a | pipe) : 3" [p2]="b | pipe">
```
These pipe bindings are executed *conditionally*. This means that, because we don't count and assign all the "fixed" variable slots first, i.e. those belonging to the property bindings, their indices might end up incorrect, depending on whether or not a pipeBinding happened as part of the update block.
With this change, we count all variables on top-level ops first, and then descend into all expressions.
PR Close#51961
Fixes an issue where if animations are enabled, deferred blocks don't remove their placeholder blocks immediately from the DOM. The problem is that we register the event handlers in `afterRender` which runs outside the zone, but the logic that removes the DOM nodes during animations is tied to change detection.
These changes resolve the issue by binding the listeners inside the zone. This was the intention from the beginning, I just forgot that `afterRender` runs outside the zone.
Fixes#51970.
PR Close#51971
Currently, there is no change detection scheduled after triggering `on idle` condition, since `requestIdleCallback` is not patched by Zone.js. This commit invokes the callback in NgZone, so that the code that is invoked within the callback can use zones and a new change detection round is scheduled as needed.
Fixes#51973.
PR Close#51975
This API allows for inspection of a given injector to determine it's type (Element, Environment, Null) as well as it's "source".
- For Environment injectors the source is the source of the injector; `injector.source`.
- For Element injectors the name is the DOM Element that created the injector.
- For the Null Injector this is the string `"Null Injector"`.
PR Close#51900
This commit adds hydration support for repeaters (for loops) and empty blocks. The logic looks up a dehydrated view and use this information for hydration. Otherwise, DOM elements for a view are created from scratch.
PR Close#51920
`provideLazyLoadedAnimations()` returns providers which allow the lazy loading of the animation module.
Lazy loading of the animation code can shave off up to 16KB gzipped of the main bundle.
PR Close#50738
Adds support for defining `viewport`, `interaction` and `hover` triggers with no parameters. If the framework encounters such a case, it resolves the trigger to the root element of the `@placeholder` block. Triggers with no parameters have the following restrictions:
1. They have to be placed on an `@defer` block that has an `@placeholder`.
2. The `@placeholder` can only have one root node.
3. The root placeholder node has to be an element.
PR Close#51922
Prior to this change `this.isStable.pipe(first((isStable) => isStable)).toPromise()` had to be done in multiple places across the framework and the Angular CLI see https://github.com/angular/angular-cli/pull/25856#discussion_r1328158846. In the majority of cases an Observable based `isStable` API is not needed. This also removes the need for RXJS operator imports.
PR Close#51807
This commit updates the `if` and `switch` logic to support hydration. The logic attempts to find dehydrated views in containers while processing `if` and `switch` instructions. If a dehydrated is found, its used to further match elements. Otherwise, DOM elements for a view are created from scratch.
PR Close#51915
Previously, dehydrated views lookup was triggered only when ViewContainerRef was injected. The new control flow logic uses lower level APIs, thus having the code only in the ViewContainerRef is not sufficient.
This commit adds the logic to invoke the process of dehydrated views lookup from the `template` instruction, thus enabling it for new control flow instructions as well.
PR Close#51915
This commit updates hydration runtime code to avoid creating an empty array when we can avoid it. Instead, we just check whether the field is `null` directly (without using nullish coalescing).
PR Close#51917