Previous to this change, we assumed embedded views could only be created after
their parent template node had completed processing. As a result, we only set
up query logic for containers after directives on the node were created.
However, this assumption didn't take into account the case where a directive
on a template node could create views in its constructor.
This commit fixes query logic to work with views created in constructors.
In that case, we need to create a query container before the new view is
rendered so query results in the view aren't lost. But since the query container
is created before directives have completed processing, we also have to ensure
that query results gathered later on the template node are inserted before that
query container. Otherwise, query results in embedded views will clobber query
results on template nodes.
This splice mode may be slightly slower than the normal matching for queries on
containers, but we should only fall back to this strategy in the edge case where
views are created in constructors. (We should encourage developers to create
views in ngOnInit instead).
PR Close#29983
Prior to this change, components created via TestBed.createComponent in the same test were placed into the same root context, which caused problems in conjunction with fixture.autoDetectChanges usage in the same test. Specifically, change detection was triggered immediately for created component (starting from the 2nd one) even if it was not required/desired. This commit makes Ivy and VE behavior consistent: now every component created via TestBed.createComponent is isolated from each other. Current solution uses host element id naming convention, which is not ideal, but helps avoid public API surface changes at this point (we might revisit this approach later).
Note: this commit also adds extra tests to verify bootstrap and change detection behavior in case of multiple components in `bootstrap` array in @NgModule, to make sure this behavior is aligned between Ivy and VE.
PR Close#29981
Currently the `template-strategy` for the static query migration uses the
Angular compiler in order to determine the query timing. This is problematic
as the AngularCompilerProgram also collects metadata for referenced
component stylesheets which aren't necessarily present. e.g. in a CLI
project the component can reference a Sass file. It's not guaranteed
that the standalone Angular compiler plugin supports Sass without
custom logic that is brought in by the Angular CLI webpack plugin.
In order to avoid any failures for invalid stylesheets, we just disable
normalizing of all referenced stylesheets.
PR Close#29876
Currently there are two available migration strategies for the `static-query`
schematic. Both have benefits and negatives which depend on what the
developer prefers. Since we can't decide which migration strategy is the
best for a given project, the developer should be able to select a specific
strategy through a simple choice prompt.
In order to be able to use prompts in a migration schematic, we need to
take advantage of the "inquirer" package which is also used by the CLI
schematic prompts (schematic prompts are usually only statically defined
in the schema). Additionally the schematic needs to be made "async"
because with prompts the schematic can no longer execute synchronously
without implementing some logic that blocks the execution.
PR Close#29876
With dts bundles, `core.d.ts` will include an `EventListener` class as it's used in 303eae918d/packages/core/src/debug/debug_node.ts (L32)
This will conflict with the DOM EventListener, as anything in `core.d.ts` which is using the DOM EventListener will fallback in using the one defined in the same module and hence build will fail because their implementation is different.
With this change, we rename the local `EventListener` to `DebugEventListener`, the later one is non exported.
Fixes#29806
PR Close#29809
When compiling Angular classes, the compiler may decide to append statements with specific metadata that's only required for JIT. This includes things like decorator metadata as well as NgModule scope data.
When the compiler generates such calls, the call sites are marked with Uglify's PURE annotation, so the optimizer will remove them in production builds. However, Closure does not have the PURE (or similar) annotation. We have a utility function `noSideEffects` in the runtime for this purpose. This commit wraps `setClassMetadata` and `setNgModuleScope` function bodies in `noSideEffect` closures to allow Closure remove them.
PR Close#29947
The `TNode.cleanup` data structure can contain sequences of 4-element
sequence of entries (event handlers, directive outputs) mixed with
2-element sequence of entries (QueryList cleanup). Before this fix
we would always skip 4 elements in the `TNode.cleanup` while looking
up event handler cleanups. 4-element skips are not correct in case
of query cleanup presence and this commit corrects the algorithm to
jump 4 or 2 elements depending on a type of cleanup encountered.
PR Close#29957
If a component has its definition set by defineComponent (as opposed to
JIT getter), then it will generate a factory that uses directiveInject()
to retrieve its dependencies. This can be problematic in test code because
tests could use the injection utility before bootstrapping the component,
and directiveInject() relies on the view having been created.
This commit tweaks directiveInject() to fall back to inject() if the view
has not been created. This will allow injection to work in tests even if
it is called before the component is bootstrapped.
PR Close#29948
Previous to this commit, providing a component or directive in a test
module without @Injectable() would throw because the injectable factory
would not be found. Providing components in tests in addition to declaring
or importing them is not necessary, but it should not throw an error.
This commit ensures factory data is saved when component defs and directive
defs are created, which allows them to be processed by the module injector.
Note that bootstrapping is still required for this setup to work because
directiveInject() does not support cases where the view has not been
created. This case will be handled in a future commit.
PR Close#29945
We had a bug where event.preventDefault() was not always called if listeners
were coalesced. This is because we were overwriting the previous listener's
result every time we called the next listener, so listeners early in the chain
that returned false would be ignored and preventDefault would not be called.
This commit fixes that issue, so now preventDefault() is called if any listener
in a coalesced chain returns false. This brings us in line with View Engine
behavior.
PR Close#29934
Overriding multi provider values (providers with `multi: true` flag) via TestBed require additional handling: all existing multi-provider values for the same token should be removed from the override list, so that they are not included into the final value of a given provider. This commit adds this logic to make sure we handle multi providers correctly.
PR Close#29919
Because the styling context may be stored in a different location
and be apart of a sub array, reading the styling context each time
a host binding is evaluated is costly. This patch ensures that the
active styling context is cached so that multiple interactions with
styling bindings can easily retrieve the styling context efficiently.
PR Close#29818
Currently if there are multiple source files within a given
TypeScript source file, only the last template in the source
file is checked as we store templates in a `Map` with the
source file paths as keys.
This is problematic as multiple templates can live within the
same source file and we therefore accidentally overwrite
existing entries in the resolved templates map.
PR Close#29841
Introduces a new strategy for the `static-query` schematic that
is enabled by default. In order to provide a migration that works
for the most Angular applications and makes the upgrade as easy
as possible, the template strategy leverages the view engine
Angular compiler logic in order to determine the query timing
that is currently used within applications using view engine.
PR Close#29815
In order to support multiple strategies for detecting the query timing, the
query usage logic has been moved into a query usage strategy.
PR Close#29815
Plural ICU expressions depend on the locale (different languages have different plural forms). Until now the locale was hard coded as `en-US`.
For compatibility reasons, if you use ivy with AOT and bootstrap your app with `bootstrapModule` then the `LOCALE_ID` token will be set automatically for ivy, which is then used to get the correct plural form.
If you use JIT, you need to define the `LOCALE_ID` provider on the module that you bootstrap.
For `TestBed` you can use either `configureTestingModule` or `overrideProvider` to define that provider.
If you don't use the compat mode and start your app with `renderComponent` you need to call `ɵsetLocaleId` manually to define the `LOCALE_ID` before bootstrap. We expect this to change once we start adding the new i18n APIs, so don't rely on this function (there's a reason why it's a private export).
PR Close#29249
Prior to this change, element attributes annotated with i18n- prefix were removed from element attribute list and processed separately by i18n-specific logic. This behavior is causing issues with directive matching, since attributes are not present in the list of attrs for matching purposes. This commit updates i18n logic to retain attributes in the main attribute list, thus allowing directive matching logic to work correctly.
PR Close#29856
In order to be backwards compatible with View Engine, Ivy should log
errors by default in the TestBed error handler rather than re-throwing
them. Re-throwing the errors is a breaking change that causes issues with
libraries like ngrx that have async behavior and custom error handling.
This logging approach has issues (for both VE and Ivy) because it can allow
tests to pass inappropriately if errors are thrown inside listeners. However,
since re-throwing would be breaking and requires a larger redesign, we should
wait until post-Ivy.
PR Close#29853
Previously, this check was done with bracket property access on the
global object: global['goog']
This will not be minified when Closure compiles this code, which:
1) breaks, because 'goog' will have been minified but the check won't have
taken that into consideration
2) causes build failures in g3, because the actual property 'goog' is
forbidden in some published JS code (to ensure obfuscation).
A TODO is added to validate that this logic is correct, as it's difficult to
test within the Angular repo.
PR Close#29873
The `Δ` caused issue with other infrastructure, and we are temporarily
changing it to `ɵɵ`.
This commit also patches ts_api_guardian_test and AIO to understand `ɵɵ`.
PR Close#29850
The code failed presubmit in google3 because the original ts config was not as strict
as the one used elsewhere in angular/angular and google3.
PR Close#29843
So far using runtime i18n with ivy meant that you needed to use Closure and `goog.getMsg` (or a polyfill). This PR changes the compiler to output both closure & non-closure code, while the unused option will be tree-shaken by minifiers.
This means that if you use the Angular CLI with ivy and load a translations file, you can use i18n and the application will not throw at runtime.
For now it will not translate your application, but at least you can try ivy without having to remove all of your i18n code and configuration.
PR Close#28689
Queries can technically be also accessed within component templates
e.g.
```html
<my-comp [binding]="myQuery"></my-comp>
```
In that case the query with the property "myQuery" is accessed
statically and needs to be marked with `static: true`. There are
other edge cases that need to be handled as the template property
read doesn't necessarily resolve to the actual query property.
For example:
```html
<foo #myQuery></foo>
<my-comp [binding]="myQuery"></my-comp>
```
In this scenario the binding doesn't refer to the actual query
because the template reference variable takes precedence. The
query doesn't need to be marked with "static: true" this time.
This commit ensures that the `static-query` migration schematic
now handles this cases properly. Also template property reads
that access queries from within a `<ng-template>` are ignored
as these can't access the query before the view has been initialized.
Resolves FW-1216
PR Close#29713
- Removes `@publicApi` annotation from ivy instructions
- Adds new `@codeGenApi` annotation to ivy instructions
- Updates ts_api_guardian to support the new annotation properly
PR Close#29820
Currently in Ivy we pass both the raw and parsed selectors to the projectionDef instruction, because the parsed selectors are used to match most nodes, whereas the raw ones are used to match against nodes with the ngProjectAs attribute. The raw selectors add a fair bit of code that won't be used in most cases, because ngProjectAs is somewhat rare.
These changes rework the compiler not to output the raw selectors in the projectionDef, but to parse the selector in ngProjectAs and to store it on the TAttributes. The logic for matching has also been changed so that it matches the pre-parsed ngProjectAs selector against the list of projection selectors.
PR Close#29578
- Adds the instructions
- Adds tests for all instructions
- Adds TODO to remove all tests when we are able to test this with TestBed after the compiler is updated
PR Close#29576
- moves the property instruction to its own file
- moves shared functions that should not be public to the existing `shared.ts` file.
- adds the export of `property.ts` to `all.ts`
PR Close#29576
While investigating styling performance regressions, it was discovered
that a single `fn(...args)` operation was causing a performance hit
because the generated es5 `__spread` operation uses `[].concat` and
reads from the `arguments` values (which are not very efficient). This
patch changes that around to use `fn.apply` instead.
PR Close#29795
Currently the `static-query` and `template-var-assignment` schematic only runs
for `8.0.0` which does not include any betas or release candidates. We want to
run the schematic in the beta's and RC in order to get early feedback about the
schematics. Enabling it promptly with V8 stable release can result in accidental
breakages that we would like to fix/identify before.
PR Close#29735
Queries can not only be accessed within derived classes, but also in
the super class through abstract methods. e.g.
```
abstract class BaseClass {
abstract getEmbeddedForm(): NgForm {}
ngOnInit() {
this.getEmbeddedForm().doSomething();
}
}
class Subclass extends BaseClass {
@ViewChild(NgForm) form: NgForm;
getEmbeddedForm() { return this.form; }
}
```
Same applies for abstract properties which are implemented in the base class
through accessors. This case is also now handled by the schematic.
Resolves FW-1213
PR Close#29688
Currently the static-query schematic is not able to properly handle
call expressions that pass function declarations that access a given
query. e.g.
```ts
ngOnInit() {
this._callFunction(() => this.myQuery.doSomething());
}
_callFunction(cb: any) { cb(); }
```
In that case the passed function is executed synchronously in
the "ngOnInit" lifecycle and therefore the query needs to be
detected as "static".
We can fix this by keeping track of the current function context
and using it to resolve identifiers to the passed arguments.
PR Close#29663
Currently we only check getters for property access expressions. This is wrong
because property access expressions do not always cause the "getter" to be
triggered. e.g.
```ts
set a() {...}
get a() {...}
ngOnInit() {
this.a = true;
}
```
In that case the schematic currently incorrectly checks the "getter", while this is a binary
expression and the property access is used as left-side of the binary expression. In that
case we need to check the setter declaration of the property and not the "getter". In order
to fix this, we need to also check `ts.BinaryExpression` nodes and check getters/setters
based on the used operator token. There are three types of binary expressions:
1) Value assignment (using `=`). In that case only the setter is triggered.
2) Compound assignment (e.g. using `+=`). In that case `getter` and `setter` are triggered.
3) Comparison (e.g. using `===`). In that case only the getter is triggered.
PR Close#29663
Fixes the creation mode block not being run on components which have been detached from change detection.
This PR resolves FW-1217.
Fixes#29645.
PR Close#29741
Fixes Ivy not throwing an error if it runs into an invalid property binding on a container node (e.g. `<div *ngFor="let row of rows">` instead of `<div *ngFor="let row if rows">`).
This PR resolves FW-1219.
PR Close#29691