5.0.0-beta
angular
5.0.0-beta
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:
- Dartium is no longer supported. Instead, use the new DartDevCompiler
- Pub transformers are no longer used. Instead, use the new webdev
CLI, or, for advanced users, the build_runner CLI.
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
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).
New features
-
Provider(andprovide) are soft deprecated, and in their place are four
new classes with more precise type signatures. Additionally,Providernow
supports an optional type argument<T>, making itProvider<T>.-
ValueProvider(Type, T)andValueProvider.forToken(OpaqueToken<T>, T)
instead ofProvider(typeOrToken, useValue: ...). -
FactoryProvier(Type, T)andFactoryProvider.forToken(OpaqueToken<T>, T)
instead ofProvider(typeOrToken, useFactory: ...). -
ClassProvider(Type, useClass: T)and
ClassProvider.forToken(OpaqueToken<T>, useClass: T)instead of
Provider(typeOrToken, useClass: ...)or an implicitType. -
ExistingProvider(Type, T)and
ExistingProvider.forToken(OpaqueToken<T>, T)instead of
Provider(typeOrToken, useExisting: ...).
-
-
OpaqueTokenis now much more useful. Previously, it could be used to define
a custom, non-Typeto refer to something to be injected; commonly instead of
types likeString. For example, you might use anOpaqueTokento 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,
OpaqueTokenadds an optional type argument, makingOpaqueToken<T>.
The type argument,T, should be used to refer to theTypeof the object
this token should be bound to:const downloadUrl = OpaqueToken<String>('downloadUrl');
Coupled with the new named
Providerclasses and their.forTokennamed
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 extendsOpaqueToken<List<T>>.
This is an idiomatic replacement for the now deprecatedmulti: true
argument to theProviderconstructor: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) toextendeitherOpaqueTokenorMultiTokento
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
@Injectin the future, and let
you write less to inject tokens. So,OpaqueTokenandMultiToken
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
Injectorinstance with an optional parent. -
A new annotation,
@GenerateInjector, was added. It is now posibble to
generate, at compile-time, a standaloneInjectorFactorymethod 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;
-
Modulehas been added as a new, more-typed way to encapsulate a collection
ofProviderinstances. This is an optional feature to use instead of
nestedconstlists 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
Modulesyntax: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.Modulespecifically outlines that order is significant, and
thatincludeis processed beforeprovide. -
Modulerejects using aTypeimplicitly as aClassProvider. This
removes additional ambiguity around supportingList<dynamic>, and while
more verbose, should lead to more correct use. -
Moduletends to be more understandable by users of other dependency
injection systems such as Guice or Dagger, and reads better than aconst
List(which is a very Dart-only idiom).
NOTE: It is also possible to use
Modulein@GenerateInjector:@GenerateInjector.fromModules([ commonModule, ]) final InjectorFactory exampleFromModule = ng.exampleFromModule$Injector;
NOTE: It is also possible to use
ModuleinReflectiveInjector:// Using ReflectiveInjector is strongly not recommended for new code // due to adverse effects on code-size and runtime performance. final injector = ReflectiveInjector.resolveAndCreate([ commonModule, ]);
-
Breaking changes
-
OpaqueTokenno longer overridesoperator==orhashCode. In practice this
should have no effect for most programs, but it does mean that effectively
that onlyconstinstances ofOpaqueToken(orMultiToken) are valid. -
It is no longer valid to provide a token type of anything other than
Typeor
anOpaqueToken(orMultiToken). In the past anything from aribtrary
literals (such as a string -'iAmAToken') or a customconstinstance of a
class were supported. -
For defining whether a componenr or directive should provide itself for
injection,Visibility.nonehas been renamedVisibility.localto make it
more clear that it is accessable locally (withinprovidersfor example). -
Classes annotated with
@Componentor@Directiveare no longer treated like
services annotated with@Injectable, and not accessible (by default) to
ReflectiveInjector.@Injectablecan always be added to these classes in
order to return to the old behavior.
Bug fixes
-
Fixed a bug where calling
geton anInjectorinjected in the context of an
@Componentor@Directive-annotated class and passing a second argument
always returnednull(instead of that second argument) if the token was not
found. -
Setting
@Component(visibility: Visibility.none) no longer applies to
providers, if any. Note thatVisibility.nonewas always renamed
Visibility.localin breaking changes above. -
Fixed a bug where
Provider(SomeType)was not parsed correctly as an implicit
use ofProvider(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 dependencyY. We now emit the correct message.
Other improvements
-
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.
Components and Templates
New features
-
NgTemplateOutletaddedngTemplateOutletContextfor setting local
variables in an embedded view. These variables are assignable to template
input variables declared usinglet, which can be bound within the template.
See theNgTemplateOutletdocumentation for examples. -
Added a lifecycle event
AfterChanges, which is similar toOnChanges, 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
ElementorHtmlElementtypes from
content or view children instead ofElementRef(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>andList<HtmlElement>for@ViewChildren
and@ContentChildrenno longer requireread: 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:
ExampleComponentas 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 theconst ['$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
fromis 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
.iffor attribute bindings, both in a template and
in a@HostBinding(). Accepts an expression of typebool, and adds an
attribute iftrue, and removes it iffalseand closes
#1058:<!-- These are identical --> <button [attr.disabled]="isDisabled ? '' : null"></button> <button [attr.disabled.if]="isDisabled"></button>
Breaking changes
-
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
ngNonBindablein the new template syntax. -
The fields
inputs:,outputs:, andhost: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 nowtrue. Many
improvements were put into the whitespace optimziation in order to make the
results easier to understand and work around. -
<AsyncPipe>.transformno 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
transformmethod 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
@deferredno longer supports the legacy bootstrap processes. You must
userunApp(orrunAppAsync) to bootstrap the application without relying
oninitReflector(). -
<ComponentRef>.componentTypealways throwsUnsupportedError, 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. -
QueryListfor@ViewChildrenand@ContentChildrenhas been removed, in
favor of just a plainListthat 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; }
-
EventEmitterwas removed in favor usingStreamandStreamController. -
COMMON_DIRECTIVESwas renamedcommonDirectives. -
CORE_DIRECTIVESwas renamedcoreDirectives. -
COMMON_PIPESwas renamedcommonPipes. -
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 #844 for more
information. -
SafeInnerHtmlDirectiveis no longer injectable. -
The following types were never intended for external use and are no longer
exported bypackage:angular/security.dart:SafeHtmlImplSafeScriptImplSafeStyleImplSafeResourceUrlImplSafeUrlImplSafeValueImpl
To mark a value as safe, users should inject
DomSanitizationServiceand
invoke the correspondingbypassSecurityTrust*()method, instead of
constructing these types directly. -
ComponentResolverwas removed, andSlowComponentLoaderwas deprecated. -
Methods in lifecycle hooks have
voidreturn type. This is breaking change
if the override doesn't specify return type and usesreturnwithout any
value. To fix add avoidorFuture<void>return type to the override:class MyComp implements OnInit { @override void ngOnInit() { // ... } }
-
Removed the rarely used
templateattribute 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
DoCheckandOnChanges
lifecycle interfaces.DoCheckwill never fill in values for theMapin
OnChanges, so this compile-error helps avoid bugs and directs the user to
useDoCheckandAfterChangesinstead. -
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).
Bug fixes
-
Fixed a bug where an
@deferredcomponents 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 theExceptionHandler. -
Fixed a bug where
DatePipedidn't formatmillisecondsSinceEpochin the
local time zone (consistent with how it formatsDateTime). -
Testability now includes
ComponentStateupdates. Due to prior use of
animationFramecallback,NgTestBedwas 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 alwaysdynamic), but the compiler no longer crashes.
Other improvements
-
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
ComponentFactoryandComponentRefnow have a generic type parameter
<T>, which is properly reified whereTis the type of the component class. -
The
$implicit(iterable) value in*ngForis now properly typed whenever
possible. It was previously always typed asdynamic, which caused dynamic lookups/calls at runtime, and hid compilation errors. -
The type of
<EmbeddedViewRef>.rootNodesand<ViewRefImpl>.rootNodeshas
been tightened fromList<dynamic>toList<Node>(whereNodeis from
dart:html). -
A combination of compile errors and warnings are produced when it seem that
template,templateUrl,style, orstyleUrlsare 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
*ngIfusages 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 asString). This allows us to
optimize the generated code.
Application Bootstrap
New features
-
The process for starting your AngularDart application changed significantly:
-
For most applications, we recommend now strongly recommend using the new
runAppfunction. Instead of starting your application by passing the
Typeof an@Component-annotatedclass, 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
createInjectorparameter, and
pass a generatedInjectorFactoryfor 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
runAppand previous bootstrapping code is
the lack of theinitReflector()method or call, which is no longer
needed. That means usingrunAppdisables the use of
SlowComponentLoaderandReflectiveInjector, 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:
initReflectorandrunAppLegacydisables tree-shaking on
any class annotated with@Componentor@Injectable. We strongly
recommend migrating to therunApppattern.
-
Breaking changes
-
The top-level function
bootstrapwas deleted. This function always threw a
runtime exception since5.0.0-alpha+5, and was a relic of when a code
transformer rewrote it automatically asbootstrapStatic. -
Dropped support for
@AngularEntrypointand rewriting entrypoints to
automatically useinitReflector()andbootstrapStatic. This is no longer
supported in the new build system. -
RenderComponentTypeis no longer part of the public API. -
<ApplicationRef>.componentFactories,.componentTypes,.zone, and
.registerBootstrapListenerwere removed; these were used internally by the
legacy router and not intended to be part of the public API. -
PLATFORM_INITIALIZERSwas removed. -
APP_INITIALIZERwas removed. A similar functionality can be accomplished
using therunAppAsyncorrunAppLegacyAsyncfunctions with the
beforeComponentCreatedcallback. -
PlatformRefandPlatformRefImplwere removed.
Misc
Breaking changes
-
<NgZone>.onStablehas been renamed toonTurnDone. -
<NgZone>.onUnstablehas been renamed toonTurnStart. -
The
contextparameter was removed from<TemplateRef>.createEmbeddedView. -
The following relatively unused fields and functions were removed:
APPLICATION_COMMON_PROVIDERSBROWSER_APP_COMMON_PROVIDERSBROWSER_APP_PROVIDERSPACKAGE_ROOT_URLErrorHandlingFnUrlResolverWrappedTimerWrappedValueZeroArgFunctionappIdRandomProviderFactorycoreBootstrapcoreLoadAndBootstrapcreateNgZonecreatePlatformdisposePlatformgetPlatform
-
Running within the
NgZonewill no longer cause addtional turns to occur
within it's parent's zone.<NgZone>.run()will now run inside the parent
zone'srun()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 (fromDart2JS)
going forward to emit debug-only conditional code.
Bug fixes
- We longer invoke
<ExceptionHandler>.callwith anullexception.
Other improvements
- 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.
Breaking changes
-
Removed
SafeScriptand its associated APIs. There was no path through the
compiler that made use of this type. -
Removed the
sanitize()method fromSanitizationService. This method was
entirely unused. Use the more specific methods such assanitizeHtml()and
sanitizeUrl()instead. -
Generated
ComponentFactoryinstances are no longer functionallyconst.
This is to prevent issues where users attempt to use generated component
factories in their ownconstcontexts, which was known to cause problems
in some build systems. -
MultiToken<T>now extendsOpaqueToken<List<T>>. This should have no real
affect on most programs, unless you manually typed yourMultiTokensuch as
usPresidents = const MultiToken<List<String>>(). This will allow future
features for theInjectorinterface:
#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).
New features
-
ReflectiveInjector.resolveStaticAndCreatewas added as an experimental
API (subject to breaking change at any time). This is primarily for adopting
runAppincrementally in existing large code-bases that use
ReflectiveInjector. See #1426
for details. -
Support for the suffix
.iffor attribute bindings, both in a template and
in a@HostBinding(). Accepts an expression of typebool, and adds an
attribute iftrue, and removes it iffalseand closes
#1058:<!-- These are identical --> <button [attr.disabled]="isDisabled ? '' : null"></button> <button [attr.disabled.if]="isDisabled"></button>
Bug fixes
-
The generated
.template.dartcode now properly subtypesAppView<C>where
Cis the annotated@Componentclass. This avoids implicit down-casts in
the framework. -
Fixed a bug where the compiler crashed if an injectable token contained the
typevoid(directly or in some nested type likeList<void>).
angular_compiler
0.4.0-alpha+16
- Added support for
voidandNulltypes to appear in tokens.
angular_forms
2.0.0-beta
- Maintanence release to bring into sync with
angular 5.0.0-beta.
angular_router
2.0.0-alpha+15
Breaking changes
- Removed component instance parameter from
RouterHook.canNavigate().
angular_test
2.0.0-beta
NgZoneStabilizerwaits for the longest pending timer duringupdate().