Moves the `ClassPropertyMapping` into the compiler, rather than having to pass around the limited `InputOutputPropertySet` interface that is only implemented by `ClassPropertyMapping`.
Fixes that the template binder didn't resolve references to DOM nodes (e.g. `<div #ref></div>` if the matcher being passed in isn't a `SelectorMatcher`.
PR Close#61100
An earlier commit that introduced tracking of selectorless directives to the template binder made it so `getDirectivesOfNode` returns _all_ of the matched directives while a new method called `getOwnedDirectives` would return only the ones brought in by the specific node.
In hindsight, this is likely to cause bugs in the future, because it's unclear whether to reach for `getDirectivesOfNode` or `getOwnedDirectives`. These changes remove `getOwnedDirectives` and make it so in selectorless `getDirectivesOfNode` accepts the directive AST node itself.
Another goal of this refactor is that the TCB shouldn't have to check the `selectorlessEnabled` config option.
PR Close#61018
Updates the template binder to include information about directives owned by a specific component/directive node and the names of template symbols that don't exist. These will be used when generating the type check block.
PR Close#60977
Updates the template binder to account for the new selectorless AST nodes. This is a prerequisite to supporting template type checking of the new syntax.
PR Close#60952
Updates the target binder to allow either a selector-based or selectorless matcher to be passed in. This will allow us to skip some of the overhead when matching directives to nodes.
PR Close#60952
Consider a template with a context variable `a`:
```
<ng-template let-a>{{this.a}}</ng-template>
```
t push -fAn interpolation inside that template to `this.a` should intuitively read the class variable `a`. However, today, it refers to the context variable `a`, both in the TCB and the generated code.
In this commit, the above interpolation now refers to the class field `a`.
BREAKING CHANGE: `this.foo` property reads no longer refer to template context variables. If you intended to read the template variable, do not use `this.`.
Fixes#55115
PR Close#55183
This commit updates a directive mock instance to include an extra field that a compiler code was expecting, which caused issues while processing elements with local refs and exported directives.
PR Close#57537
This commit adds an internal util method that allows to detect:
* which selectors are matching nodes in a template
* which pipes are present in a template
Both directives and pipes are split into 2 buckets: eagerly used and the ones that might potentially be defer-loaded.
PR Close#57466
Enables the new `@let` syntax by default.
`@let` declarations are defined as:
1. The `@let` keyword.
2. Followed by one or more whitespaces.
3. Followed by a valid JavaScript name and zero or more whitespaces.
4. Followed by the `=` symbol and zero or more whitespaces.
5. Followed by an Angular expression which can be multi-line.
6. Terminated by the `;` symbol.
Example usage:
```
@let user = user$ | async;
@let greeting = user ? 'Hello, ' + user.name : 'Loading';
<h1>{{greeting}}</h1>
```
Fixes#15280.
PR Close#56715
These changes expose the `ngContentSelectors` and `preserveWhitespaces` metadata to the TCB so they can be used in the next commit to implement a new diagnostic.
PR Close#53190
These changes expose the `ngContentSelectors` and `preserveWhitespaces` metadata to the TCB so they can be used in the next commit to implement a new diagnostic.
PR Close#52726
Adds some logic to skip over comments when resolving implicit `@defer` block triggers. This currently isn't a problem since we don't capture comments by default, but it may come up if we start capturing comments.
PR Close#52449
Enables the new `@` block syntax by default by removing the `enabledBlockTypes` flags. There are still some internal flags that allow special use cases to opt out of the block syntax, like during XML parsing and when compiling older libraries (see #51979).
PR Close#51994
Adds support for defining `viewport`, `interaction` and `hover` triggers with no parameters. If the framework encounters such a case, it resolves the trigger to the root element of the `@placeholder` block. Triggers with no parameters have the following restrictions:
1. They have to be placed on an `@defer` block that has an `@placeholder`.
2. The `@placeholder` can only have one root node.
3. The root placeholder node has to be an element.
PR Close#51922
Switches the syntax for blocks from `{#block}{/block}` to `@block {}` based on the feedback from the community.
Read more about the decision-making process in our blog: https://blog.angular.io/meet-angulars-new-control-flow-a02c6eee7843
The existing block types changed in the following ways:
**Conditional blocks:**
```html
<!-- Before -->
{#if cond}
Main content
{:else if otherCond}
Else if content
{:else}
Else content
{/if}
<!-- After -->
@if (cond) {
Main content
} @else if (otherCond) {
Else if content
} @else {
Else content
}
```
**Deferred blocks**
```html
<!-- Before -->
{#defer when isLoaded}
Main content
{:loading} Loading...
{:placeholder} <icon>pending</icon>
{:error} Failed to load
{/defer}
<!-- After -->
@defer (when isLoaded) {
Main content
} @loading {
Loading...
} @placeholder {
<icon>pending</icon>
} @error {
Failed to load
}
```
**Switch blocks:**
```html
<!-- Before -->
{#switch value}
{:case 1}
One
{:case 2}
Two
{:default}
Default
{/switch}
<!-- After -->
@switch (value) {
@case (1) {
One
}
@case (2) {
Two
}
@default {
Default
}
}
```
**For loops**
```html
<!-- Before -->
{#for item of items; track item}
{{item.name}}
{:empty} No items
{/for}
<!-- After -->
@for (item of items; track item) {
{{item.name}}
} @empty {
No items
}
```
PR Close#51891
Adds a utility to the `BoundTarget` that helps with resolving which element a deferred block is pointing to. We need a separate method for this, because deferred blocks have some special logic for where the trigger can be located.
PR Close#51816
When the `TargetBinder` was written, the only embedded-view-based nodes were templates, but now we have `{#if}`, `{#switch}` and `{#defer}` which have similar semantics. These changes rework the binder to account for the new nodes.
PR Close#51816
Fixes that if a directive/pipe is used after a nested `defer` block, we weren't tracking it as lazy anymore. This was due to the fact that we were resetting the `isInDeferBlock` to false every time instead of the previous value.
PR Close#51262
This commit updates the logic of the TemplateBinder and DirectiveBinder classes to recognize defer blocks. The logic is updated to prevent Directive and Pipe matching inside the defer block. Instead, the scope for those blocks would be calculated separately.
PR Close#51162
Fixes that the compiler was matching directives based on `attr` bindings which doesn't correspond to the runtime behavior. This wasn't a problem until now because the matched directives would basically be a noop, but they can cause issues with required inputs.
PR Close#49713
This is the compile-time implementation of the `hostDirectives` feature plus a little bit of runtime code to illustrate how the newly-generated code will plug into the runtime. It works by creating a call to the new `ɵɵHostDirectivesFeature` feature whenever a directive has a `hostDirectives` field. Afterwards `ɵɵHostDirectivesFeature` will patch a new function onto the directive definition that will be invoked during directive matching.
For example, if we take the following definition:
```ts
@Directive({
hostDirectives: [HostA, {directive: HostB, inputs: ['input: alias']}]
})
class MyDir {}
```
Will compile to:
```js
MyDir.ɵdir = ɵɵdefineComponent({
features: [ɵɵHostDirectivesFeature([HostA, {
directive: HostB,
inputs: {
input: "alias"
}
}])]
});
```
The template type checking is implemented during directive matching by adding the host directives applied on the host to the array of matched directives whenever the host is matched in a template.
Relates to #8785.
PR Close#46868
In `language-service`, the `checker.getDirectiveMetadata` doesn't return the animations meta of the `Component`.
but it's useful for animation completion.
PR Close#44630
This commit introduces an `isStructural` flag on directive metadata, which
is `true` if the directive injects `TemplateRef` (and thus is at least
theoretically usable as a structural directive). The flag is not used for
anything currently, but will be utilized by the Language Service to offer
better autocompletion results for structural directives.
PR Close#40032
This commit updates the symbols in the TemplateTypeCheck API and methods
for retrieving them:
* Include `isComponent` and `selector` for directives so callers can determine which
attributes on an element map to the matched directives.
* Add a new `TextAttributeSymbol` and return this when requesting a symbol for a `TextAttribute`.
* When requesting a symbol for `PropertyWrite` and `MethodCall`, use the
`nameSpan` to retrieve symbols.
* Add fix to retrieve generic directives attached to elements/templates.
PR Close#38844
Recent work on compiler internals in #38539 led to an unexpected failure,
where a pipe used exclusively inside of an ICU would no longer be
emitted into the compilation output. This caused runtime errors due to
missing pipes.
The issue occurred because the change in #38539 would determine the set
of used pipes up-front, independent from the template compilation using
the `R3TargetBinder`. However, `R3TargetBinder` did not consider
expressions within ICUs, so any pipe usages within those expressions
would not be detected. This fix unblocks #38539 and also concerns
upcoming linker work, given that prelink compilations would not go
through full template compilation but only `R3TargetBinder`.
PR Close#38810
The `R3TargetBinder` accepts an interface for directive metadata which
declares types for `input` and `output` objects. These types convey the
mapping between the property names for an input or output and the
corresponding property name on the component class. Due to
`R3TargetBinder`'s requirements, this mapping was specified with property
names as keys and field names as values.
However, because of duck typing, this interface was accidentally satisifed
by the opposite mapping, of field names to property names, that was produced
in other parts of the compiler. This form more naturally represents the data
model for inputs.
Rather than accept the field -> property mapping and invert it, this commit
introduces a new abstraction for such mappings which is bidirectional,
eliminating the ambiguous plain object type. This mapping uses new,
unambiguous terminology ("class property name" and "binding property name")
and can be used to satisfy both the needs of the binder as well as those of
the template type-checker (field -> property).
A new test ensures that the input/output metadata produced by the compiler
during analysis is directly compatible with the binder via this unambiguous
new interface.
PR Close#38685
Previously, bound events were incorrectly bound to directives with
inputs matching the bound event attribute. This fixes that so bound
events can only be bound to directives with matching outputs.
Adds tests for all kinds of directive matching on bound attributes.
PR Close#31938
Prior to this change, namespaced elements such as SVG elements would not
participate correctly in directive matching as their namespace was not
ignored, which was the case with the View Engine compiler. This led to
incorrect behavior at runtime and template type checking.
This commit resolved the issue by ignoring the namespace of elements and
attributes like they were in View Engine.
Fixes#32061
PR Close#33555
Previously, Template.templateAttrs was introduced to capture attribute
bindings which originated from microsyntax (e.g. bindings in *ngFor="...").
This means that a Template node can have two different structures, depending
on whether it originated from microsyntax or from a literal <ng-template>.
In the literal case, the node behaves much like an Element node, it has
attributes, inputs, and outputs which determine which directives apply.
In the microsyntax case, though, only the templateAttrs should be used
to determine which directives apply.
Previously, both the t2_binder and the TemplateDefinitionBuilder were using
the wrong set of attributes to match directives - combining the attributes,
inputs, outputs, and templateAttrs of the Template node regardless of its
origin. In the TDB's case this wasn't a problem, since the TDB collects a
global Set of directives used in the template, so it didn't matter whether
the directive was also recognized on the <ng-template>. t2_binder's API
distinguishes between directives on specific nodes, though, so it's more
sensitive to mismatching.
In particular, this showed up as an assertion failure in template type-
checking in certain cases, when a directive was accidentally matched on
a microsyntax template element and also had a binding which referenced a
variable declared in the microsyntax. This resulted in the type-checker
attempting to generate a reference to a variable that didn't exist in that
scope.
The fix is to distinguish between the two cases and select the appropriate
set of attributes to match on accordingly.
Testing strategy: tested in the t2_binder tests.
PR Close#29698
This commit introduces the "t2" API, which processes parsed template ASTs
and performs a number of functions such as binding (the process of
semantically interpreting cross-references within the template) and
directive matching. The API is modeled on TypeScript's TypeChecker API,
with oracle methods that give access to collected metadata.
This work is a prerequisite for the upcoming template type-checking
functionality, and will also become the basis for a refactored
TemplateDefinitionBuilder.
PR Close#26203