Fast and productive web framework provided by Dart
Welcome to the first release candidate of AngularDart v5.0.0, with full support
for Dart 2. Please note that an up-to-date copy of dev
channel Dart SDK is
required (at least 2.0.0-dev.65
as of this writing) to use this version of
AngularDart. Additionally:
More details of changes to Dart 2 for web users are available on our webiste.
We are no longer expecting further breaking changes as we get closer to a final release (which itself is pending a final release of the Dart 2 SDK). Please continue to report stability issues, bugs, or requests for small non-breaking enhancements.
Thanks, and enjoy AngularDart!
Dependency injection was enhanced greatly for 5.0.0, primarily around using proper types (for Dart 2), and paths to enable much smaller code size (for everyone).
Provider
(and provide
) are soft deprecated, and in their place are four
new classes with more precise type signatures. Additionally, Provider
now
supports an optional type argument <T>
, making it Provider<T>
.
ValueProvider(Type, T)
and ValueProvider.forToken(OpaqueToken<T>, T)
instead of Provider(typeOrToken, useValue: ...)
.
FactoryProvier(Type, T)
and FactoryProvider.forToken(OpaqueToken<T>, T)
instead of Provider(typeOrToken, useFactory: ...)
.
ClassProvider(Type, useClass: T)
and
ClassProvider.forToken(OpaqueToken<T>, useClass: T)
instead of
Provider(typeOrToken, useClass: ...)
or an implicit Type
.
ExistingProvider(Type, T)
and
ExistingProvider.forToken(OpaqueToken<T>, T)
instead of
Provider(typeOrToken, useExisting: ...)
.
OpaqueToken
is now much more useful. Previously, it could be used to define
a custom, non-Type
to refer to something to be injected; commonly instead of
types like String
. For example, you might use an OpaqueToken
to refer to
the a URL to download a file from:
const downloadUrl = OpaqueToken('downloadUrl');
@Component(
providers: [
Provider(downloadUrl, useValue: 'https://a-site.com/file.zip'),
],
)
class Example {
Example(@Inject(downloadUrl) String url) {
// url == 'https://a-site.com/file.zip'
}
}
First, OpaqueToken
adds an optional type argument, making OpaqueToken<T>
.
The type argument, T
, should be used to refer to the Type
of the object
this token should be bound to:
const downloadUrl = OpaqueToken<String>('downloadUrl');
Coupled with the new named Provider
classes and their .forToken
named
constructor (see below), you now also have a way to specify the type of
providers using type inference:
@Component(
providers: [
// This is now a Provider<String>.
ValueProvider.forToken(downloadUrl, 'https://a-site.com/file.zip'),
],
)
Second, MultiToken<T>
has been added, and it extends OpaqueToken<List<T>>
.
This is an idiomatic replacement for the now deprecated multi: true
argument to the Provider
constructor:
const usPresidents = MultiToken<String>('usPresidents');
@Component(
providers: [
ValueProvider.forToken(usPresidents, 'George'),
ValueProvider.forToken(usPresidents, 'Abe'),
],
)
class Example {
Example(@Inject(usPresidents) List<String> names) {
// names == ['George', 'Abe']
}
}
Third, we heard feedback that the String
-based name of tokens was
insufficient for larger teams because the names could collide. Imagine 2
different tokens being registered with a name of 'importantThing'
! It is now
possible (but optional) to extend
either OpaqueToken
or MultiToken
to
create scoped custom token names:
class DownloadUrl extends OpaqueToken<String> {
const DownloadUrl();
}
class UsPresidents extends MultiToken<String> {
const UsPresidents();
}
class Example {
providers: const [
ValueProvider.forToken(DownloadUrl(), 'https://a-site.com/file.zip'),
ValueProvider.forToken(UsPresidents(), 'George'),
ValueProvider.forToken(UsPresidents(), 'Abe'),
],
}
Fourth, and finally, we'd like to repurpose @Inject
in the future, and let
you write less to inject tokens. So, OpaqueToken
and MultiToken
instances may now be used directly as annotations:
class Example {
Example(@DownloadUrl() String url, @UsPresidents() List<String> names) {
// url == 'https://a-site.com/file.zip'
// names == ['George', 'Abe']
}
}
InjectorFactory
, a function type definition of
Injector Function([Injector parent])
, was added and started to be used
across the framework. It normally indicates the ability to create a new
Injector
instance with an optional parent.
A new annotation, @GenerateInjector
, was added. It is now posibble to
generate, at compile-time, a standalone InjectorFactory
method for
providers, without explicitly wrapping in an @Component
:
// example.dart
import 'example.template.dart' as ng;
@GenerateInjector([
ClassProvider(HelloService),
])
final InjectorFactory rootInjector = ng.rootInjector$Injector;
Module
has been added as a new, more-typed way to encapsulate a collection
of Provider
instances. This is an optional feature to use instead of
nested const
lists to represent shared providers. For example:
const httpModule = [ /* Other providers and/or modules. */ ];
const commonModule = [
httpModule,
ClassProvider(AuthService, useClass: OAuthService),
FactoryProvider.forToken(xsrfToken, useFactory: readXsrfToken),
];
... you can represent this with the new typed Module
syntax:
const httpModule = Module( /* ... Configuration ... */);
const commonModule = Module(
include: [httpModule],
provide: [
ClassProvider(AuthService, useClass: OAuthService),
FactoryProvider.forToken(xsrfToken, useFactory: readXsrfToken),
],
);
The advantages here are numerous:
Less ambiguity around ordering of providers. Engineers would tend to try
and sort providers alphabetically, would of course, would lead to
problems. Module
specifically outlines that order is significant, and
that include
is processed before provide
.
Module
rejects using a Type
implicitly as a ClassProvider
. This
removes additional ambiguity around supporting List<dynamic>
, and while
more verbose, should lead to more correct use.
Module
tends to be more understandable by users of other dependency
injection systems such as Guice or Dagger, and reads better than a const
List
(which is a very Dart-only idiom).
NOTE: It is also possible to use Module
in @GenerateInjector
:
@GenerateInjector.fromModules([
commonModule,
])
final InjectorFactory exampleFromModule = ng.exampleFromModule$Injector;
NOTE: It is also possible to use Module
in ReflectiveInjector
:
// Using ReflectiveInjector is strongly not recommended for new code
// due to adverse effects on code-size and runtime performance.
final injector = ReflectiveInjector.resolveAndCreate([
commonModule,
]);
OpaqueToken
no longer overrides operator==
or hashCode
. In practice this
should have no effect for most programs, but it does mean that effectively
that only const
instances of OpaqueToken
(or MultiToken
) are valid.
It is no longer valid to provide a token type of anything other than Type
or
an OpaqueToken
(or MultiToken
). In the past anything from aribtrary
literals (such as a string - 'iAmAToken'
) or a custom const
instance of a
class were supported.
For defining whether a componenr or directive should provide itself for
injection, Visibility.none
has been renamed Visibility.local
to make it
more clear that it is accessable locally (within providers
for example).
Classes annotated with @Component
or @Directive
are no longer treated like
services annotated with @Injectable
, and not accessible (by default) to
ReflectiveInjector
. @Injectable
can always be added to these classes in
order to return to the old behavior.
Fixed a bug where calling get
on an Injector
injected in the context of an
@Component
or @Directive
-annotated class and passing a second argument
always returned null
(instead of that second argument) if the token was not
found.
Setting @Component(visibility: Visibility.none
) no longer applies to
providers
, if any. Note that Visibility.none
was always renamed
Visibility.local
in breaking changes above.
Fixed a bug where Provider(SomeType)
was not parsed correctly as an implicit
use of Provider(SomeType, useClass: SomeType
).
Fixed a bug where <ReflectiveInjectior>.get(X)
would throw with a message
of no provider found for X, even when the acutal cause was a missing
downstream dependency Y
. We now emit the correct message.
Some injection failures will display the chain of dependencies that were
attempted before a token was not found ('X -> Y -> Z'
) in development mode.
We are working on making sure this better error message shows up always but
it is likely to slip until after the v5 release.
It is no longer a build warning to have an @Injectable
-annotated service
with more than one constructor. This was originally meant to keep injection
from being too ambiguous, but there are understood patterns now (first
constructor), and there is no alternative present yet. We may re-add this as
a warning if there ends up being a mechanism to pick a constructor in the
future.
It is no longer a build warning to have @Injectable
-annotated services
with named constructor parameters. While they are still not supported for
injected, they were always successfully ignored in the past, and showing a
warning to the user on every build served no purpose.
If a private class is annotated with @Injectable()
the compiler fails. In
practice this caused a compilation error later in DDC/Dart2JS, but now the
AngularDart compiler will not emit invalid code.
Removed spurious/incorrect warnings about classes that are used as
interfaces needing @Injectable
(or needing to be non-abstract), which are
wrong and confusing.
The compiler behind initReflector()
has changed implementations and now
uses fully-scoped import statements instead of trying to figure out the
original scope (including import prefixes) of your source code. This was not
intended to be a breaking change.
NgTemplateOutlet
added ngTemplateOutletContext
for setting local
variables in an embedded view. These variables are assignable to template
input variables declared using let
, which can be bound within the template.
See the NgTemplateOutlet
documentation for examples.
Added a lifecycle event AfterChanges
, which is similar to OnChanges
, but
with a much lower performance cost - it does not take any parameters and is
suitable when you have multiple fields and you want to be notified when any
of them change:
class Comp implements AfterChanges {
@Input()
String field1;
@Input()
String field2;
@override
void ngAfterChanges() {
print('Field1: $field1, Field2: $field2');
}
}
It is possible to directly request Element
or HtmlElement
types from
content or view children instead of ElementRef
(which is deprecated). For
example:
@Component(
selector: 'uses-element',
template: '<div #div>1</div>'
)
class UsesElement {
@ViewChild('div')
// Could also be HtmlElement.
Element div;
}
Additionally, List<Element>
and List<HtmlElement>
for @ViewChildren
and @ContentChildren
no longer require read: Element
, and the type is
correctly inferred the same as a single child is.
Static properties and methods of a component may now be referenced without a receiver in the component's own template. For example:
Before: ExampleComponent
as receiver is necessary.
@Component(
selector: 'example',
template: '<h1>{{ExampleComponent.title}}</h1>',
)
class ExampleComponent {
static String title;
}
After: No receiver is necessary.
@Component(
selector: 'example',
template: '<h1>{{title}}</h1>',
)
class ExampleComponent {
static String title;
}
@HostListener()
can now automatically infer the const ['$event']
parameter when it is omitted but the bound method has a single argument:
class Comp {
@HostListener('click')
void onClick(MouseEvent e) {}
}
Added <ng-container>
, an element for logical grouping that has no effect
on layout. This enables use of the *-syntax for structural directives, without requiring the cost an HTML element.
Before
<ul>
<template ngFor let-user [ngForOf]="users">
<li *ngIf="user.visible">{{user.name}}</li>
</template>
</ul>
After
<ul>
<ng-container *ngFor="let user of users">
<li *ngIf="user.visible">{{user.name}}</li>
</ng-container>
</ul>
In dev mode only, an attribute named from
is now added to each <style>
tag whose value identifies the source file URL and name of the component from
which the styles originate.
Support for the suffix .if
for attribute bindings, both in a template and
in a @HostBinding()
. Accepts an expression of type bool
, and adds an
attribute if true
, and removes it if false
and closes
https://github.com/dart-lang/angular/issues/1058:
<!-- These are identical -->
<button [attr.disabled]="isDisabled ? '' : null"></button>
<button [attr.disabled.if]="isDisabled"></button>
We now have a new, more stricter template parser, which strictly requires
double quotes ("..."
) versus single quotes, and in general enforces a
stricter HTML-like syntax. It does produce better error messages than before.
We removed support for ngNonBindable
in the new template syntax.
The fields inputs:
, outputs:
, and host:
have been removed from
@Directive(...)
and @Component(...
). It is expected to use the member
annotations (@Input()
, @Output()
, @HostBinding()
, @HostLitsener()
)
instead.
The default for @Component(preserveWhitespace: ...)
is now true
. Many
improvements were put into the whitespace optimziation in order to make the
results easier to understand and work around.
<AsyncPipe>.transform
no longer returns the (now removed) WrappedValue
when a transformed result changes, and relies on regular change detection.
Pipes no longer support private types in their transform
method signature.
This method's type is now used to generate a type annotation in the generated
code, which can't import private types from another library.
Using @deferred
no longer supports the legacy bootstrap processes. You must
use runApp
(or runAppAsync
) to bootstrap the application without relying
on initReflector()
.
<ComponentRef>.componentType
always throws UnsupportedError
, and will be
removed in a later minor release. This removes our last invocation of
.runtimeType
, which has potentially severe code-size implications for some
apps.
QueryList
for @ViewChildren
and @ContentChildren
has been removed, in
favor of just a plain List
that is replaced with a new instance when the
children change (instead of requiring a custom collection and listener):
class Comp {
@ViewChildren(ChildComponent)
set viewChildren(List<ChildComponent> viewChildren) {
// ...
}
// Can also be a simple field.
@ContentChildren(ChildComponent)
List<ChildComponent> contentChildren;
}
EventEmitter
was removed in favor using Stream
and StreamController
.
COMMON_DIRECTIVES
was renamed commonDirectives
.
CORE_DIRECTIVES
was renamed coreDirectives
.
COMMON_PIPES
was renamed commonPipes
.
Private types can't be used in template collection literals bound to an input. This is a consequence of fixing a cast warning that is soon to be an error caused by the code generated for change detecting collection literals in templates. See https://github.com/dart-lang/angular/issues/844 for more information.
SafeInnerHtmlDirective
is no longer injectable.
The following types were never intended for external use and are no longer
exported by package:angular/security.dart
:
SafeHtmlImpl
SafeScriptImpl
SafeStyleImpl
SafeResourceUrlImpl
SafeUrlImpl
SafeValueImpl
To mark a value as safe, users should inject DomSanitizationService
and
invoke the corresponding bypassSecurityTrust*()
method, instead of
constructing these types directly.
ComponentResolver
was removed, and SlowComponentLoader
was deprecated.
Methods in lifecycle hooks have void
return type. This is breaking change
if the override doesn't specify return type and uses return
without any
value. To fix add a void
or Future<void>
return type to the override:
class MyComp implements OnInit {
@override
void ngOnInit() {
// ...
}
}
Removed the rarely used template
attribute syntax. Uses can be replaced
with either the *
micro-syntax, or a <template>
element.
Before
<div template="ngFor let item of items; trackBy: trackById; let i=index">
{{i}}: {{item}}
</div>
After
<!-- * micro-syntax -->
<div *ngFor="let item of items; trackBy: trackById; let i=index">
{{i}}: {{item}}
</div>
<!-- <template> element -->
<template
ngFor
let-item
[ngForOf]="items"
[ngForTrackBy]="trackById"
let-i="index">
<div>
{{i}}: {{item}}
</div>
</template>
It is now a compile error to implement both the DoCheck
and OnChanges
lifecycle interfaces. DoCheck
will never fill in values for the Map
in
OnChanges
, so this compile-error helps avoid bugs and directs the user to
use DoCheck
and AfterChanges
instead.
Using a suffix/unit for the [attr.name.*]
syntax other than the newly
introduced [attr.name.if]
is now a compile-error. These binding suffixes
were silently ignored (users were likely confused with [style.name.px]
,
which is supported).
Fixed a bug where an @deferred
components were still being linked to in
initReflector()
.
Fixed a bug where errors thrown in event listeners were sometimes uncaught
by the framework and never forwarded to the ExceptionHandler
.
Fixed a bug where DatePipe
didn't format millisecondsSinceEpoch
in the
local time zone (consistent with how it formats DateTime
).
Testability now includes ComponentState
updates. Due to prior use of
animationFrame
callback, NgTestBed
was not able to detect a stable state.
String literals bound in templates now support Unicode escapes of the form
\u{?-??????}
. This enables support for Unicode supplementary planes, which
includes emojis!
Inheriting from a class that defines a @HostBinding()
on a static member
no longer causes the web compiler (Dartdevc or Dart2JS) to fail. We
previously inherited these bindings and generated invalid Dart code. Given
that static members are not inherited in the Dart language, it made sense
to give a similar treatment to these annotations. Instance-level members are
still inherited:
class Base {
@HostBinding('title')
static const hostTitle = 'Hello';
@HostBinding('class')
final hostClass = 'fancy';
}
// Will have DOM of <fancy-button class="fancny"> but *not* title="Hello".
@Component(
selector: 'fancy-button',
template: '...',
)
class FancyButton extends Base {}
Fixed a bug where a recursive type signature on a component or directive
would cause a stack overflow. We don't support generic type arguments yet
(the reified type is always dynamic
), but the compiler no longer crashes.
Types bound from generics are now properly resolved in a component when inheriting from a class with a generic type. For example, the following used to be untyped in the generated code:
class Container<T> {
@Input()
T value;
}
class StringContainerComponent implements Container<String> {}
Both ComponentFactory
and ComponentRef
now have a generic type parameter
<T>
, which is properly reified where T
is the type of the component class.
The $implicit
(iterable) value in *ngFor
is now properly typed whenever
possible. It was previously always typed as dynamic
, which caused dynamic lookups/calls at runtime, and hid compilation errors.
The type of <EmbeddedViewRef>.rootNodes
and <ViewRefImpl>.rootNodes
has
been tightened from List<dynamic>
to List<Node>
(where Node
is from
dart:html
).
A combination of compile errors and warnings are produced when it seem that
template
, templateUrl
, style
, or styleUrls
are either incorrect or
missing when required.
The compiler now reports an actionable error when an annotation is used on a private class member. We also report errors when various annotations are used improperly (but not in all cases yet).
The compiler optimizes *ngIf
usages where the content is pure HTML.
The view compiler is able to tell when exports: [ ... ]
in an @Component
are static reads and are immutable (such as String
). This allows us to
optimize the generated code.
The process for starting your AngularDart application changed significantly:
For most applications, we recommend now strongly recommend using the new
runApp
function. Instead of starting your application by passing the
Type
of an @Component
-annotated class
, you now pass a
ComponentFactory
, the generated code for a component:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runApp(ng.RootComponentNgFactory);
}
@Component(
selector: 'root',
template: 'Hello World',
)
class RootComponent {}
To provide top-level services, use the createInjector
parameter, and
pass a generated InjectorFactory
for a top-level annotated with
@GenerateInjector
:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runApp(ng.RootComponentNgFactory, createInjector: rootInjector);
}
class HelloService {
void sayHello() => print('Hello!');
}
@GenerateInjector([
ClassProvider(HelloService),
])
final InjectorFactory rootInjector = ng.rootInjector$Injector;
A major difference between runApp
and previous bootstrapping code is
the lack of the initReflector()
method or call, which is no longer
needed. That means using runApp
disables the use of
SlowComponentLoader
and ReflectiveInjector
, two APIs that require
this extra runtime metadata.
To enable use of these classes for migration purposes, use
runAppLegacy
:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runAppLegacy(
RootComponent,
createInjectorFromProviders: [
ClassProvider(HelloService),
],
initReflector: ng.initReflector,
);
}
NOTE: initReflector
and runAppLegacy
disables tree-shaking on
any class annotated with @Component
or @Injectable
. We strongly
recommend migrating to the runApp
pattern.
The top-level function bootstrap
was deleted. This function always threw a
runtime exception since 5.0.0-alpha+5
, and was a relic of when a code
transformer rewrote it automatically as bootstrapStatic
.
Dropped support for @AngularEntrypoint
and rewriting entrypoints to
automatically use initReflector()
and bootstrapStatic
. This is no longer
supported in the new build system.
RenderComponentType
is no longer part of the public API.
<ApplicationRef>
.componentFactories
, .componentTypes
, .zone
, and
.registerBootstrapListener
were removed; these were used internally by the
legacy router and not intended to be part of the public API.
PLATFORM_INITIALIZERS
was removed.
APP_INITIALIZER
was removed. A similar functionality can be accomplished
using the runAppAsync
or runAppLegacyAsync
functions with the
beforeComponentCreated
callback.
PlatformRef
and PlatformRefImpl
were removed.
<NgZone>.onStable
has been renamed to onTurnDone
.
<NgZone>.onUnstable
has been renamed to onTurnStart
.
The context
parameter was removed from <TemplateRef>.createEmbeddedView
.
The following relatively unused fields and functions were removed:
APPLICATION_COMMON_PROVIDERS
BROWSER_APP_COMMON_PROVIDERS
BROWSER_APP_PROVIDERS
PACKAGE_ROOT_URL
ErrorHandlingFn
UrlResolver
WrappedTimer
WrappedValue
ZeroArgFunction
appIdRandomProviderFactory
coreBootstrap
coreLoadAndBootstrap
createNgZone
createPlatform
disposePlatform
getPlatform
Running within the NgZone
will no longer cause addtional turns to occur
within it's parent's zone. <NgZone>.run()
will now run inside the parent
zone's run()
function as opposed to the other way around.
The compilation mode --debug
(sparingly used externally) is now no longer
supported. Some flags and code paths in the compiler still check/support it
but it will be removed entirely by the final release and should no longer
be used. We will rely on assertion-based tree-shaking (from Dart2JS
)
going forward to emit debug-only conditional code.
<ExceptionHandler>.call
with a null
exception.Removed SafeScript
and its associated APIs. There was no path through the
compiler that made use of this type.
Removed the sanitize()
method from SanitizationService
. This method was
entirely unused. Use the more specific methods such as sanitizeHtml()
and
sanitizeUrl()
instead.
Generated ComponentFactory
instances are no longer functionally const
.
This is to prevent issues where users attempt to use generated component
factories in their own const
contexts, which was known to cause problems
in some build systems.
MultiToken<T>
now extends OpaqueToken<List<T>>
. This should have no real
affect on most programs, unless you manually typed your MultiToken
such as
usPresidents = const MultiToken<List<String>>()
. This will allow future
features for the Injector
interface:
https://github.com/dart-lang/angular/issues/555.
Using a suffix/unit for the [attr.name.*]
syntax other than the newly
introduced [attr.name.if]
is now a compile-error. These binding suffixes
were silently ignored (users were likely confused with [style.name.px]
,
which is supported).
ReflectiveInjector.resolveStaticAndCreate
was added as an experimental
API (subject to breaking change at any time). This is primarily for adopting
runApp
incrementally in existing large code-bases that use
ReflectiveInjector
. See https://github.com/dart-lang/angular/issues/1426
for details.
Support for the suffix .if
for attribute bindings, both in a template and
in a @HostBinding()
. Accepts an expression of type bool
, and adds an
attribute if true
, and removes it if false
and closes
https://github.com/dart-lang/angular/issues/1058:
<!-- These are identical -->
<button [attr.disabled]="isDisabled ? '' : null"></button>
<button [attr.disabled.if]="isDisabled"></button>
The generated .template.dart
code now properly subtypes AppView<C>
where
C
is the annotated @Component
class. This avoids implicit down-casts in
the framework.
Fixed a bug where the compiler crashed if an injectable token contained the
type void
(directly or in some nested type like List<void>
).
void
and Null
types to appear in tokens.angular 5.0.0-beta
.RouterHook.canNavigate()
.NgZoneStabilizer
waits for the longest pending timer during update()
.--debug
(sparingly used externally) is now no longer
supported. Some flags and code paths in the compiler still check/support it
but it will be removed entirely by the final release and should no longer
be used. We will rely on assertion-based tree-shaking (from Dart2JS
)
going forward to emit debug-only conditional code.The from
attribute added to <style>
tags created for component styles
now refers to the component URL, rather than its template URL.
AngularDart now has official support of the optional new/const feature of Dart2. The most significant impact to the framework will be increased terse-ness of the various metadata annotations. Please file issues if you see any unexpected behavior. Here is one example:
// Before
@Component(
selector: 'comp',
directives: const [
FooComponent,
],
providers: const [
const ClassProvider(SomeService),
],
)
// After
@Component(
selector: 'comp',
directives: [
FooComponent,
],
providers: [
ClassProvider(SomeService),
],
)
Prevented a crash in NgTemplateOutlet
caused by a specific sequence of
inputs to [ngTemplateOutlet]
.
Relaxed type checks for events bound with a single parameter. In practice
this started failing in Dart2JS with --preview-dart-2
, potentially where
synthetic events were being passed instead of the real DOM event:
<some-comp (focus)="handleFocus($event)"></some-comp>
import 'dart:html';
void handleFocus(FocusEvent e) {
// Failed when 'e' was a CustomEvent or not strictly a FocusEvent.
}
Fixed a bug where a recursive type signature on a component or directive
would cause a stack overflow. We don't support generic type arguments yet
(the reified type is always dynamic
), but the compiler no longer crashes.
Fixed a bug where Iterable.retype
being removed from the SDK caused the
compiler to crash on the newest Dart2 -dev SDKs. We now use .cast
instead.
-dev.60
.CompilerFlags
no longer parses and supports the 'debug'
option and
genDebugInfo
is always false
, and is deprecated pending removal in a
future version.reset
method added to AbstractControl
and AbstractControlDirective
.
RequiredValidator
now has a required
input. This allows the required
property to be toggled at runtime. Previously, this could only be set
statically at compile time.
Control.invalid
getter added.
Remove deprecated NG_VALUE_ACCESSOR
token. Use ngValueAccessor
instead.
Abstract updateValue
method added to AbstractControl
. All subclasses of
AbstractControl
will need to implement this method.
Preserves NavigationParams
on redirection.
Added canNavigate
to RouterHook
.
Navigation will no longer succeed for an empty path if it doesn't match a route.
Removed throwsInAngular
(was a no-op since alpha+8
). Use throwsA
.
Removed NgTestFixture#query/queryAll
, as debug-mode is being turned down.
Added isStable
API to NgTestStabilizer
.
Run DelegatingNgTestStabilizer
stabilizers one by one instead of run all
at once. update()
for each stabilizers will be run at least once. After
that, it will only be run if the current stabilizer is not stable.
In dev mode only, an attribute named from
is now added to each <style>
tag whose value identifies the source file URL and name of the component
from which the styles originate.
Styles that are inlined into generated .dart
code (either in
.template.dart
or .css.dart
) now are final
rather than const
when
specified. This allows incremental compilers (such as DDC) to avoid
cascading rebuilds when only CSS styles are changed (not HTML or Dart).
Expression conversion failures are now reported as build failures, with source context if available, rather than as bugs in the compiler.
Unresolved exports
arguments in @Component
annotations will no longer
crash the compiler and are now reported as build failures.
-dev.56
.sdk: ">=2.0.0-dev.56.0 <2.0.0"
.sdk: ">=2.0.0-dev.56.0 <2.0.0"
.normalizePath()
from an internal type to Location
to give
fine-grained control over path normalization.Fixed a regression where the RouterLinkActive
directive would not activate
for empty paths (including '/'
).
Fixed a bug where if a component threw an exception during routing the router would get in a perpetual bad state where it was impossible to route away or otherwise use the application.
NgTestStabilizerFactory
public.A directive with Visibility.local
may now be injected by another directive
on the same host element, or by a descendant within the same view.
Removed support for (deprecated) host: const { ... }
syntax in a
@Directive
or @Component
annotation. This can be easily migrated to use
@HostBinding
or @HostListener
.
Pins angular_ast
and angular_compiler
to avoid future versioning issues.
AttributeAst
which would extend past EOF when
recovering from a value with an unclosed quote.sdk: ">=2.0.0-dev.55.0 <2.0.0"
.sdk: ">=2.0.0-dev.55.0 <2.0.0"
.platformStrategy
field of Location
to locationStrategy
,
since it's of type LocationStrategy
.Fixed a bug where a WillNeverStabilizeError
was thrown whenever there was
a non-zero length Timer
being executed. This was due to a bug in how the
NgStabilizer
was executing - constantly calling the update
function
instead of calling it once and waiting for stabilization.
Fixed a bug where stabilizers are considered stable even when some of them are not.
It is now a compile-time error to place a @HostBinding
or @HostListener
annotation on a class member that does not accept the respective annotation.
For example, the following snippet will break at compile-time:
class Comp {
// Deceptively, this did not do *anything* before, but the user was never
// told that the annotation was effectively a no-op. It will fail now!
@HostListener('click')
Function onClick;
}
... as part of this refactor, error messages in general around use of these annotations have been greatly improved.
The semantics of @Component(preserveWhitespace: false)
(the default flag)
have changed somewhat in this release due to user feedback both internally
and externally (see https://github.com/dart-lang/angular/issues/804). The
easiest way to explain the changes are with this example:
Foo <strong>Bar</strong> Baz
... used to display "FooBarBaz" in the old semantics, and now displays "Foo Bar Baz" in the new semantics. There are some cases where generated code is slightly larger, and other cases where it is smaller (we have some smarter heuristics around safe places to collapse whitespace).
Added <ng-container>
, an element for logical grouping that has no effect
on layout. This enables use of the *-syntax for structural directives,
without requiring the cost an HTML element.
Before
<ul>
<template ngFor let-user [ngForOf]="users">
<li *ngIf="user.visible">{{user.name}}</li>
</template>
</ul>
After
<ul>
<ng-container *ngFor="let user of users">
<li *ngIf="user.visible">{{user.name}}</li>
</ng-container>
</ul>
.ng_placeholder
files will be excluded from --output
builds. .css
and
.html
files will be excluded by default from the lib/
directory for
release builds. Disable entirely with:
targets:
$default:
angular|component_source_cleanup:
options:
enabled: false
or exclude some sources by glob:
targets:
$default:
angular|component_source_cleanup:
options:
exclude:
- "lib/non_angular_style.css"
- "lib/something/**"
@HostBinding()
for static
const
or final
fields are set at build
time rather than being change-detected.
Inheriting from a class that defines a @HostBinding()
on a static member
no longer causes the web compiler (Dartdevc or Dart2JS) to fail. We
previously inherited these bindings and generated invalid Dart code. Given
that static members are not inherited in the Dart language, it made sense
to give a similar treatment to these annotations. Instance-level members are
still inherited:
class Base {
@HostBinding('title')
static const hostTitle = 'Hello';
@HostBinding('class')
final hostClass = 'fancy';
}
// Will have DOM of <fancy-button class="fancny"> but *not* title="Hello".
@Component(
selector: 'fancy-button',
template: '...',
)
class FancyButton extends Base {}
Styles from an @import
statement are now included before the styles
declared within the file, instead of after. This allows a style declared
within a file to override an imported one of equivalent specificity.
URLs from @import
statements with the package
scheme are no longer
resolved to the packages/
directory. The package
scheme is now preserved
which the build ecosystem understands.
In ReflectiveInjector
, .injectFromSelfOptional
now checks if it is truly
a instance cache misses before creating a new instance.
Exported ParsedAnnotationAst
.
Added ContainerAst
.
Annotations may now be assigned values.
Added support for annotations on ContainerAst
.
The *
micro-syntax now supports leading whitespace.
Whitespace between <ng-content>
and </ng-content>
will no longer yield a
parsing error.
ComponentReader
.DirectiveVisitor
, and removed $HostBinding
and $HostListener
.Control.markAsPristine
added. This will clear the dirty
property.NgControlName
will no longer initialize with null
if a value is
specified by 'ngModel'.
The touched
property of Control
s is now propagated to parents /
children.
_createDynamic
does not preserve rootInjector
.Removed ApplicationRef.injector
from the public API. This was a sparingly
used API, and it was usually incorrect to specifically use this injector
over the current injector in the component context or the root component's
Injector
.
Removed the experimental parent
argument to ApplicationRef.bootstrap
.
It is no longer needed since all applications use the same bootstrap code
branch (either runApp
, or code that eventually runs through runApp
).
Removed the rarely used template
attribute syntax. Uses can be replaced
with either the *
micro-syntax, or a <template>
element.
Before
<div template="ngFor let item of items; trackBy: trackById; let i=index">
{{i}}: {{item}}
</div>
After
<!-- * micro-syntax -->
<div *ngFor="let item of items; trackBy: trackById; let i=index">
{{i}}: {{item}}
</div>
<!-- <template> element -->
<template
ngFor
let-item
[ngForOf]="items"
[ngForTrackBy]="trackById"
let-i="index">
<div>
{{i}}: {{item}}
</div>
</template>
The new Module
syntax for dependency injection is shipped! This is an
optional feature instead of using nested const
lists to represent sets
of shared providers. For example, instead of the following:
const httpModule = const [ /* Other providers and/or modules. */ ];
const commonModule = const [
httpModule,
const ClassProvider(AuthService, useClass: OAuthService),
const FactoryProvider.forToken(xsrfToken, useFactory: readXsrfToken),
];
... you can represent this with the new typed Module
syntax:
const httpModule = const Module( /* ... Configuration ... */);
const commonModule = const Module(
include: const [httpModule],
provide: const [
const ClassProvider(AuthService, useClass: OAuthService),
const FactoryProvider.forToken(xsrfToken, useFactory: readXsrfToken),
],
);
The advantages here are numerous:
Less ambiguity around ordering of providers. Engineers would tend to try
and sort providers alphabetically, would of course, would lead to
problems. Module
specifically outlines that order is significant, and
that include
is processed before provide
.
Module
rejects using a Type
implicitly as a ClassProvider
. This
removes additional ambiguity around supporting List<dynamic>
, and while
more verbose, should lead to more correct use.
Module
tends to be more understandable by users of other dependency
injection systems such as Guice or Dagger, and reads better than a const
List
(which is a very Dart-only idiom).
We're not yet updating the [style guide] (https://github.com/dart-lang/angular/blob/master/doc/effective/di.md) in this release, but we are looking for users to help validate that this is the way to go for the future.
NOTE: It is also possible to use Module
in @GenerateInjector
:
@GenerateInjector.fromModules(const [
const Module(
include: const [
const Module(
provide: const [
const ValueProvider(ExampleService, const ExampleService()),
],
),
],
provide: const [
const ValueProvider(ExampleService2, const ExampleService2()),
const ExistingProvider(ExampleService, ExampleService2),
],
),
])
final InjectorFactory exampleFromModule = ng.exampleFromModule$Injector;
NOTE: It is also possible to use Module
in ReflectiveInjector
:
// Using ReflectiveInjector is strongly not recommended for new code
// due to adverse effects on code-size and runtime performance.
final injector = ReflectiveInjector.resolveAndCreate([
const Module(
include: const [
const Module(
provide: const [
const ValueProvider(ExampleService, const ExampleService()),
],
),
],
provide: const [
const ValueProvider(ExampleService2, const ExampleService2()),
const ExistingProvider(ExampleService, ExampleService2),
],
),
]);
@HostListener()
can now automatically infer the const ['$event']
parameter when it is omitted but the bound method has a single argument:
class Comp {
@HostListener('click')
void onClick(MouseEvent e) {}
}
The *
micro-syntax now supports newlines after an identifier.
The compiler is now reporting errors again for invalid property bindings on
<template>
elements.
*
micro-syntax now supports newlines after an identifier.ModuleReader.extractProviderObjects
to use in the view compiler.logFine
as a new top-level API.ngDisabled
input to all Control directives.NgControlGroup
can no longer be injected directly. It can still be
injected as a ControlContainer
.
NgControlName
and NgFormControl
can no longer be injected directly. They
can still be injected as a NgControl
.
CheckboxControlValueAccessor
, DefaultValueAccessor
,
NumberValueAccessor
, RadioControlValueAccessor
, and NgSelectOption
can
no longer be injected directly.
navigateByUrl
to Router
.pub run angular_test
was entirely removed. This hasn't worked since
2.0.0-alpha+3
, but instead threw an error message reminding users it was
no longer supported.The minimum SDK version is now sdk: ">=2.0.0-dev.46.0 <2.0.0"
.
The process for starting your AngularDart application changed significantly:
For most applications, we recommend now strongly recommend using the new
runApp
function. Instead of starting your application by passing the
Type
of an @Component
-annotated class
, you now pass a
ComponentFactory
, the generated code for a component:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runApp(ng.RootComponentNgFactory);
}
@Component(
selector: 'root',
template: 'Hello World',
)
class RootComponent {}
To provide top-level services, use the createInjector
parameter, and
pass a generated InjectorFactory
for a top-level annotated with
@GenerateInjector
:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runApp(ng.RootComponentNgFactory, createInjector: rootInjector);
}
class HelloService {
void sayHello() => print('Hello!');
}
@GenerateInjector(const [
const ClassProvider(HelloService),
])
final InjectorFactory rootInjector = ng.rootInjector$Injector;
A major difference between runApp
and previous bootstrapping code is
the lack of the initReflector()
method or call, which is no longer
needed. That means using runApp
disables the use of
SlowComponentLoader
and ReflectiveInjector
, two APIs that require
this extra runtime metadata.
To enable use of these classes for migration purposes, use
runAppLegacy
:
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
void main() {
runAppLegacy(
RootComponent,
createInjectorFromProviders: [
const ClassProvider(HelloService),
],
initReflector: ng.initReflector,
);
}
NOTE: initReflector
and runAppLegacy
disables tree-shaking on
any class annotated with @Component
or @Injectable
. We strongly
recommend migrating to the runApp
pattern.
The APP_INITIALIZERS
token was removed. The closest functionality
(running a function that returns a Future
before creating the root
component) is using runAppAsync
or runAppLegacyAsync
functions with
a befofreComponentCreated
callback:
import 'dart:async';
import 'package:angular/angular.dart';
// ignore: uri_has_not_been_generated
import 'main.template.dart' as ng;
Future<String> fetchSomeText() => ...
void main() {
runApp(
ng.RootComponentNgFactory,
beforeComponentCreated: (injector) {
final prefetch = injector.get(Prefetch) as Prefetch;
return prefetch.prefetchSomeData();
},
// @GenerateInjector could be used instead. This is a simple example.
createInjector: (parent) {
return new Injector.map({
Prefetch: new Prefetch();
}, parent);
},
);
}
@Component(
selector: 'root',
template: 'Hello World',
)
class RootComponent {}
class Prefetch {
Future<void> prefetchSomeData() => ...
}
The top-level function bootstrap
was deleted. This function always
threw a runtime exception since 5.0.0-alpha+5
, and was a relic of when
a code transformer rewrote it automatically as bootstrapStatic
.
The top-level function bootstrapStatic
is now deprecated. The
closest equivalent is the new runAppLegacy
_or runAppLegacyAsync
functions.
The interface PlatformRef
(and PlatformRefImpl
) were removed. They were
not used, and added an unnecessary code-size overhead to every application.
Removed the deprecated QueryList
class, List
is used instead, only.
Removed ApplicationRef.registerBootstrapListener
, which was unused.
The compiler now warns when a @Component.styles
seems to reference a file
on disk, such as styles: const ['a.css']
(this is usually an accident). We
already warned for template: 'a.html'
.
Running within an angular zone will no longer cause addtional turns to occur within it's parent's zone. ngZone's run() will now run inside the parent zone's run() function as opposed to the other way around.
The minimum SDK version is now sdk: ">=2.0.0-dev.46.0 <2.0.0"
.
The *
micro-syntax now supports binding to the primary input when followed
by additional input or let
bindings. Previously the micro-syntax supported
binding to the primary input only in isolation.
Example usage enabled by this change.
Before:
<template [foo]="expr1" [fooContext]="expr2">
<div></div>
</template>
After:
<div *foo="expr1; context: expr2"></div>
sdk: ">=2.0.0-dev.46.0 <2.0.0"
.The minimum SDK version is now sdk: ">=2.0.0-dev.46.0 <2.0.0"
.
Add ControlValueAccessor.onDisabledChanged()
method. All implementations
of ControlValueAccessor
need to add this method.
Remove include
and exclude
methods from ControlGroup
. These can be
replaced with calls to markAsEnabled
and markAsDisabled
instead.
Before: controlGroup.include('foo');
After: controlGroup.controls['foo'].markAsEnabled();
CheckboxControlValueAccessor
now implements ControlValueAccessor<bool>
and RadioControlValueAccessor
now implements
ControlValueAccessor<RadioButtonState>
. Previously, they were both
ControlValueAccessor<dynamic>
.
sdk: ">=2.0.0-dev.46.0 <2.0.0"
.The minimum SDK version is now sdk: ">=2.0.0-dev.46.0 <2.0.0"
.
DEPRECATED: throwsInAngular
is now a no-op, and can be removed.
Static properties and methods of a component may now be referenced without a receiver in the component's own template.
Before: ExampleComponent
as receiver is necessary.
@Component(
selector: 'example',
template: '<h1>{{ExampleComponent.title}}</h1>',
)
class ExampleComponent {
static String title;
}
After: No receiver is necessary.
@Component(
selector: 'example',
template: '<h1>{{title}}</h1>',
)
class ExampleComponent {
static String title;
}
The field COMMON_PIPES
has been renamed to commonPipes
.
The field zone
in ApplicationRef
has been removed from the API.
The token PLATFORM_INITIALIZERS
has been removed. This was used sparingly to
run functions before the application was started, but can just as easily be
converted into running functions in main()
before.
The token APP_INITIALIZER
is now DEPRECATED. It was used sparingly,
and can easily be replaced by running functions in your root component with
an *ngIf
guard for initialization.
Methods in lifecycle hooks have void
return type. This is breaking change
if the override doesn't specify return type and uses return
without any
value. To fix add a void
or Future<void>
return type to the override:
class MyComp implements OnInit {
@override
void ngOnInit() {
// ...
}
}
Removed deprecated ComponentResolver
class.
Removed deprecated componentFactories
and componentTypes
getters from
the ApplicationRef
class. These were used internally by the legacy router
and were not intended to be parts of the public API.
String literals bound in templates now support Unicode escapes of the form
\u{?-??????}
. This enables support for Unicode supplementary planes, which
includes emojis!
Using @GenerateInjector
with a ValueProvider
where the value is either a
top-level function or static-class method no longer crashes.
package:angular_ast
. There is no flag to enable the old behavior
but please reach out if you see issues in this release.BREAKING CHANGE: We no longer support parsing Dart expressions as part
of parsing the template AST. We hope to re-add some support for this by
migrating the existing parser in package:angular
, but we are likely not to
have a stable API for some time.
BREAKING CHANGE: Deleted ExpressionParserVisitor
(related to above).
TokenReader
no longer supports arbitrary const objects or literals.MemorizedForm
directive. This is a form that will not remove controls
if the control is taken out of the view, for example with a [NgIf].optionals
param from ControlGroup
constructor. This will soon be
replaced by disabled
state for all Controls
. See
https://github.com/dart-lang/angular/issues/1037 for more details.APP_BASE_HREF
was removed in favor of appBaseHref
.
When navigating to and from the same implementation of CanReuse
, if it
returns true, the implementation will remain attached to the DOM.
Previously, components were unconditionally detached from the DOM on
navigation, and reusable components would simply be reattached.
This may be a change to the scroll behavior of your components, as temporarily removing reused components from the DOM could reset the scroll position if no other content was present.
CanNavigate
, CanDeactivate
, and OnDeactivate
should now always be
invoked on the active instance of a component rather than the instance
created during route resolution. This previously could occur when navigating
away and back to a nested route whose parent was not reusable.Compiler can optimize field accesses to classes that are statically accessed
using exports
.
Compiler will now set immutable Text
node values at component build time.
For more details see https://github.com/dart-lang/angular/issues/993
Removes the old template parser completely. The new template parser was made
the default in 5.0.0-alpha+5
.
InjectionError.enableBetterErrors
was removed, it is now the (only) option
and is always enabled at development time. We are still waiting for fixes
to the view compiler to complete this feature:
https://github.com/dart-lang/angular/issues/434
Support for injecting services by an arbitrary object or literal is being
discontinued for compile-time injection. You'll receive a build exception;
fix by instead either providing by a Type
or OpaqueToken
/MultiToken
.
Testability now includes ComponentState updates. Due to prior use of animationFrame callback, testbed was not able to detect stable state.
Misspelled or otherwise erroneous annotations on classes now produce a more understandable error message, including the element that was annotated and the annotation that was not resolved.
bootstrapFactory
now injects an implementation of SlowComponentLoader
that always throws. This is to allow a migration path for common components
that still injet SlowComponentLoader
into apps that are using the new
bootstrap.
List<Element>
and List<HtmlElement>
for @ViewChildren
and
@ContentChildren
no longer require read: Element
, and the type is
correctly inferred the same as a single child is.
Missing a selector for a query (such as @ViewChild
) throws a better error.
MinimizeWhitespaceVisitor
.use_new_template_parser
flag. The old parser was removed.source_gen >= 0.7.6
.TokenReader
. In a future version of the compiler
these arguments will be completely removed.throwFailure
hit an NPE without a stack trace.NOTE: This used to be 1.0.1-alpha
, but has changed to be 2.0.0-alpha
due
to the fact that there are breaking changes in the previous dev releases. Future
development releases are moving to 2.x.x
, and a 1.0.1
will never be
released.
AbstractControl.find
now only accepts a String. To supply a list, use
AbstractControl.findPath
instead. Also, for find
or findPath
,
ControlArray
index is now calling int.parse
instead of expecting a raw
number.APP_BASE_HREF
is being renamed appBaseHref
.NOTE: We now require a dev SDK of >=2.0.0-dev.28.0
.
SafeInnerHtmlDirective
is no longer injectable.
The following types were never intended for external use and are no longer
exported by package:angular/security.dart
:
SafeHtmlImpl
SafeScriptImpl
SafeStyleImpl
SafeResourceUrlImpl
SafeUrlImpl
SafeValueImpl
To mark a value as safe, users should inject DomSanitizationService
and
invoke the corresponding bypassSecurityTrust*()
method, instead of
constructing these types directly.
Private types can't be used in template collection literals bound to an input. This is a consequence of fixing a cast warning that is soon to be an error caused by the code generated for change detecting collection literals in templates. See https://github.com/dart-lang/angular/issues/844 for more information.
OpaqueToken
and MultiToken
no longer have overridden operator==
and
hashCode
methods/fields. This wasn't supported, in practice, in most of
the DI systems, but any custom use of this class may have relied on this.
The view compiler hoists this.rootEl
as a final
local variable to help
Dart2JS know that its type stays the same and that repeated accesses to the
class instance variable is not needed. This should help remove interceptors
and reduce code-size a bit, especially for users of @HostBinding
or
@HostListener
(https://github.com/dart-lang/angular/issues/450).
Fixed a cast warning caused by untyped code generated for change detecting collection literals in templates.
The view compiler is now able to tell when exports
which are static reads
of the component class are immutable and String
type. This allows us to
optimize them when they are used in template bindings. See
https://github.com/dart-lang/angular/issues/995 for more information.
The following directives are no longer injectable:
NgControlStatus
RequiredValidator
MinLengthValidator
MaxLengthValidator
PatternValidator
Properly typed the generic parameter on subclasses of
AbstractControlDirective
. Now, NgControl.control
will return a
Control
, and ControlContainer.control
will return a ControlGroup
.
There may be some unnecessary casts that can now be cleaned up.
FormBuilder
instance methods group
, control
, and array
have been
removed. For FormBuilder.control
, just call new Control(value, validator)
directly. For FormBuilder.group
and FormBuilder.array
, use
the static methods FormBuilder.controlGroup
and
FormBuilder.controlArray
, respectively. FormBuilder
is no longer
Injectable
.
RouterOutlet
is no longer injectable.
Renamed Router.stream
to Router.onRouteActivated
. Router.stream
is now
deprecated and will be removed after next release.
RouterPath.toUrl()
no longer generates an incorrect URL with an extra '/'
when a parent route has an empty path.