This commit removes code duplication around projectables nodes insertion.
It also simplfy the overall logic by using recursive function calls instead
of hand-unrolled stack (we can always optimise this part if needed).
PR Close#29008
In the past, the sanitizer would remove unsafe elements, but still
traverse and sanitize (and potentially preserve) their content. This was
problematic in the case of `<style></style>` tags, whose content would
be converted to HTML text nodes.
In order to fix this, the sanitizer's behavior was changed in #25879 to
ignore the content of _all_ unsafe elements. While this fixed the
problem with `<style></style>` tags, it unnecessarily removed the
contents for _any_ unsafe element. This was an unneeded breaking change.
This commit partially restores the old sanitizer behavior (namely
traversing content of unsafe elements), but introduces a list of
elements whose content should not be traversed if the elements
themselves are considered unsafe. Currently, this list contains `style`,
`script` and `template`.
Related to #25879 and #26007.
Fixes#28427
PR Close#28804
Currently if an embedded view contains projected nodes, its `rootNodes` array will include `null` instead of the root nodes inside the projection slot. This manifested itself in one of the Material unit tests where we stamp out a template and then move its `rootNodes` into the overlay container.
This PR is related to FW-1087.
PR Close#28951
For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`,
`StylingContext`) in same location in `LView`. This is because we don't want to pre-allocate
space
for it because the storage is sparse. This file contains utilities for dealing with such data
types.
How do we know what is stored at a given location in `LView`.
- `Array.isArray(value) === false` => `RNode` (The normal storage value)
- `Array.isArray(value) === true` => than the `value[0]` represents the wrapped value.
- `typeof value[TYPE] === 'object'` => `LView`
- This happens when we have a component at a given location
- `typeof value[TYPE] === 'number'` => `StylingContext`
- This happens when we have style/class binding at a given location.
- `typeof value[TYPE] === true` => `LContainer`
- This happens when we have `LContainer` binding at a given location.
NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
PR Close#28947
`LView`, `LContainer`, `StylingContext` are all arrays which wrap either
an `HTMLElement`, `LView`, `LContainer`, `StylingContext`. It is often
necessary to retrieve the correct type of element from the location
which means that we often have to wrap the arrays. Logically it makes
more sense if the thing which we are wrapping is at `0` location. Also
it may be more performant since data is more local which may result in
more L2 cache hits in CPU.
PR Close#28947
This change contains conditionally attached classes which provide human readable (debug) level
information for `LView`, `LContainer` and other internal data structures. These data structures
are stored internally as array which makes it very difficult during debugging to reason about the
current state of the system.
Patching the array with extra property does change the array's hidden class' but it does not
change the cost of access, therefore this patching should not have significant if any impact in
`ngDevMode` mode. (see: https://jsperf.com/array-vs-monkey-patch-array)
So instead of seeing:
```
Array(30) [Object, 659, null, …]
```
```
LViewDebug {
views: [...],
flags: {attached: true, ...}
nodes: [
{html: '<div id="123">', ..., nodes: [
{html: '<span>', ..., nodes: null}
]}
]
}
```
PR Close#28945
If an interface is not exported publicly from its package, then the doc-gen
does not see it, and so cannot include it in the generated documentation.
This was the case for a number of `...Decorator` interfaces, such as
`PipeDecorator` and `InputDecorator.
This commit adds these interfaces to the public export to fix this problem.
PR Close#28836
Google3 detected circular references here, so splitting up this rather hodge-podge list of functions into slightly better organizational units.
PR Close#28382
- TView no longer stores childIndex
- LView now as CHILD_HEAD and CHILD_TAIL
TView used to store the head of the list, therefor all LViews had to have the same head, which is incorrect.
PR Close#28382
- Removes CONTAINER_INDEX
- LView[PARENT] now contains LContainer when necessary
- Removes now unused arguments to methods after refactor
PR Close#28382
During build time we remap particular property bindings, because their names don't match their attribute equivalents (e.g. the property for the `for` attribute is called `htmlFor`). This breaks down if the particular element has an input that has the same name, because the property gets mapped to something invalid.
The following changes address the issue by mapping the name during runtime, because that's when directives are resolved and we know all of the inputs that are associated with a particular element.
PR Close#28765
Fixes the `ngOnDestroy` hook on a component or directive being called twice, if the type is also registered as a provider.
This PR resolves FW-1010.
PR Close#28470
Prior to this fix, both the `NgStyle` and `NgClass` directives made use
of `Renderer2` and this dependency raised issues for future versions of
Angular that cannot inject it. This patch ensures that there are two
versions of both directives: one for the VE and another for Ivy.
Jira Issue: FW-882
PR Close#28711
I18n can change the order of the nodes, or insert new dynamic nodes. When that happens it can break the existing links (`TNode.next`) or even create loops:
```
div1 → div2 → div3 → div4 → div5
```
Can become:
```
div1 → div4 → div2 → div3 div5
🡑 │
└─────────────┘
```
This PR fixes this issue by recreating the broken links between those nodes.
PR Close#28827
This commit adds support for the `static: true` flag in `ContentChild`
queries. Prior to this commit, all `ContentChild` queries were resolved
after change detection ran. This is a problem for backwards
compatibility because View Engine also supported "static" queries which
would resolve before change detection.
Now if users add a `static: true` option, the query will be resolved in
creation mode (before change detection runs). For example:
```ts
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef;
```
This feature will come in handy for components that need
to create components dynamically.
PR Close#28811
This commit adds support for the `static: true` flag in
`ViewChild` queries. Prior to this commit, all `ViewChild`
queries were resolved after change detection ran. This is
a problem for backwards compatibility because View Engine
also supported "static" queries which would resolve before
change detection.
Now if users add a `static: true` option, the query will be
resolved in creation mode (before change detection runs).
For example:
```ts
@ViewChild(TemplateRef, {static: true}) template !: TemplateRef;
```
This feature will come in handy for components that need
to create components dynamically.
PR Close#28811
Prior to this commit, the timing of `ViewChild`/`ContentChild` query
resolution depended on the results of each query. If any results
for a particular query were nested inside embedded views (e.g.
*ngIfs), that query would be resolved after change detection ran.
Otherwise, the query would be resolved as soon as nodes were created.
This inconsistency in resolution timing had the potential to cause
confusion because query results would sometimes be available in
ngOnInit, but sometimes wouldn't be available until ngAfterContentInit
or ngAfterViewInit. Code depending on a query result could suddenly
stop working as soon as an *ngIf or an *ngFor was added to the template.
With this commit, users can dictate when they want a particular
`ViewChild` or `ContentChild` query to be resolved with the `static`
flag. For example, one can mark a particular query as `static: false`
to ensure change detection always runs before its results are set:
```ts
@ContentChild('foo', {static: false}) foo !: ElementRef;
```
This means that even if there isn't a query result wrapped in an
*ngIf or an *ngFor now, adding one to the template later won't change
the timing of the query resolution and potentially break your component.
Similarly, if you know that your query needs to be resolved earlier
(e.g. you need results in an ngOnInit hook), you can mark it as
`static: true`.
```ts
@ViewChild(TemplateRef, {static: true}) foo !: TemplateRef;
```
Note: this means that your component will not support *ngIf results.
If you do not supply a `static` option when creating your `ViewChild` or
`ContentChild` query, the default query resolution timing will kick in.
Note: This new option only applies to `ViewChild` and `ContentChild`
queries, not `ViewChildren` or `ContentChildren` queries, as those types
already resolve after CD runs.
PR Close#28810
Accounts for schemas in when validating properties in Ivy.
This PR resolves FW-819.
A couple of notes:
* I had to rework the test slightly, in order to have it fail when we expect it to. The one in master is passing since Ivy's validation runs during the update phase, rather than creation.
* I had to deviate from the design in FW-819 and not add an `enableSchema` instruction, because the schema is part of the `NgModule` scope, however the scope is only assigned to a component once all of the module's declarations have been resolved and some of them can be async. Instead, I opted to have the `schemas` on the component definition.
PR Close#28637
Previously `DebugNode.classes` and `DebugNode.styles` mistakenly used an
object that is only *sometimes* a `StylingContext`. Also fixes a mistake
in `debug_node_spec.ts` where a test component was not declared in the
testing module.
There is still a bug here where `DebugNode` is not exposing *static*
values. This will need to be fixed in a follow up.
PR Close#28709
Prior to this fix if a root component was instantiated it create host
bindings, but never render them once update mode ran unless one or more
slot-allocated bindings were issued. Since styling in Ivy does not make
use of LView slots, the host bindings function never ran on the root
component.
This fix ensures that the `hostBindings` function does run for a root
component and also renders the schedlued styling instructions when
executed.
Jira Issue: FW-1062
PR Close#28664
In the past, @Injectable had no side effects and existing Angular code is
therefore littered with @Injectable usage on classes which are not intended
to be injected.
A common example is:
@Injectable()
class Foo {
constructor(private notInjectable: string) {}
}
and somewhere else:
providers: [{provide: Foo, useFactory: ...})
Here, there is no need for Foo to be injectable - indeed, it's impossible
for the DI system to create an instance of it, as it has a non-injectable
constructor. The provider configures a factory for the DI system to be
able to create instances of Foo.
Adding @Injectable in Ivy signifies that the class's own constructor, and
not a provider, determines how the class will be created.
This commit adds logic to compile classes which are marked with @Injectable
but are otherwise not injectable, and create an ngInjectableDef field with
a factory function that throws an error. This way, existing code in the wild
continues to compile, but if someone attempts to use the injectable it will
fail with a useful error message.
In the case where strictInjectionParameters is set to true, a compile-time
error is thrown instead of the runtime error, as ngtsc has enough
information to determine when injection couldn't possibly be valid.
PR Close#28523
While marking a given views tree as dirty we should go all the way to the
root of the views tree and cross boundaries of dynamically inserted views.
In other words the markForCheck functionality should consider parents of
dynamically inserted views.
PR Close#28687
Prior to this change we used current injector implementation for module injector, which was causing problems and produces circular dependencies in case the same token is referenced (with @SkipSelf flag) in the `deps` array. The origin of the problem was that once `directiveInject` implementation becomes active, it was used for module injector as well, thus searching deps in Component/Directive DI scope. This fix sets `injectInjectorOnly` implementation for module injector to resolve the problem.
PR Close#28667
Prior to this update we had separate contentQueries and contentQueriesRefresh functions to handle creation and update phases. This approach was inconsistent with View Queries, Host Bindings and Template functions that we generate for Component/Directive defs. Now the mentioned 2 functions are combines into one (contentQueries), creation and update logic is separated with RenderFlags (similar to what we have in other generated functions).
PR Close#28503
For TypeScript compilation units that have the "strictFunctionTypes"
option enabled, an error would be produced for Ivy's definition fields
in declaration files in the case of inheritance across directives or
pipes.
This change loosens the definition types to allow for subtypes of the
defined type where necessary.
A test package that has the "strict" option enabled verifies that we
won't regress in environments where strict type checking is enabled.
Fixes#28079
PR Close#28634
Previously, using a pipe in an input binding on an ng-template would
evaluate the pipe in the context of node that was processed before the
template. This caused the retrieval of e.g. ChangeDetectorRef to be
incorrect, resulting in one of the following bugs depending on the
template's structure:
1. If the template was at the root of a view, the previously processed
node would be the component's host node outside of the current view.
Accessing that node in the context of the current view results in a crash.
2. For templates not at the root, the ChangeDetectorRef injected into the
pipe would correspond with the previously processed node. If that node
hosts a component, the ChangeDetectorRef would not correspond with the
view that the ng-template is part of.
The solution to the above problem is two-fold:
1. Template compilation is adjusted such that the template instruction
is emitted before any instructions produced by input bindings, such as
pipes. This ensures that pipes are evaluated in the context of the
template's container node.
2. A ChangeDetectorRef can be requested for container nodes.
Fixes#28587
PR Close#27565
When we resolve a component `templateUrl` we copy the contents of the
resolved template file into the `template` property.
Previously we would then remove the `templateUrl` to indicate that
the component has been resolved. But this meant that we no longer had
access to the URL of the original template file. This is essential for
diagnostics messages about the template compilation.
Now the existence of the `template` property overrides the existence of
`templateUrl`, which allows us to keep the `templateUrl` property.
PR Close#28055
Up until now, `[style]` and `[class]` bindings (the map-based ones) have only
worked as template bindings and have not been supported at all inside of host
bindings. This patch ensures that multiple host binding sources (components and
directives) all properly assign style values and merge them correctly in terms
of priority.
Jira: FW-882
PR Close#28246
Before this fix our ViewRef implementation assumed that checkNoChanges can be
only called on component views. In reality checkNoChanges can be also called on
embedded views (ex.: when an embedded view is attached to ApplicationRef).
PR Close#28644
We need to support rendering in contexts like web workers where
nodes are emulated and properties may not be set directly. This
commit gates property validation checks to environments that have
real Node objects.
FW-1043 #resolve
PR Close#28610
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
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
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
Prior to this change we didn't verify types passed to bootstrap as a part of NgModule semantics verification. Now we check whether all types passed to bootstrap are actually Components.
PR Close#28386
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
Adds the information that the templateUrl and styleUrls options supports only relative Urls.
Adds more information about relative paths and absolute URLs in project metadata.
PR Close#27962
When testing whether `value` is an object, use the ideal sequence of
strictly not equal to `null` followed by `typeof value === 'object'`
consistently. Specifically there's no point in using double equal with
`null` since `undefined` is ruled out by the `typeof` check.
Also avoid the unnecessary ToBoolean check on `value.ngOnDestroy` in
`hasOnDestroy()`, since the `typeof value.ngOnDestroy === 'function'`
will only let closures pass and all closures are truish (with the
notable exception of `document.all`, but that shouldn't be relevant
for the `ngOnDestroy` hook).
PR Close#28400
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
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
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
There are cases where we should check an element injector but don't go
into the associated module injector if a token is not found. In both the
view engine and ngIvy this is acheived by passing the
`NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR` as the `notFoundValue`.
Before this fix the view engine and ngIvy were using different objects to
represent `NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR`. This was causing problems
as ngUpgrade is using `NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR` const in its
`NgAdapterInjector` to prevent searching of module injectors.
This commit makes sure that ngIvy is using the same object to represent
`NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR` as the view engine.
PR Close#28313