The codebase currently contains several `EMPTY_ARRAY` constants,
and they can end up in the bundle of an application.
A recent commit 6fbe219 tipped us off
as it introduced several `noop` occurrences in the golden symbol files.
After investigating with @petebacondarwin,
we decided to remove the duplicated symbols.
This probably shaves only a few bytes,
but this commit removes the duplicated functions,
by always using the one in `core/src/utils/empty`.
PR Close#40991
This commit adds more configurability to the `Router#isActive` method
and `RouterLinkActive#routerLinkActiveOptions`.
It allows tuning individual match options for query params and the url
tree, which were either both partial or both exact matches in the past.
Additionally, it also allows matching against the fragment and matrix
parameters.
fixes#13205
BREAKING CHANGE:
The type of the `RouterLinkActive.routerLinkActiveOptions` input was
expanded to allow more fine-tuned control. Code that previously read
this property may need to be updated to account for the new type.
PR Close#40303
Currently the only way to disable animations is by providing the `NoopAnimationsModule`
which doesn't allow for it to be disabled based on runtime information. These changes
add support for disabling animations based on runtime information by using
`BrowserAnimationsModule.withConfig({disableAnimations: true})`.
PR Close#40731
Currently `EmbeddedViewRef.context` is read-only which means that the only way to update
it is to mutate the object which can lead to some undesirable outcomes if the template
and the context are provided by an external consumer (see #24515).
These changes make the property writeable since there doesn't appear to be a specific
reason why it was readonly to begin with.
PR Close#40360
fix https://github.com/angular/components/issues/21674
When setting `ngZoneRunCoalescing` to true, `onStable` is not emitted correctly.
the reason is before this commit, the code looks like this:
```
// application code call `ngZone.run()`
ngzone.run(() => {}); // step 1
// inside NgZone, in the OnInvoke hook, NgZone try to delay the checkStable()
function delayChangeDetectionForEvents(zone: NgZonePrivate) {
if (zone.lastRequestAnimationFrameId !== -1) { // step 9
return;
}
zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(global, () => { // step 2
if (!zone.fakeTopEventTask) {
zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
zone.lastRequestAnimationFrameId = -1; // step 3
updateMicroTaskStatus(zone); // step 4
checkStable(zone); // step 6
}, undefined, () => {}, () => {});
}
zone.fakeTopEventTask.invoke();
});
updatemicroTaskStatus(zone);
}
function updateMicroTaskStatus(zone: NgZonePrivate, ignoreCheckRAFId = false) {
if (zone._hasPendingMicrotasks ||
((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
zone.lastRequestAnimationFrameId !== -1)) { // step 5
zone.hasPendingMicrotasks = true;
} else {
zone.hasPendingMicrotasks = false;
}
}
function checkStable(zone: NgZonePrivate) {
if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { // step 7
try {
zone._nesting++;
zone.onMicrotaskEmpty.emit(null);
...
}
// application ref subscribe onMicroTaskEmpty
ngzone.onMicroTaskEmpty.subscribe(() => {
ngzone.run(() => { // step 8
tick();
});
});
```
and the process is:
1. step 1: application call ngZone.run()
2. step 2: NgZone delay the checkStable() call in a requestAnimationFrame, and also set
zone.lastRequestAnimationFrameId
3. step 3: Inside the requestAnimationFrame callback, reset zone.lastRequestAnimationFrameId first
4. step 4: update microTask status
5, step 5: if zone.lastRequestAnimationFrameId is -1, that means no microTask pending.
6. step 6: checkStable and trigger onMicrotaskEmpty emitter.
7. step 7: ApplicationRef subscribed onMicrotaskEmpty, so it will call another `ngZone.run()` to process
tick()
8. step 8: And this new `ngZone.run()` will try to check `zone.lastRequestAnimationFrameId` in `step 9`
when trying to delay the checkStable(), and since the zone.lastRequestAnimationFrameId is already reset
to -1 in step 3, so this ngZone.run() will run into step 2 again.
9. and become a infinite loop..., so onStable is never emit
in this commit, there is a new flag `zone.isCheckStableRunning` added to
prevent re-entry when `shouldCoaleascing` flag is enabled.
PR Close#40540
Currently TestBed (both ViewEngine and Ivy) invoke `ApplicationInitStatus.runInitializers` as a part of the
bootstrap process to mimic real bootstrap steps. This is problematic for the `ApplicationInitStatus` class
tests since the `runInitializers` call performed by TestBed interfere with actual tests.
This commit updates ApplicationInitStatus tests to interact with the class directly instead of relying on TestBed
APIs to retrieve the class though DI.
PR Close#33222
This commit adds support for Observables that now can be used as a part of APP_INITIALIZER. Previously, only
Primises were supported.
Closes#15088.
PR Close#33222
This PR performs a small refactoring to use `RuntimeError` class and corresponding error code (by calling
`throwProviderNotFoundError` which formats the message) to make it more consistent with other places where
similar errors are thrown.
PR Close#40901
This commit implements creating of `ɵɵngDeclarePipe()` calls in partial
compilation, and processing of those calls in the linker and JIT compiler.
See #40677
PR Close#40803
This commit fixes the issue of the ASSERTION ERROR issue when
a projected node(RNode) inside an array is checked against the types
of TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer,
TNodeType.IcuContainer, TNodeType.Projection. As it's inside an array,
it doesn't fall into any of those types, as a result, it throws
the ASSERTION ERROR.
PR Close#37120
PR Close#37167
Prior to this patch, if an element was removed multiple times (due
to the nature of parent/child elements), the leave listeners may
have been fired for an element that was already removed. This patch
adds a guard within the animations code to prevent this.
PR Close#40712
fix https://github.com/angular/components/issues/21674
When setting `ngZoneRunCoalescing` to true, `onStable` is not emitted correctly.
The reason is before this commit, the code looks like this
```
// Application code call `ngZone.run()`
ngZone.run(() => {}); // step 1
// Inside NgZone, in the OnInvoke hook, NgZone try to delay the checkStable()
function delayChangeDetectionForEvents(zone: NgZonePrivate) {
if (zone.lastRequestAnimationFrameId !== -1) { // step 9
return;
}
zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(global, () => { // step 2
if (!zone.fakeTopEventTask) {
zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
zone.lastRequestAnimationFrameId = -1; // step 3
updateMicroTaskStatus(zone); // step 4
checkStable(zone); // step 6
}, undefined, () => {}, () => {});
}
zone.fakeTopEventTask.invoke();
});
updateMicroTaskStatus(zone);
}
function updateMicroTaskStatus(zone: NgZonePrivate, ignoreCheckRAFId = false) {
if (zone._hasPendingMicrotasks ||
((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
zone.lastRequestAnimationFrameId !== -1)) { // step 5
zone.hasPendingMicrotasks = true;
} else {
zone.hasPendingMicrotasks = false;
}
}
function checkStable(zone: NgZonePrivate) {
if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { // step 7
try {
zone._nesting++;
zone.onMicrotaskEmpty.emit(null);
...
}
// application ref subscribe onMicroTaskEmpty
ngZone.onMicroTaskEmpty.subscribe(() => {
ngZone.run(() => { // step 8
tick();
});
});
```
And the process is:
1. step 1: application call ngZone.run()
2. step 2: NgZone delay the checkStable() call in a requestAnimationFrame, and also set
zone.lastRequestAnimationFrameId
3. step 3: Inside the requestAnimationFrame callback, reset zone.lastRequestAnimationFrameId first
4. step 4: update microTask status
5, step 5: if zone.lastRequestAnimationFrameId is -1, that means no microTask pending.
6. step 6: checkStable and trigger onMicrotaskEmpty emitter.
7. step 7: ApplicationRef subscribed onMicrotaskEmpty, so it will call another `ngZone.run()` to process
tick()
8. step 8: And this new `ngZone.run()` will try to check `zone.lastRequestAnimationFrameId` in `step 9`
when trying to delay the checkStable(), and since the zone.lastRequestAnimationFrameId is already reset
to -1 in step 3, so this ngZone.run() will run into step 2 again.
9. And become a infinite loop..., so onStable is never emit
In this commit, the `zone.lastRequestAnimationFrameId` reset is moved after `checkStable()` call.
PR Close#40540
The codebase currently contains several `EMPTY_ARRAY` constants,
and they can end up in the bundle of an application.
A recent commit 6fbe219 tipped us off
as it introduced several `noop` occurrences in the golden symbol files.
After investigating with @petebacondarwin,
we decided to remove the duplicated symbols.
This probably shaves only a few bytes,
but this commit removes the duplicated functions,
by always using the one in `core/src/utils/empty`.
PR Close#40587
Previously, in `_mismatch()`, the `DefaultIterableDiffer` first checks
`_linkedRecords` for `itemTrackBy`, then checks `_unlinkedRecords`.
This cause the `DefaultIterableDiffer` to move "later" items that match the
`itemTrackBy` from the old collection, rather than using the "earlier" one.
Now we check `_unlinkedRecords` first, so that the `DefaultIterableDiffer`
can give a more stable and reasonable result after diffing. For example,
rather than (`a1` and `a2` have same trackById)
```
a1 b c a2 => b a2 c a1
```
we get
```
a1 b c a2 => b a1 c a2
```
where a1 and a2 retain their original order despite both
having the same track by value.
Fixes#23815
PR Close#23941
This is a follow up fix for
894286dd0c.
It turns out that comments can be closed in several ways:
- `<!-->`
- `<!-- -->`
- `<!-- --!>`
All of the above are valid ways to close comment per:
https://html.spec.whatwg.org/multipage/syntax.html#comments
The new fix surrounds `<` and `>` with zero width space so that it
renders in the same way, but it prevents the comment to be closed eagerly.
PR Close#40525
This is a follow up fix for
894286dd0c.
It turns out that comments can be closed in several ways:
- `<!-->`
- `<!-- -->`
- `<!-- --!>`
All of the above are valid ways to close comment per:
https://html.spec.whatwg.org/multipage/syntax.html#comments
The new fix surrounds `<` and `>` with zero width space so that it
renders in the same way, but it prevents the comment to be closed eagerly.
PR Close#40525
This commits adds additional expectations to verify that the bloom
filter is able to correctly handle token IDs that exceed the size of
the bloom filter (which is currently 256 bits).
PR Close#40489
Because the query now has `flags` which specify the mode, the static query
instruction can now be remove. It is simply normal query with `static` flag.
PR Close#40091
Previous implementation would fire changes `QueryList.changes.subscribe`
whenever the `QueryList` was recomputed. This resulted in artificially
high number of change notifications, as it is possible that recomputing
`QueryList` results in the same list. When the `QueryList` gets recomputed
is an implementation detail and it should not be the thing which determines
how often change event should fire.
This change introduces a new `emitDistinctChangesOnly` option for
`ContentChildren` and `ViewChildren`.
```
export class QueryCompWithStrictChangeEmitParent {
@ContentChildren('foo', {
// This option will become the default in the future
emitDistinctChangesOnly: true,
})
foos!: QueryList<any>;
}
```
PR Close#40091
This commit updates the logic that calculates `useFactory` function arguments to avoid relying on `instanceof`
checks (thus always retaining symbols) and relies on flags that DI decorators contain (as a monkey-patched property).
Another perf benefit is having less megamorphic reads while calculating args for the `useFactory` call: we used to
check whether a token has `ngMetadataName` property 4 times (in worst case), now we have just 1 megamorphic read in
all cases.
Closes#40143.
PR Close#40145
The `template` and `isInline` fields were previously stored in a nested
object, which was initially done to accommodate for additional template
information to support accurate source maps for external templates. In
the meantime the source mapping has been accomplished in a different
way, and I feel this flattened structure is simpler and smaller so is
preferable over the nested object. This change also makes the `isInline`
property optional with a default value of `false`.
PR Close#40383
When we attach a `ViewRef` to a `ViewContainerRef`, we save a reference to the container
onto the `ViewRef` so that we can remove it when the ref is destroyed. The problem is
that if the container's `hostView` is destroyed first, the `ViewRef` has no way of knowing
that it should stop referencing the container.
These changes remove the leak by not saving a reference at all. Instead, when a `ViewRef`
is destroyed, we clean it up through the `LContainer` directly. We don't need to worry
about the case where the container is destroyed before the view, because containers
automatically clean up all of their views upon destruction.
Fixes#38648.
PR Close#40219
The "monitoring" workflow has been failing since #40127 was merged,
due to a Saucelabs test failure in Internet Explorer 11. The issue is
with the test's expectation which does not account for Ivy instruction
invocations to use "anonymous" instead of the instruction's function
name. This commit changes the test expectation to also accept
"anonymous", which was already the case for similar expectations.
PR Close#40342
Currently when analyzing the metadata of a directive, we bundle together the bindings from `host`
and the `HostBinding` and `HostListener` together. This can become a problem later on in the
compilation pipeline, because we try to evaluate the value of the binding, causing something like
`@HostBinding('class.foo') public true = 1;` to be treated the same as
`host: {'[class.foo]': 'true'}`.
While looking into the issue, I noticed another one that is closely related: we weren't treating
quoted property names correctly. E.g. `@HostBinding('class.foo') public "foo-bar" = 1;` was being
interpreted as `classProp('foo', ctx.foo - ctx.bar)` due to the same issue where property names
were being evaluated.
These changes resolve both of the issues by treating all `HostBinding` instance as if they're
reading the property from `this`. E.g. the `@HostBinding('class.foo') public true = 1;` from above
is now being treated as `host: {'[class.foo]': 'this.true'}` which further down the pipeline becomes
`classProp('foo', ctx.true)`. This doesn't have any payload size implications for existing code,
because we've always been prefixing implicit property reads with `ctx.`. If the property doesn't
have an identifier that can be read using dotted access, we convert it to a quoted one (e.g.
`classProp('foo', ctx['is-foo']))`.
Fixes#40220.
Fixes#40230.
Fixes#18698.
PR Close#40233
In some browsers, notably a mobile version of webkit on iPad, the
result of calling `DOMParser.parseFromString()` returns a document
whose `body` property is null until the next tick of the browser.
Since this is of no use to us for sanitization, we now fall back to the
"inert document" strategy for this case.
Fixes#39834
PR Close#40107
The `ɵɵngDeclareComponent` calls are designed to be translated to fully
AOT compiled code during a build transform, but in cases this is not
done it is still possible to compile the declaration object in the
browser using the JIT compiler. This commit adds a runtime
implementation of `ɵɵngDeclareComponent` which invokes the JIT compiler
using the declaration object, such that a compiled component definition
is made available to the Ivy runtime.
PR Close#40127
The `render3` test targets are currently also executed for ViewEngine
builds, even though the `render3` infrastructure only concerns Ivy
infrastructure. This commit tags the test targets as ivy-only to disable
those tests for View Engine.
PR Close#40127
Currently we check whether a property binding contains an interpolation using a regex so
that we can throw an error. The problem is that the regex doesn't account for quotes
which means that something like `[prop]="'{{ foo }}'"` will be considered an error, even
though it's not actually an interpolation.
These changes build on top of the logic from #39826 to account for interpolation
characters inside quotes.
Fixes#39601.
PR Close#40267
The trustConstantHtml and trustConstantResourceUrl functions are only
meant to be passed constant strings extracted from Angular application
templates, as passing other strings or variables could introduce XSS
vulnerabilities.
To better protect these APIs, turn them into template tags. This makes
it possible to assert that the associated template literals do not
contain any interpolation, and thus must be constant.
Also add tests for the change to prevent regression.
PR Close#40082
There are two parts to this commit:
1. Revert the changes from #38379. This change had an incomplete view of
how things worked and also diverged the implementations of
`applyRedirects` and `recognize` even more.
2. Apply the fixes from the `recognize` algorithm to ensure that named
outlets with empty path parents can be matched. This change also passes
all the tests that were added in #38379 with the added benefit of being
a more complete fix that stays in-line with the `recognize` algorithm.
This was made possible by using the same approach for `split` by
always creating segments for empty path matches (previously, this was
only done in `applyRedirects` if there was a `redirectTo` value). At the
end of the expansions, we need to squash all empty segments so that
serializing the final `UrlTree` returns the same result as before.
Fixes#39952Fixes#10726Closes#30410
PR Close#40029
Prior to this commit, removing `FormControlDirective` and `FormGroupName` directive instances didn't clear
the callbacks previously registered on FromControl/FormGroup class instances. As a result, these callbacks
were executed even after `FormControlDirective` and `FormGroupName` directive instances were destroyed. That was
also causing memory leaks since these callbacks also retained references to DOM elements.
This commit updates the cleanup logic to take care of properly detaching FormControl/FormGroup/FormArray instances
from the view by removing view-specific callback at destroy time.
Closes#20007, #37431, #39590.
PR Close#39235
DI providers can be defined via `useFactory` function, which may have arguments configured via `deps` array.
The `deps` array may contain DI flags represented by DI decorators (such as `@Self`, `@SkipSelf`, etc). Prior to this
commit, having the `@Host` decorator in `deps` array resulted in runtime error in Ivy. The problem was that the `@Host`
decorator was not taken into account while `useFactory` argument list was constructed, the `@Host` decorator was
treated as a token that should be looked up.
This commit updates the logic which prepares `useFactory` arguments to recognize the `@Host` decorator.
PR Close#40122
The `ɵɵngDeclareDirective` calls are designed to be translated to fully
AOT compiled code during a build transform, but in cases this is not
done it is still possible to compile the declaration object in the
browser using the JIT compiler. This commit adds a runtime
implementation of `ɵɵngDeclareDirective` which invokes the JIT compiler
using the declaration object, such that a compiled directive definition
is made available to the Ivy runtime.
PR Close#40101
Internally we store lifecycle hooks in the format `[index, hook, index, hook]` and when
iterating over them, we check one place ahead to figure out whether we've hit found
a hook or an index. The problem is that the loop is set up to iterate up to `hooks.length`
which means that we may go out of bounds on the last iteration, depending on where
we started. This appears to happen under a specific set of circumstances where a
directive calls `detectChanges` from an input setter while it has `ngOnChanges` and
`ngAfterViewInit` hooks.
These changes resolve the issue by only iterating up to `length - 1` which guarantees that
we can always look one place ahead.
This appears to have regressed some time in version 10.
Fixes#38611.
PR Close#40206
PR #39876 introduced an error where the `onDestroy` of `ComponentRef`
would only get called if `ngDevMode` was set to true. This was because
in dev mode we would freeze `TCleanup` to verify that no more
static cleanup would get added to `TCleanup` array. This ensured
that `TCleanup` was always present in dev mode. In production the
`TCleanup` would get created only when needed. The resulting cleanup
code was incorrectly indented and would only run if `TCleanup` was
present causing this issue.
Fix#40105
PR Close#40120
Escape the content of the strings so that it can be safely inserted into a comment node.
The issue is that HTML does not specify any way to escape comment end text inside the comment.
`<!-- The way you close a comment is with "-->". -->`. Above the `"-->"` is meant to be text
not an end to the comment. This can be created programmatically through DOM APIs.
```
div.innerHTML = div.innerHTML
```
One would expect that the above code would be safe to do, but it turns out that because comment
text is not escaped, the comment may contain text which will prematurely close the comment
opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
may contain such text and expect them to be safe.)
This function escapes the comment text by looking for the closing char sequence `-->` and replace
it with `-_-_>` where the `_` is a zero width space `\u200B`. The result is that if a comment
contains `-->` text it will render normally but it will not cause the HTML parser to close the
comment.
PR Close#40136
Currently when `ɵɵtemplate` and `ɵɵelement` instructions are generated by compiler, all static attributes are
duplicated for both instructions. As a part of this duplication, i18n translation blocks for static i18n attributes
are generated twice as well, causing duplicate entries in extracted translation files (when Ivy extraction mechanisms
are used). This commit fixes this issue by introducing a cache for i18n translation blocks (for static attributes
only).
Also this commit further aligns `ɵɵtemplate` and `ɵɵelement` instruction attributes, which should help implement
more effective attributes deduplication logic.
Closes#39942.
PR Close#40077
Currently the compiler treats something like `{{ '{{a}}' }}` as a nested
binding and throws an error, because it doesn't account for quotes
when it looks for binding characters. These changes add a bit of
logic to skip over text inside quotes when parsing.
Fixes#39601.
PR Close#39826
Differs tries to inject parent differ in order to support extending.
This does not work in the 'root' injector as the provider overrides the
default injector. The fix is to just assume standard set of providers
and extend those instead.
PR close#25015
Issue close#11309 `Can't extend IterableDiffers`
Issue close#18554 `IterableDiffers.extend is not AOT compatible`
(This is fixed because we no longer have an arrow function in the
factory but a proper function which can be imported.)
PR Close#39981
At the moment, when creating a root module, a subscription to the
`onError` subject is also created. It captures the scope where `NgModuleRef`
is created and prevents it from being garbage collected. Also note that this
`NgModuleRef` has a reference to the root module instance (e.g. `AppModule`),
which also prevents it from being GC'd.
PR Close#39940
This commit adds a few tests to verify that the `onDestroy` callbacks are invoked when `ComponentRef` instance
is destroyed and the logic is consistent between ViewEngine and Ivy.
PR Close#39876
In the new behavior Angular removes applications from the testability registry when the
root view gets destroyed. This eliminates a memory leak, because before that the
TestabilityRegistry holds references to HTML elements, thus they cannot be GCed.
PR Close#22106
PR Close#39876