The functional API provided by the
@angular/animations
module provides a domain-specific language (DSL) for creating and controlling animations in Angular applications.
See the API reference for a complete listing and syntax details of the core functions and related data structures.
Function name
What it does
trigger()
Kicks off the animation and serves as a container for all other animation function calls. HTML template binds to
triggerName
. Use the first argument to declare a unique trigger name. Uses array syntax.
style()
Defines one or more CSS styles to use in animations. Controls the visual appearance of HTML elements during animations. Uses object syntax.
state()
Creates a named set of CSS styles that should be applied on successful transition to a given state. The state can then be referenced by name within other animation functions.
animate()
Specifies the timing information for a transition. Optional values for
delay
and
easing
. Can contain
style()
calls within.
transition()
Defines the animation sequence between two named states. Uses array syntax.
keyframes()
Allows a sequential change between styles within a specified time interval. Use within
animate()
. Can include multiple
style()
calls within each
keyframe()
. Uses array syntax.
group()
Specifies a group of animation steps (
inner animations
) to be run in parallel. Animation continues only after all inner animation steps have completed. Used within
sequence()
or
transition()
.
query()
Finds one or more inner HTML elements within the current element.
sequence()
Specifies a list of animation steps that are run sequentially, one by one.
stagger()
Staggers the starting time for animations for multiple elements.
animation()
Produces a reusable animation that can be invoked from elsewhere. Used together with
useAnimation()
.
useAnimation()
Activates a reusable animation. Used with
animation()
.
animateChild()
Allows animations on child components to be run within the same timeframe as the parent.
An animation
transition
specifies changes that occur between one state and another. Set the transition to make the change less abrupt. An animation
transition
specifies the changes that occur between one state and another.
Animation
transition()
function defined
link
The
transition()
function accepts two arguments:
An expression that defines the direction between two transition states
An expression that accepts one or a series of
animate()
steps
Use the
animate()
function of a transition to define:
Length
Delay
Easing
Style function for defining styles while transitions are taking place
Use the
animate()
function to define the
keyframes()
function for multi-step animations.
These definitions are placed in the second argument of the
animate()
function.
Animation metadata: duration, delay, and easing
link
The
animate()
function accepts the
timings
and
styles
input parameters.
The
timings
parameter takes either a number or a string defined in three parts.
animate(duration)
or
animate('duration delay easing')
The first part,
duration
, is required.
The duration can be expressed in milliseconds as a number without quotes, or in seconds with quotes and a time specifier.
For example, a duration of a tenth of a second can be expressed as follows:
As a plain number, in milliseconds:
100
In a string, as milliseconds:
'100ms'
In a string, as seconds:
'0.1s'
The second argument,
delay
, has the same syntax as
duration
.
For example:
Wait for 100 ms and then run for 200 ms:
'0.2s 100ms'
The third argument,
easing
, controls how the animation accelerates and decelerates during its runtime.
For example,
ease-in
causes the animation to begin slowly, and to pick up speed as it progresses.
Wait for 100 ms, run for 200 ms.
Use a deceleration curve to start out fast and slowly decelerate to a resting point:
0.2s100ms ease-out
Run for 200 ms, with no delay.
Use a standard curve to start slow, speed up in the middle, and then decelerate slowly at the end:
0.2s ease-in-out
Start immediately, run for 200 ms.
Use an acceleration curve to start slow and end at full velocity:
The code sample is missing.
0.2s ease-in
NOTE
:
See the Material Design website's topic on Natural easing curves for general information on easing curves.
This example provides a state transition from
open
to
closed
with a 1-second transition between states.
src/app/open-close.component.ts
transition('open => closed',[animate('1s')]),
In the preceding code snippet, the
=>
operator indicates unidirectional transitions, and
<=>
is bidirectional.
Within the transition,
animate()
specifies how long the transition takes.
In this case, the state change from
open
to
closed
takes 1 second, expressed here as
1s
.
This example adds a state transition from the
closed
state to the
open
state with a 0.5-second transition animation arc.
src/app/open-close.component.ts
transition('closed => open',[animate('0.5s')]),
NOTE
:
Using styles within
state
and
transition
functions:
Use
state()
to define styles that are applied at the end of each transition, they persist after the animation completes
Use
transition()
to define intermediate styles, which create the illusion of motion during the animation
When animations are turned off,
transition()
styles can be skipped, but
state()
styles can't
Include multiple state pairs within the same
transition()
argument:
When an animation trigger for a component is defined, attach it to an element in the template. Wrap the trigger name in brackets and precede it with an
@
symbol.
Bind the trigger to a template expression using standard Angular property binding syntax. The
triggerName
is the name of the trigger, and
expression
evaluates to a defined animation state.
<div [@triggerName]="expression">…</div>;
The animation is executed or triggered when the expression value changes to a new state.
The following code snippet binds the trigger to the value of the
isOpen
property.
src/app/open-close.component.html
<nav><buttontype="button" (click)="toggle()">Toggle Open/Close</button></nav><div [@openClose]="isOpen ? 'open' : 'closed'"class="open-close-container"><p>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</p></div>
In this example, when the
isOpen
expression evaluates to a defined state of
open
or
closed
, it notifies the trigger
openClose
of a state change.
Then it's up to the
openClose
code to handle the state change and kick off a state change animation.
For elements entering or leaving a page (inserted or removed from the DOM), you can make the animations conditional.
For example, use
*ngIf
with the animation trigger in the HTML template.
NOTE
:
In the component file, set the trigger that defines the animations as the value of the
animations:
property in the
@Component()
decorator.
Attach an animation to an HTML template
link
In the HTML template file, use the trigger name to attach the defined animations to the HTML element to be animated.
Step 2: Importing animation functions into component files
Step 3: Adding the animation metadata property
Animating a transition
Animation state and styles
Transitions and timing
Triggering the animation
Defining animations and attaching them to the HTML template
Code review
Summary
Animations API summary
More on Angular animations
Animation provides the illusion of motion: HTML elements change styling over time.
Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic.
Animations can improve your application and user experience in a number of ways:
Without animations, web page transitions can seem abrupt and jarring
Motion greatly enhances the user experience, so animations give users a chance to detect the application's response to their actions
Good animations intuitively call the user's attention to where it is needed
Typically, animations involve multiple style
transformations
over time.
An HTML element can move, change color, grow or shrink, fade, or slide off the page.
These changes can occur simultaneously or sequentially. You can control the timing of each transformation.
Angular's animation system is built on CSS functionality, which means you can animate any property that the browser considers animatable.
This includes positions, sizes, transforms, colors, borders, and more.
The W3C maintains a list of animatable properties on its CSS Transitions page.
About this guide
link
This guide covers the basic Angular animation features to get you started on adding Angular animations to your project.
The features described in this guide —and the more advanced features described in the related Angular animations guides— are demonstrated in an example application available as a
live example
/ download example
.
Prerequisites
link
The guide assumes that you're familiar with building basic Angular apps, as described in the following sections:
Tutorial
Architecture Overview
Getting started
link
The main Angular modules for animations are
@angular/animations
and
@angular/platform-browser
.
When you create a new project using the CLI, these dependencies are automatically added to your project.
To get started with adding Angular animations to your project, import the animation-specific modules along with standard Angular functionality.
Step 1: Enabling the animations module
link
Import
BrowserAnimationsModule
, which introduces the animation capabilities into your Angular root application module.
NOTE
:
See a summary of available animation functions at the end of this guide.
Step 3: Adding the animation metadata property
link
In the component file, add a metadata property called
animations:
within the
@Component()
decorator.
You put the trigger that defines an animation within the
animations
metadata property.
src/app/app.component.ts
@Component({
selector:'app-root',
templateUrl:'app.component.html',
styleUrls:['app.component.css'],
animations:[// animation triggers go here]})
Animating a transition
link
Let's animate a transition that changes a single HTML element from one state to another.
For example, you can specify that a button displays either
Open
or
Closed
based on the user's last action.
When the button is in the
open
state, it's visible and yellow.
When it's the
closed
state, it's translucent and blue.
In HTML, these attributes are set using ordinary CSS styles such as color and opacity.
In Angular, use the
style()
function to specify a set of CSS styles for use with animations.
Collect a set of styles in an animation state, and give the state a name, such as
open
or
closed
.
Let's create a new
open-close
component to animate with simple transitions.
Run the following command in terminal to generate the component:
ng g component open-close
This will create the component at
src/app/open-close.component.ts
.
Animation state and styles
link
Use Angular's
state()
function to define different states to call at the end of each transition.
This function takes two arguments:
A unique name like
open
or
closed
and a
style()
function.
Use the
style()
function to define a set of styles to associate with a given state name.
You must use
camelCase
for style attributes that contain dashes, such as
backgroundColor
or wrap them in quotes, such as
'background-color'
.
Let's see how Angular's
state()
function works with the
style()
function to set CSS style attributes.
In this code snippet, multiple style attributes are set at the same time for the state.
In the
open
state, the button has a height of 200 pixels, an opacity of 1, and a yellow background color.
In Angular, you can set multiple styles without any animation.
However, without further refinement, the button instantly transforms with no fade, no shrinkage, or other visible indicator that a change is occurring.
To make the change less abrupt, you need to define an animation
transition
to specify the changes that occur between one state and another over a period of time.
The
transition()
function accepts two arguments:
The first argument accepts an expression that defines the direction between two transition states, and the second argument accepts one or a series of
animate()
steps.
Use the
animate()
function to define the length, delay, and easing of a transition, and to designate the style function for defining styles while transitions are taking place.
Use the
animate()
function to define the
keyframes()
function for multi-step animations.
These definitions are placed in the second argument of the
animate()
function.
Animation metadata: duration, delay, and easing
link
The
animate()
function (second argument of the transition function) accepts the
timings
and
styles
input parameters.
The
timings
parameter takes either a number or a string defined in three parts.
animate(duration)
or
animate('duration delay easing')
The first part,
duration
, is required.
The duration can be expressed in milliseconds as a number without quotes, or in seconds with quotes and a time specifier.
For example, a duration of a tenth of a second can be expressed as follows:
As a plain number, in milliseconds:
100
In a string, as milliseconds:
'100ms'
In a string, as seconds:
'0.1s'
The second argument,
delay
, has the same syntax as
duration
.
For example:
Wait for 100ms and then run for 200ms:
'0.2s 100ms'
The third argument,
easing
, controls how the animation accelerates and decelerates during its runtime.
For example,
ease-in
causes the animation to begin slowly, and to pick up speed as it progresses.
Wait for 100ms, run for 200ms.
Use a deceleration curve to start out fast and slowly decelerate to a resting point:
'0.2s 100ms ease-out'
Run for 200ms, with no delay.
Use a standard curve to start slow, accelerate in the middle, and then decelerate slowly at the end:
'0.2s ease-in-out'
Start immediately, run for 200ms.
Use an acceleration curve to start slow and end at full velocity:
'0.2s ease-in'
NOTE
:
See the Material Design website's topic on Natural easing curves for general information on easing curves.
This example provides a state transition from
open
to
closed
with a 1-second transition between states.
src/app/open-close.component.ts
transition('open => closed',[animate('1s')]),
In the preceding code snippet, the
=>
operator indicates unidirectional transitions, and
<=>
is bidirectional.
Within the transition,
animate()
specifies how long the transition takes.
In this case, the state change from
open
to
closed
takes 1 second, expressed here as
1s
.
This example adds a state transition from the
closed
state to the
open
state with a 0.5-second transition animation arc.
src/app/open-close.component.ts
transition('closed => open',[animate('0.5s')]),
NOTE
:
Some additional notes on using styles within
state
and
transition
functions.
Use
state()
to define styles that are applied at the end of each transition, they persist after the animation completes
Use
transition()
to define intermediate styles, which create the illusion of motion during the animation
When animations are disabled,
transition()
styles can be skipped, but
state()
styles can't
Include multiple state pairs within the same
transition()
argument:
transition('on => off, off => void')
Triggering the animation
link
An animation requires a
trigger
, so that it knows when to start.
The
trigger()
function collects the states and transitions, and gives the animation a name, so that you can attach it to the triggering element in the HTML template.
The
trigger()
function describes the property name to watch for changes.
When a change occurs, the trigger initiates the actions included in its definition.
These actions can be transitions or other functions, as we'll see later on.
In this example, we'll name the trigger
openClose
, and attach it to the
button
element.
The trigger describes the open and closed states, and the timings for the two transitions.
NOTE
:
Within each
trigger()
function call, an element can only be in one state at any given time.
However, it's possible for multiple triggers to be active at once.
Defining animations and attaching them to the HTML template
link
Animations are defined in the metadata of the component that controls the HTML element to be animated.
Put the code that defines your animations under the
animations:
property within the
@Component()
decorator.
When you've defined an animation trigger for a component, attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an
@
symbol.
Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, where
triggerName
is the name of the trigger, and
expression
evaluates to a defined animation state.
<div [@triggerName]="expression">…</div>;
The animation is executed or triggered when the expression value changes to a new state.
The following code snippet binds the trigger to the value of the
isOpen
property.
src/app/open-close.component.html
<nav><buttontype="button" (click)="toggle()">Toggle Open/Close</button></nav><div [@openClose]="isOpen ? 'open' : 'closed'"class="open-close-container"><p>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</p></div>
In this example, when the
isOpen
expression evaluates to a defined state of
open
or
closed
, it notifies the trigger
openClose
of a state change.
Then it's up to the
openClose
code to handle the state change and kick off a state change animation.
For elements entering or leaving a page (inserted or removed from the DOM), you can make the animations conditional.
For example, use
*ngIf
with the animation trigger in the HTML template.
NOTE
:
In the component file, set the trigger that defines the animations as the value of the
animations:
property in the
@Component()
decorator.
In the HTML template file, use the trigger name to attach the defined animations to the HTML element to be animated.
Code review
link
Here are the code files discussed in the transition example.
You learned to add animation to a transition between two states, using
style()
and
state()
along with
animate()
for the timing.
Learn about more advanced features in Angular animations under the Animation section, beginning with advanced techniques in transition and triggers.
Animations API summary
link
The functional API provided by the
@angular/animations
module provides a domain-specific language (DSL) for creating and controlling animations in Angular applications.
See the API reference for a complete listing and syntax details of the core functions and related data structures.
Function name
What it does
trigger()
Kicks off the animation and serves as a container for all other animation function calls. HTML template binds to
triggerName
. Use the first argument to declare a unique trigger name. Uses array syntax.
style()
Defines one or more CSS styles to use in animations. Controls the visual appearance of HTML elements during animations. Uses object syntax.
state()
Creates a named set of CSS styles that should be applied on successful transition to a given state. The state can then be referenced by name within other animation functions.
animate()
Specifies the timing information for a transition. Optional values for
delay
and
easing
. Can contain
style()
calls within.
transition()
Defines the animation sequence between two named states. Uses array syntax.
keyframes()
Allows a sequential change between styles within a specified time interval. Use within
animate()
. Can include multiple
style()
calls within each
keyframe()
. Uses array syntax.
group()
Specifies a group of animation steps (
inner animations
) to be run in parallel. Animation continues only after all inner animation steps have completed. Used within
sequence()
or
transition()
.
query()
Finds one or more inner HTML elements within the current element.
sequence()
Specifies a list of animation steps that are run sequentially, one by one.
stagger()
Staggers the starting time for animations for multiple elements.
animation()
Produces a reusable animation that can be invoked from elsewhere. Used together with
useAnimation()
.
useAnimation()
Activates a reusable animation. Used with
animation()
.
animateChild()
Allows animations on child components to be run within the same timeframe as the parent.
More on Angular animations
link
You might also be interested in the following:
Transition and triggers
Complex animation sequences
Reusable animations
Route transition animations
Check out this presentation, shown at the AngularConnect conference in November 2017, and the accompanying source code.
An Angular application consists mainly of components and their HTML templates.
Because the components and templates provided by Angular cannot be understood by the browser directly, Angular applications require a compilation process before they can run in a browser.
The Angular ahead-of-time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase
before
the browser downloads and runs that code.
Compiling your application during the build process provides a faster rendering in the browser.
This guide explains how to specify metadata and apply available compiler options to compile your applications efficiently using the AOT compiler.
Watch Alex Rickabaugh explain the Angular compiler at AngularConnect 2019.
Here are some reasons you might want to use AOT.
Reasons
Details
Faster rendering
With AOT, the browser downloads a pre-compiled version of the application. The browser loads executable code so it can render the application immediately, without waiting to compile the application first.
Fewer asynchronous requests
The compiler
inlines
external HTML templates and CSS style sheets within the application JavaScript, eliminating separate ajax requests for those source files.
Smaller Angular framework download size
There's no need to download the Angular compiler if the application is already compiled. The compiler is roughly half of Angular itself, so omitting it dramatically reduces the application payload.
Detect template errors earlier
The AOT compiler detects and reports template binding errors during the build step before users can see them.
Better security
AOT compiles HTML templates and components into JavaScript files long before they are served to the client. With no templates to read and no risky client-side HTML or JavaScript evaluation, there are fewer opportunities for injection attacks.
Choosing a compiler
link
Angular offers two ways to compile your application:
Angular compile
Details
Just-in-Time (JIT)
Compiles your application in the browser at runtime. This was the default until Angular 8.
Ahead-of-Time (AOT)
Compiles your application and libraries at build time. This is the default starting in Angular 9.
When you run the
ng build
(build only) or
ng serve
(build and serve locally) CLI commands, the type of compilation (JIT or AOT) depends on the value of the
aot
property in your build configuration specified in
angular.json
.
By default,
aot
is set to
true
for new CLI applications.
See the CLI command reference and Building and serving Angular apps for more information.
How AOT works
link
The Angular AOT compiler extracts
metadata
to interpret the parts of the application that Angular is supposed to manage.
You can specify the metadata explicitly in
decorators
such as
@Component()
and
@Input()
, or implicitly in the constructor declarations of the decorated classes.
The metadata tells Angular how to construct instances of your application classes and interact with them at runtime.
In the following example, the
@Component()
metadata object and the class constructor tell Angular how to create and display an instance of
TypicalComponent
.
@Component({
selector:'app-typical',
template:'<div>A typical component for {{data.name}}</div>'})exportclassTypicalComponent{@Input() data:TypicalData;constructor(private someService:SomeService){…}}
The Angular compiler extracts the metadata
once
and generates a
factory
for
TypicalComponent
.
When it needs to create a
TypicalComponent
instance, Angular calls the factory, which produces a new visual element, bound to a new instance of the component class with its injected dependency.
Compilation phases
link
There are three phases of AOT compilation.
Phase
Details
1
code analysis
In this phase, the TypeScript compiler and
AOT collector
create a representation of the source. The collector does not attempt to interpret the metadata it collects. It represents the metadata as best it can and records errors when it detects a metadata syntax violation.
2
code generation
In this phase, the compiler's
StaticReflector
interprets the metadata collected in phase 1, performs additional validation of the metadata, and throws an error if it detects a metadata restriction violation.
3
template type checking
In this optional phase, the Angular
template compiler
uses the TypeScript compiler to validate the binding expressions in templates. You can enable this phase explicitly by setting the
fullTemplateTypeCheck
configuration option; see Angular compiler options.
Metadata restrictions
link
You write metadata in a
subset
of TypeScript that must conform to the following general constraints:
Limit expression syntax to the supported subset of JavaScript
Only reference exported symbols after code folding
Only call functions supported by the compiler
Decorated and data-bound class members must be public
For additional guidelines and instructions on preparing an application for AOT compilation, see Angular: Writing AOT-friendly applications.
Errors in AOT compilation commonly occur because of metadata that does not conform to the compiler's requirements (as described more fully below).
For help in understanding and resolving these problems, see AOT Metadata Errors.
Configuring AOT compilation
link
You can provide options in the TypeScript configuration file that controls the compilation process.
See Angular compiler options for a complete list of available options.
Phase 1: Code analysis
link
The TypeScript compiler does some of the analytic work of the first phase.
It emits the
.d.ts
type definition files
with type information that the AOT compiler needs to generate application code.
At the same time, the AOT
collector
analyzes the metadata recorded in the Angular decorators and outputs metadata information in
.metadata.json
files, one per
.d.ts
file.
You can think of
.metadata.json
as a diagram of the overall structure of a decorator's metadata, represented as an abstract syntax tree (AST).
Angular's schema.ts describes the JSON format as a collection of TypeScript interfaces.
Expression syntax limitations
link
The AOT collector only understands a subset of JavaScript.
Define metadata objects with the following limited syntax:
Syntax
Example
Literal object
{cherry: true, apple: true, mincemeat: false}
Literal array
['cherries', 'flour', 'sugar']
Spread in literal array
['apples', 'flour', ...]
Calls
bake(ingredients)
New
new Oven()
Property access
pie.slice
Array index
ingredients[0]
Identity reference
Component
A template string
`pie is ${multiplier} times better than cake`
Literal string
'pi'
Literal number
3.14153265
Literal boolean
true
Literal null
null
Supported prefix operator
!cake
Supported binary operator
a+b
Conditional operator
a ? b : c
Parentheses
(a+b)
If an expression uses unsupported syntax, the collector writes an error node to the
.metadata.json
file.
The compiler later reports the error if it needs that piece of metadata to generate the application code.
If you want
ngc
to report syntax errors immediately rather than produce a
.metadata.json
file with errors, set the
strictMetadataEmit
option in the TypeScript configuration file.
Angular libraries have this option to ensure that all Angular
.metadata.json
files are clean and it is a best practice to do the same when building your own libraries.
No arrow functions
link
The AOT compiler does not support function expressions
and arrow functions, also called
lambda
functions.
The AOT collector does not support the arrow function,
() => new Server()
, in a metadata expression.
It generates an error node in place of the function.
When the compiler later interprets this node, it reports an error that invites you to turn the arrow function into an
exported function
.
In version 5 and later, the compiler automatically performs this rewriting while emitting the
.js
file.
Code folding
link
The compiler can only resolve references to
exported
symbols.
The collector, however, can evaluate an expression during collection and record the result in the
.metadata.json
, rather than the original expression.
This allows you to make limited use of non-exported symbols within expressions.
For example, the collector can evaluate the expression
1 + 2 + 3 + 4
and replace it with the result,
10
.
This process is called
folding
.
An expression that can be reduced in this manner is
foldable
.
The collector can evaluate references to module-local
const
declarations and initialized
var
and
let
declarations, effectively removing them from the
.metadata.json
file.
The compiler could not refer to the
template
constant because it isn't exported.
The collector, however, can fold the
template
constant into the metadata definition by in-lining its contents.
The effect is the same as if you had written:
There is no longer a reference to
template
and, therefore, nothing to trouble the compiler when it later interprets the
collector's
output in
.metadata.json
.
You can take this example a step further by including the
template
constant in another expression:
The following table describes which expressions the collector can and cannot fold:
Syntax
Foldable
Literal object
yes
Literal array
yes
Spread in literal array
no
Calls
no
New
no
Property access
yes, if target is foldable
Array index
yes, if target and index are foldable
Identity reference
yes, if it is a reference to a local
A template with no substitutions
yes
A template with substitutions
yes, if the substitutions are foldable
Literal string
yes
Literal number
yes
Literal boolean
yes
Literal null
yes
Supported prefix operator
yes, if operand is foldable
Supported binary operator
yes, if both left and right are foldable
Conditional operator
yes, if condition is foldable
Parentheses
yes, if the expression is foldable
If an expression is not foldable, the collector writes it to
.metadata.json
as an AST for the compiler to resolve.
Phase 2: code generation
link
The collector makes no attempt to understand the metadata that it collects and outputs to
.metadata.json
.
It represents the metadata as best it can and records errors when it detects a metadata syntax violation.
It's the compiler's job to interpret the
.metadata.json
in the code generation phase.
The compiler understands all syntax forms that the collector supports, but it may reject
syntactically
correct metadata if the
semantics
violate compiler rules.
Public symbols
link
The compiler can only reference
exported symbols
.
Decorated component class members must be public.
You cannot make an
@Input()
property private or protected.
Data bound properties must also be public
Supported classes and functions
link
The collector can represent a function call or object creation with
new
as long as the syntax is valid.
The compiler, however, can later refuse to generate a call to a
particular
function or creation of a
particular
object.
The compiler can only create instances of certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
Compiler action
Details
New instances
The compiler only allows metadata that create instances of the class
InjectionToken
from
@angular/core
.
Supported decorators
The compiler only supports metadata for the Angular decorators in the
@angular/core
module.
Function calls
Factory functions must be exported, named functions. The AOT compiler does not support lambda expressions ("arrow functions") for factory functions.
Functions and static method calls
link
The collector accepts any function or static method that contains a single
return
statement.
The compiler, however, only supports macros in the form of functions or static methods that return an
expression
.
You can call the
wrapInArray
in a metadata definition because it returns the value of an expression that conforms to the compiler's restrictive JavaScript subset.
The Angular
RouterModule
exports two macro static methods,
forRoot
and
forChild
, to help declare root and child routes.
Review the source code
for these methods to see how macros can simplify configuration of complex NgModules.
Metadata rewriting
link
The compiler treats object literals containing the fields
useClass
,
useValue
,
useFactory
, and
data
specially, converting the expression initializing one of these fields into an exported variable that replaces the expression.
This process of rewriting these expressions removes all the restrictions on what can be in them because
the compiler doesn't need to know the expression's value —it just needs to be able to generate a reference to the value.
Without rewriting, this would be invalid because lambdas are not supported and
TypicalServer
is not exported.
To allow this, the compiler automatically rewrites this to something like:
This allows the compiler to generate a reference to
θ0
in the factory without having to know what the value of
θ0
contains.
The compiler does the rewriting during the emit of the
.js
file.
It does not, however, rewrite the
.d.ts
file, so TypeScript doesn't recognize it as being an export.
And it does not interfere with the ES module's exported API.
Phase 3: Template type checking
link
One of the Angular compiler's most helpful features is the ability to type-check expressions within templates, and catch any errors before they cause crashes at runtime.
In the template type-checking phase, the Angular template compiler uses the TypeScript compiler to validate the binding expressions in templates.
Enable this phase explicitly by adding the compiler option
"fullTemplateTypeCheck"
in the
"angularCompilerOptions"
of the project's TypeScript configuration file
(see Angular Compiler Options).
Template validation produces error messages when a type error is detected in a template binding
expression, similar to how type errors are reported by the TypeScript compiler against code in a
.ts
file.
my.component.ts.MyComponent.html(1,1)::Property'addresss' does not exist on type 'Person'.Did you mean 'address'?
The file name reported in the error message,
my.component.ts.MyComponent.html
, is a synthetic file
generated by the template compiler that holds contents of the
MyComponent
class template.
The compiler never writes this file to disk.
The line and column numbers are relative to the template string in the
@Component
annotation of the class,
MyComponent
in this case.
If a component uses
templateUrl
instead of
template
, the errors are reported in the HTML file referenced by the
templateUrl
instead of a synthetic file.
The error location is the beginning of the text node that contains the interpolation expression with the error.
If the error is in an attribute binding such as
[value]="person.address.street"
, the error
location is the location of the attribute that contains the error.
The validation uses the TypeScript type checker and the options supplied to the TypeScript compiler to control how detailed the type validation is.
For example, if the
strictTypeChecks
is specified, the error
The expression used in an
ngIf
directive is used to narrow type unions in the Angular
template compiler, the same way the
if
expression does in TypeScript.
For example, to avoid
Object is possibly 'undefined'
error in the template above, modify it to only emit the interpolation if the value of
person
is initialized as shown below:
Using
*ngIf
allows the TypeScript compiler to infer that the
person
used in the binding expression will never be
undefined
.
For more information about input type narrowing, see Improving template type checking for custom directives.
Non-null type assertion operator
link
Use the non-null type assertion operator to suppress the
Object is possibly 'undefined'
error when it is inconvenient to use
*ngIf
or when some constraint in the component ensures that the expression is always non-null when the binding expression is interpolated.
In the following example, the
person
and
address
properties are always set together, implying that
address
is always non-null if
person
is non-null.
There is no convenient way to describe this constraint to TypeScript and the template compiler, but the error is suppressed in the example by using
address!.street
.
The following are metadata errors you may encounter, with explanations and suggested corrections.
Expression form not supported
Reference to a local (non-exported) symbol
Only initialized variables and constants
Reference to a non-exported class
Reference to a non-exported function
Function calls are not supported
Destructured variable or constant not supported
Could not resolve type
Name expected
Unsupported enum member name
Tagged template expressions are not supported
Symbol reference expected
Expression form not supported
link
The compiler encountered an expression it didn't understand while evaluating Angular metadata.
Language features outside of the compiler's restricted expression syntax
can produce this error, as seen in the following example:
// ERRORexportclassFooish{…}…const prop =typeofFooish;// typeof is not valid in metadata…// bracket notation is not valid in metadata{ provide:'token', useValue:{[prop]:'value'}};…
You can use
typeof
and bracket notation in normal application code.
You just can't use those features within expressions that define Angular metadata.
Avoid this error by sticking to the compiler's restricted expression syntax
when writing Angular metadata
and be wary of new or unusual TypeScript features.
Reference to a local (non-exported) symbol
link
Reference to a local (non-exported) symbol 'symbol name'. Consider exporting the symbol.
The compiler encountered a referenced to a locally defined symbol that either wasn't exported or wasn't initialized.
The compiler generates the component factory, which includes the
useValue
provider code, in a separate module.
That
factory module can't reach back to
this
source module to access the local (non-exported)
foo
variable.
You could fix the problem by initializing
foo
.
let foo =42;// initialized
The compiler will fold the expression into the provider as if you had written this.
providers:[{ provide:Foo, useValue:42}]
Alternatively, you can fix it by exporting
foo
with the expectation that
foo
will be assigned at runtime when you actually know its value.
Adding
export
often works for variables referenced in metadata such as
providers
and
animations
because the compiler can generate
references
to the exported variables in these expressions. It doesn't need the
values
of those variables.
Adding
export
doesn't work when the compiler needs the
actual value
in order to generate code.
For example, it doesn't work for the
template
property.
// ERRORexportlet someTemplate: string;// exported but not initialized@Component({
selector:'my-component',
template: someTemplate
})exportclassMyComponent{}
The compiler needs the value of the
template
property
right now
to generate the component factory.
The variable reference alone is insufficient.
Prefixing the declaration with
export
merely produces a new error, "
Only initialized variables and constants can be referenced
".
Only initialized variables and constants
link
Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler.
The compiler found a reference to an exported variable or static field that wasn't initialized.
It needs the value of that variable to generate code.
The following example tries to set the component's
template
property to the value of the exported
someTemplate
variable which is declared but
unassigned
.
You'd also get this error if you imported
someTemplate
from some other module and neglected to initialize it there.
// ERROR - not initialized there eitherimport{ someTemplate } from './config';@Component({
selector:'my-component',
template: someTemplate
})exportclassMyComponent{}
The compiler cannot wait until runtime to get the template information.
It must statically derive the value of the
someTemplate
variable from the source code so that it can generate the component factory, which includes instructions for building the element based on the template.
To correct this error, provide the initial value of the variable in an initializer clause
on the same line
.
// CORRECTEDexportlet someTemplate ='<h1>Greetings from Angular</h1>';@Component({
selector:'my-component',
template: someTemplate
})exportclassMyComponent{}
Reference to a non-exported class
link
Reference to a non-exported class
<class name>
.
Consider exporting the class.
Metadata referenced a class that wasn't exported.
For example, you may have defined a class and used it as an injection token in a providers array but neglected to export that class.
Angular generates a class factory in a separate module and that factory can only access exported classes.
To correct this error, export the referenced class.
Angular generates a class factory in a separate module and that factory can only access exported functions.
To correct this error, export the function.
Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function.
The compiler does not currently support function expressions or lambda functions.
For example, you cannot set a provider's
useFactory
to an anonymous function or arrow function like this.
The
Window
type in the constructor is no longer a problem for the compiler because it
uses the
@Inject(WINDOW)
to generate the injection code.
Angular does something similar with the
DOCUMENT
token so you can inject the browser's
document
object (or an abstraction of it, depending upon the platform in which the application runs).
import{Inject} from '@angular/core';import{DOCUMENT} from '@angular/common';@Component({…})exportclassMyComponent{constructor(@Inject(DOCUMENT)private doc:Document){…}}
Name expected
link
The compiler expected a name in an expression it was evaluating.
This can happen if you use a number as a property name as in the following example.