This commit adds a devMode-only check which will throw if a user
attempts to bind a property that does not match a directive
input or a known HTML property.
Example:
```
<div [unknownProp]="someValue"></div>
```
The above will throw because "unknownProp" is not a known
property of HTMLDivElement.
This check is similar to the check executed in View Engine during
template parsing, but occurs at runtime instead of compile-time.
Note: This change uncovered an existing bug with host binding
inheritance, so some Material tests had to be turned off. They
will be fixed in an upcoming PR.
PR Close#28537
Prior to this change we only checked whether current lView has a next pointer while traversing tNode tree. However in some cases this pointer can be undefined and we need to look up parents chain to find suitable next pointer. This commit adds the logic of searching for the next pointer taking parents chain into account.
PR Close#28533
`LView` `HOST` was set in most cases right after creating `LView`.
This makes the API cleaner by explicitly passing it ont `createLView`.
PR Close#28461
When we first started writing tests for Ivy, we did not yet have a
compatible compiler. For this reason, we set up the Ivy runtime tests
to run with generated code that we wrote by hand (instead of real code
generated by the compiler).
Now that we have a working Ivy compiler and TestBed infrastructure
that is compatible with Ivy, we should start writing integration
tests that leverage them (no more handwritten generated code!). This
will prevent bugs where the compiler code and runtime code become
out of sync (which is easy if they are tested separately). And
eventually, we should migrate all the existing runtime tests in
"core/test/render3" to TestBed and ngtsc.
To kick off this effort, this commit migrates some existing tests
from "core/test/render3/exports_spec.ts" and saves them in a new file
with the same name in the "core/test/acceptance" folder.
PR Close#28534
i18n instructions create text nodes dynamically and save them between bindings and the expando block in `LView`. e.g., they try to create the following order in `LView`.
```
| -- elements -- | -- bindings -- | -- dynamic i18n text -- | -- expando (dirs, injectors) -- |
```
Each time a new text node is created, it is pushed to the end of the array and the `expandoStartIndex` marker is incremented, so the section begins slightly later. This happens in `allocExpando`.
This is fine if no directives have been created yet. The end of the array will be in the "dynamic text node" section.
| -- elements -- | -- bindings -- | -- dynamic i18n text -- |
However, this approach doesn't work if dynamic text nodes are created after directives are matched (for example when the directive uses host bindings). In that case, there are already directives and injectors saved in the "expando" section. So pushing to the end of `LView` actually pushes after the expando section. What we get is this:
```
| -- elements -- | -- bindings -- | -- dynamic i18n text -- | -- expando -- | -- dynamic i18n text-- |
```
In this case, the `expandoStartIndex` shouldn't be incremented because we are not inserting anything before the expando section (it's now after the expando section). But because it is incremented in the code right now, it's now pointing to an index in the middle of the expando section.
This PR fixes that so that we only increment the `expandoStartIndex` if nothing was pushed into the expando section.
FW-978 #resolve
PR Close#28424
Currently, DOM node removal called `removeChild` on the saved parent
node when destroying a component. However, this will fail if the
component has been manually moved in the DOM. This change makes the
removal always use the node's real `parentNode` and ignore the provided
`parent`.
PR Close#28455
Ivy allows Components to extend Directives (but not the other way around) and as a result we may have Component and Directive annotations present at the same time. The logic that resolves annotations to pick the necessary one didn't take this into account and as a result Components were recognized as Directives (and vice versa) in case of inheritance. This change updates the resolution logic by picking known annotation that is the nearest one (in inheritance tree) and compares it with expected type. That should help avoid mis-classification of Components/Directives during resolution.
PR Close#28439
Improve the stacktrace for `R3Injector` errors by adding the source component (or module) that tried to inject the missing provider, as well as the name of the injector which triggered the error (`R3Injector`).
e.g.:
```
R3InjectorError(SomeModule)[car -> SportsCar]:
NullInjectorError: No provider for SportsCar!
```
FW-807 #resolve
FW-875 #resolve
PR Close#28207
createInjector() is an Ivy-only API that should not have
been exported as part of the public API. This commit removes
the export. It will be re-exported when Ivy is released.
PR Close#28509
Fixes Ivy not passing thrown errors along to the `ErrorHandler`.
**Note:** the failing test had to be reworked a little bit, because it has some assertions that depend on an error context being logged, however Ivy doesn't keep track of the error context.
This PR resolves FW-840.
PR Close#28447
In Ivy, we support a new manual mode that allows for stricter control
over change detection in OnPush components. Specifically, in this mode,
events do not automatically mark OnPush views as dirty. Only changed
inputs and manual calls to `markDirty()` actually mark a view dirty.
However, this mode cannot be the default for OnPush components if we
want to be backwards compatible with View Engine. This commit re-adds
the legacy logic for OnPush components where events always mark views
dirty and makes it the default behavior.
Note: It is still TODO to add a public API for manual change detection.
PR Close#28474
Due to the fact that the test in 'ng_module_integration_spec.ts' relied on internal VE data structures (the '_def' field) to verify the state and the structure has changed in Ivy, this commit adds an Ivy version of the same test.
PR Close#28477
Previously, attempting to destroy a view with listeners more than once
throws an error during event listener cleanup. This happens because
`cleanup` field on the `TView` has already been cleared out by the time
the second destruction runs.
The `destroyed` flag on LView was previously being set in the `destroyLView` function,
but this flag was never _checked_ anywhere in the codebase. This commit
moves _setting_ this flag to the `cleanupView` function, just before
destroy hooks are called. This is necessary because the destroy hooks
can contain arbitrary user code, such as (surprise!) attempting to
destroy the view (again). We also add a check to `destroyLView` to skip
already-destroyed views. This prevents the cleanup code path from running twice.
PR Close#28413
In View Engine, we supported @Input and @ContentChild annotations
on the same property. This feature was somewhat brittle because
it would only work for static queries, so it would break if a
content child was passed in wrapped in an *ngIf. Due to the
inconsistent behavior and low usage both internally and externally,
we will likely be deprecating it in the next version, and it does
not make sense to perpetuate it in Ivy.
This commit ensures that we now throw in Ivy if we encounter the
two annotations on the same property.
PR Close#28415
This commit updates the token used in fakeAsync test to the one available in both VE and R3 TestBeds. The goal of the test is to verify that fakeAsync works with inject function, so the actual token that is used for a test is irrelevant in that case. The logic to retrieve tokens from compiler injector (that the comment in "fixmeIvy" refers to) was implemented in PR #28196.
PR Close#28383
Prior to this change we may encounter some errors (like pipes being used where they should not be used) while compiling Host Bindings and Listeners. With this update we move validation logic to the analyze phase and throw an error if something is wrong. This also aligns error messages between Ivy and VE.
PR Close#28356
Previously, these components were not added to the view tree for the
(fake) root view in which they were bootstrapped. Without this,
root view destruction does not work as expected since the root view's
children are not present to be also destroyed.
PR Close#28409
This commit updates test that was added after Content Queries inheritance fix (that renames some instructions) was merged into master. The test used previous version of instructions, thus causing failures after merging Content Queries inheritance fix.
PR Close#28414
Prior to this change contentQueriesRefresh functions that represent refresh logic for @ContentQuery list were not composable, which caused problems in case one Directive inherits another one and both of them contain Content Queries. Due to the fact that we used indices to reference queries in refresh function, results were placed into wrong Queries. In order to avoid that we no longer use indices to reference queries and instead maintain current content query index while iterating through them. This allows us to compose contentQueriesRefresh functions and make inheritance feature work with Content Queries.
PR Close#28324
By its nature, Ivy alters the import graph of a TS program, adding imports
where template dependencies exist. For example, if ComponentA uses PipeB
in its template, Ivy will insert an import of PipeB into the file in which
ComponentA is declared.
Any insertion of an import into a program has the potential to introduce a
cycle into the import graph. If for some reason the file in which PipeB is
declared imports the file in which ComponentA is declared (maybe it makes
use of a service or utility function that happens to be in the same file as
ComponentA) then this could create an import cycle. This turns out to
happen quite regularly in larger Angular codebases.
TypeScript and the Ivy runtime have no issues with such cycles. However,
other tools are not so accepting. In particular the Closure Compiler is
very anti-cycle.
To mitigate this problem, it's necessary to detect when the insertion of
an import would create a cycle. ngtsc can then use a different strategy,
known as "remote scoping", instead of directly writing a reference from
one component to another. Under remote scoping, a function
'setComponentScope' is called after the declaration of the component's
module, which does not require the addition of new imports.
FW-647 #resolve
PR Close#28169
TestBed.compileComponents has always been an async API. However,
ViewEngine tolerated using this API in a synchronous manner if the
components declared in the testing module did not have any async
resources (templateUrl, styleUrls). This change makes the ivy TestBed
mirror this tolerance by configuring such components synchronously.
Ref: FW-992
PR Close#28350
This commit unifies handling of the "check no changes" mode between
ngIvy and the view engine. More specifically:
- check no changes can be invoked before change detection in ivy;
- `undefined` values are considered equal `NO_CHANGES` for the "check no changes"
mode purposes.
Chanes in this commit enables several tests that were previously running only in ivy
or only in the view engine.
PR Close#28366
Prior to this change the postprocess step relied on the order of placeholders combined in one group (e.g. [�#1�|�*1:1�]). The order is not guaranteed in case we have nested templates (since we use BFS to process templates) and some tags are represented using same placeholders. This change performs postprocessing more accurate by keeping track of currently active template and searching for matching placeholder.
PR Close#28209
Due to the fast moving nature of the Ivy codebase, the timing isn't
right to make changes to how errors and reported and handled during the
runtime.
Once ivy is stable this test should be revisted because that stage there
will be a better and more robust understanding of how ivy should recover
from runtime errors.
Jira Issue: FW-952
PR Close#28212
DebugElement.properties should contain a map of element
property names to element property values, with entries
for both normal property bindings and host bindings.
This commit adds support for property bindings in
DebugElement.properties (including interpolations).
PR Close#28355
DebugElement.properties should contain a map of element
property names to element property values, with entries
for both normal property bindings and host bindings.
Many Angular core tests depend on this map being present.
This commit adds support for host property bindings in
DebugElement.properties, which fixes the Angular core tests.
There is still work to be done for normal property bindings.
PR Close#28355
Previous to this change, we were storing view queries at the
wrong index. This is because we were passing a raw index to the
store() instruction instead of an adjusted index (i.e. an
index that does not include the HEADER_OFFSET). We had an
additional issue where TView.blueprint was not backfilled
when TView.data was backfilled, so new component instances
created from the blueprint would end up having a shorter LView.
Both of these problems together led to the Material demo app
failing with Ivy. This commit fixes those discrepancies.
PR Close#28327
Previously, bootstrapping a component with render3 would create a chained injector with the test bed ngModule instead of the ngModule that the component belongs to.
Now when a component belongs to an ngModule, we use that for the chained injector, ensuring the correct injection of any providers that this ngModule contains.
FW-776 #resolve
PR Close#28183
Fixes components with native content projection (using `<content>` or `<slot>`) not working under Ivy.
The issue comes from the fact that when creating elements inside a component, we sometimes don't append the element immediately, but we leave it to projection to move it into its final destination. This ends up breaking the native projection, because the slots have to be in place from the beginning. The following changes switch to appending the element immediately when inside a component with Shadow DOM encapsulation.
This PR resolves FW-841.
PR Close#28261
Prior to this change `viewQuery` functions that represent @ViewQuery list were not composable, which caused problems in case one Component/Directive inherits another one and both of them contain View Queries. Due to the fact that we used indices to reference queries, resulting query set was corrupted (child component queries were overridden by super class ones). In order to avoid that we no longer use indices assigned at compile time and instead maintain current view query index while iterating through them. This allows us to compose `viewQuery` functions and make inheritance feature work with View Queries.
PR Close#28309
- Wraps the NgOnChangesFeature in a factory such that no side effects occur in the module root
- Adds comments to ngInherit property on feature definition interface to help guide others not to make the same mistake
- Updates compiler to generate the feature properly after the change to it being a factory
- Updates appropriate tests
PR Close#28187
Before this commit we were creating a "fake" TNode for each and every
projectable node passed during dynamic component creation. This approach
had several problems:
- the existing TView structure had to be mutated to accomodate new TNodes and
it was very easy to "corrupt" TView / TNode data structures;
- TNodes are not really needed to fully support projectable nodes so we were
creating objects and updating existing data structures for nothing.
This commit changes the approach so we don't create "fake" TNodes for projectable
nodes but instead we process projectable nodes directly in the projection instruction.
As a result we've got less code, less object allocation and - as a bonus - we fix few
bugs where TView / TNode data structures were corrupted when using projectable nodes.
PR Close#28275
Fixes Ivy not applying properties that are set in camelCase, because it goes through the `CSSStyleDeclaration` API via `setProperty` and `removeProperty` which requires for [the values to be in dash-case](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty).
**Note:** I opted to let the browser normalize the value, rather than convert it to dash-case during compile time, because there are some special cases like browser-prefixed properties where we might not normalize it in-line with the browser.
This PR fixes FW-579.
PR Close#28276
Previous to this change, the isStylingContext() function was improperly
returning true for LContainers because it used the presence of an array
at index 2 to determine whether it was a styling context. Unfortunately,
LContainers also contain arrays at index 2, so this would return a false
positive. This led to other errors down the line because we would treat
nodes with containers as if they already had styling contexts (even if
they did not), so the proper initialization logic for styling contexts
was not run.
This commit fixes the isStylingContext() function to use LCONTAINER_LENGTH
as a marker rather than the presence of an array, which in turn fixes
host bindings to styles on nodes with containers.
PR Close#28221
Destroys the module's injector when an `NgModule` is destroyed which in turn calls the `ngOnDestroy` methods on the instantiated providers.
This PR resolves FW-739.
PR Close#27793
Due to the fact that animations in Angular are defined in the component metadata,
all animation trigger definitions are localized to the component and are
inaccessible outside of it. Animation host listeners in Ivy are
rendered in the context of the parent component, but the VE renders them
differently. This patch ensures that animation host listeners are
always registered in the sub component's renderer
Jira issue: FW-943
Jira issue: FW-958
PR Close#28210
Prior to this fix Ivy would not execute any animation triggers
that exist as host bindings on an element if it is removed by
the parent template.
PR Close#28162
In Ivy when elements are created a series of static attribute names are provided
over to the construction instruction of that element. Static attribute names
include non-binding attribues (like `<div selected>`) as well as animation bindings
that do not have a RHS value (like `<div @foo>`). Because of this distinction,
value-less animation triggers are rendered first before value-full animation
bindings are and this improper ordering has caused various existing tests to fail.
This patch ensures that animation bindings are evaluated in the order that they
exist within the HTML template code (or host binding code).
PR Close#28165
With the refactoring or how styles/classes are implmented in Ivy,
interpolation has caused the binding code to mess up since interpolation
itself takes up its own slot in Ivy's memory management code. This patch
makes sure that interpolation works as expected with class and style
bindings.
Jira issue: FW-944
PR Close#28190
In VE the renderer.begin() and renderer.end() methods are only called
when CD is called on an element. This patch ensures that Ivy does the
same thing.
Jira issue: FW-945
PR Close#28192
When we look for matching annotations in TestBed, we should always take the last
matching annotation. Otherwise, we will return superclass data for subclasses,
which would have unintended consequences like directives matching the wrong selectors.
PR Close#28195
Initial thinking was that the bug is in the content projection logic but
it turned out to be a wrong assumption - hence adding a test to illustrate
that basic content projection of view containers works correctly.
What fails in the marked test is the logic quering debug nodes - content
peojection is fine but we never create the 'B' text node since we call
show() method on the "wrong" directive instance.
PR Close#28152
Fixes the `ModuleWithComponentFactories.componentFactories` not being populated when calling `compileModuleAndAllComponentsSync` in Ivy.
These changes resolve FW-929.
PR Close#28112
Prior to this change element's i18n attributes like "i18n-title" were processed after "i18n" ones that placed "i18n" and "i18nAttributes" instructions in wrong order, thus "i18nAttributes" failed to target its host element at runtime. This change updates processing order and puts "i18nAttributes" instructions in front of "i18n" ones to resolve the problem.
PR Close#28163
There were two issues with multiple ICU expressions in the same i18n block:
- the regexp that was used to parse the text wasn't able to handle multiple ICU expressions, I've replaced it with parsing the text and searching for brackets (which is what we ended up doing in the end anyway)
- we allocate node indexes for nodes generated by the ICU expressions which increases the expando value, but we would create the nodes for those cases during the update phase. In the mean time we would create some nodes during the creation phase (comment nodes for ICU expressions, text nodes, ...) with an auto increment index. This means that any node created after an ICU expression would get the following index value, but the ICU case nodes expected to use the same index as well... There was a mismatch between the auto generated index, and the expected index which was causing problems when we needed to select those nodes for updates later on. To fix it, I've added the expected node index to the list of mutate codes that we generate, and we do not use an auto increment value anymore.
FW-905 #resolve
PR Close#28083
Throws a similar error to ViewEngine when encountering an `@Output` that hasn't been initialized to an `Observable`.
These changes resolve FW-680.
PR Close#28085
Up until this point, all static attribute values (things like `title` and `id`)
defined within the `host` are of a Component/Directive definition were
generated into a `def.attributes` array and then processed at runtime.
This design decision does not lend itself well to tree-shaking and is
inconsistent with other static values such as styles and classes.
This fix ensures that all static attribute values (attributes, classes,
and styles) that exist within a host definition for components and
directives are all assigned via the `elementHostAttrs` instruction.
```
// before
defineDirective({
...
attributes: ['title', 'my title']
...
})
//now
defineDirective({
...
hostBindings: function() {
if (create) {
elementHostAttrs(..., ['title', 'my-title']);
}
...
}
...
})
```
PR Close#28089
Angular allows for `<ng-content>` elements to include a selector which
filters which content-projected entries are inserted into the container
depending on whether or not the selector is matched.
With Ivy this feature has not fully worked due to the massive changes
that took place inside of Ivy's styling algorithm code (which is
responsible for assigning classes and styles to an element). This
fix ensures that content-projection can correctly identify which slot
an element should be placed into when class-based selectors are used.
PR Close#27849
Fixes the `DebugNode.references` returning a reference to the underlying comment node, rather than the `TemplateRef` that the reference is pointing to. The issue comes from the fact that `discoverLocalRefs` falls back directly to returning the native node, if the ref isn't pointing to a directive, rather than looking through the locals.
These changes resolve FW-870.
PR Close#28101
When requesting a queries instance for a node, it was previously
decided whether it needs to be cloned if the node was not already marked
as hosting a query. This check is in place to have only a single queries
instance per node.
The issue with this approach is that no clone is created for subsequent
instantiations of a component, as the TNode is already marked as hosting
a query during first template pass, whereas the cloning of queries
should be independent of first template pass.
To overcome this issue, the queries are assigned an owner TNode such
that it can reliably be determined if a clone needs to be created.
PR Close#27892
Prior to this change we performed prop and attr name validation at compile time, which failed in case a given prop/attr is an input to a Directive (thus should not be a subject to this check). Since Directive matching in Ivy happens at runtime, the corresponding checks are now moved to runtime as well.
PR Close#28054
This change is a prerequasity for a later change which will turn the
'di' into its own bazel package. In order to do that we have to:
- have `Injector` type be importable by Ivy. This means that we need
to create `Injector` as a pure type in `interface` folder which is
already a bazel package which Ivy can depend on.
- Remove the dependency of `class Injector` on Ivy so that it can be
compiled in isolation. We do that by using `-1` as special value for
`__NG_ELEMENT_ID__` which tells the Ivy `NodeInjector` than
`Injector` is being requested.
PR Close#28066
This update aligns Ivy behavior with ViewEngine related to empty bindings (for example <div [someProp]></div>): empty bindings are ignored.
PR Close#28059
Previously, we had the logic to schedule a change detection tick
inside markViewDirty(). This is fine when used in markDirty(),
the user-facing API, because it should always schedule change
detection. However, this doesn't work when used in markForCheck()
because historically markForCheck() does not trigger change
detection.
To be backwards compatible, this commit moves the scheduling
logic out of markViewDirty() and into markDirty(), so
markForCheck no longer triggers a tick.
PR Close#28048
Libraries that create components dynamically using component factories,
such as `@angular/upgrade` need to pass blocks of projected content
through to the `ComponentFactory.create()` method. These blocks
are extracted from the content by matching CSS selectors defined in
`<ng-content select="..">` tags found in the component's template.
The Angular compiler collects these CSS selectors when compiling a component's
template, and exposes them via the `ComponentFactory.ngContentSelectors`
property.
This change ensures that this property is filled correctly when the
component factory is created by compiling a component with the Ivy engine.
PR Close#27867
When a pipe returns an instance of WrappedValue we should "invalidate" value
of a binding where the pipe in question is used.
Before this change we've always wrtten the invalidation value (NO_CHANGE) to
the binding root this invalidating the first binding in a LView. This commit
corrects the binding index calculation so the binding with a pipe is invalidated.
PR Close#28044
These tests validate the ability of the View Engine TestBed to consume
summary metadata, a mechanism which allows the TestBed to use
AOT-compiled components & directives in tests. It achieves this through
two operations which are independently obsolete in Ivy:
1. It injects CompileMetadataResolver, a View Engine specific compiler
internal class which extracts global analysis metadata from classes,
and uses it to construct summary metadata. This happens in a
beforeEach() block which calls createSummaries().
2. It uses TestBed.initTestEnvironment to pass summary metadata to the
TestBed itself. Any such metadata is ignored in Ivy.
Operation #1 makes it impossible to run these tests under Ivy, as the
CompileMetadataResolver is not available with an Ivy compiler.
Ivy itself does not rely on summary data, and the R3TestBed can depend
directly on AOT compiled components without it. Thus, the spirit of thes
tests is obsolete in an Ivy world.
FW-838 #resolve
PR Close#28027
Previously the canInsertNativeNode and getRenderParent functions had almost
_exaclty_ the same logic. What was worse that getRenderParent was calling
canInsertNativeNode thus executing the same, non-trivial logic twice.
This commit merges canInsertNativeNode and getRenderParent into one function.
Now getRenderParent will return a native parent or null if a node can't be
inserted (content projection, root of a view that is not inserted etc.).
PR Close#28011
The problem that `fixmeIvy`s refer to is resolved, but the tests are still broken due to other issue (not possible to retrieve host property bindings for DebugElement).
PR Close#28003
This commit adds sanitization for `elementProperty` and `elementAttribute` instructions used in `hostBindings` function, similar to what we already have in the `template` function. Main difference is the fact that for some attributes (like "href" and "src") we can't define which SecurityContext they belong to (URL vs RESOURCE_URL) in Compiler, since information in Directive selector may not be enough to calculate it. In order to resolve the problem, Compiler injects slightly different sanitization function which detects proper Security Context at runtime.
PR Close#27939