-
Notifications
You must be signed in to change notification settings - Fork 231
Transformer
While developing an Angular 2 Dart app, the framework uses reflection via dart:mirrors to enable dependency injection and template binding among other features. This is convenient, but comes at the cost of slower execution and greatly increased deployed code size.
The Transformer is designed to analyze your Angular 2 Dart app and generate static code to replace the use of dart:mirrors
, increasing speed and reducing code size. See the Design Doc for more information.
See Assets and Transformers for general information on transformers.
- Declare in your
pubspec.yaml
file that your application uses theangular2
transformer - Provide the transformer with one or more
entry_points
- (Optional) Provide one or more
custom_annotations
entry_points
are the file(s) where you call Angular 2's bootstrap
method.
Due to the way these files are processed, you should pass bootstrap
the Type
literal of your root Component
. Indirection will likely cause the transformer to generate incorrect code.
Good:
helper.dart:
void setup() {
// Some initialization
}
entry_point.dart:
void main() {
setup();
bootstrap(MyComponent);
}
or
entry_point.dart:
@AngularEntrypoint()
void main() {
group('Test an angular component in compiled mode', () {
...
});
}
Bad:
helper.dart:
void setupThenBootstrap(Type t) {
// Some initialization
bootstrap(t);
}
entry_point.dart:
void main() {
setupThenBootstrap(MyComponent);
}
The new Angular 2 template compiler generates dependency injection code. This means that all the identifiers used in DI have to be collected by the Angular 2 transformer. As a result, the libraries containing these identifiers have to be transformed by the Angular 2 transformer. So if you see
Missing identifier "SomeClass" needed by "SomeComponent" from metadata map.
just enable the Angular 2 transformer for the library that contains SomeClass, and the issue will be fixed.
Currently, the following types of libraries are not transformed:
- core libraries (e.g., dart:html)
- libraries generated using protobufs
To be able to use symbols from these libraries, list identifier/asset pairs after the resolved_identifiers
transformer option. For example:
- angular2:
resolved_identifiers:
Window: 'dart:html'
custom_annotations
are custom classes that you define which extend the built in Angular annotation classes (such as @Component
and @Injectable
). In order for the transformer to discover these you must list them in your transformer, below is an example:
transformers:
- angular2:
custom_annotations:
- name: MyComponent # The name of the class.
import: 'package:my_package/my_component.dart' # The import that defines the class.
superClass: Component # The class that this class extends.
A list of Directive
s which are considered to be present in every Component
's list of dependencies. That is, any Component
in the package may use any Directive
s in the list without explicitly declaring it.
The format of this parameter should be
package:<package>/path/to/library#symbol_to_use
Example:
in the package named "my_directives", lib/common_directives.dart contains:
...
const commonDirectives = const [CommonDirective1, CommonDirective2];
...
In your pubspec.yaml for package "my_app", you declare:
angular2:
platform_directives: 'package:my_directives/common_directives.dart#commonDirectives'
Now all Component
s in the package "my_app" will behave as if they declared CommonDirective1
and CommonDirective2
as dependencies in their directives
lists.
A list of Pipe
s which are considered to be present in every Component
's list of dependencies. That is, any Component
in the package may use any Pipe
s in the list without explicitly declaring it.
The format of this parameter should be
package:<package>/path/to/library#symbol_to_use
Example:
in the package named "my_pipes", lib/common_pipes.dart contains:
...
const commonPipes = const [CommonPipe1, CommonPipe2];
...
In your pubspec.yaml for package "my_app", you declare:
angular2:
platform_pipes: 'package:my_pipes/common_pipes.dart#commonPipes'
Now all Component
s in the package "my_app" will behave as if they declared CommonPipe1
and CommonPipe2
as dependencies in their pipes
lists.
These generally do not need to be modified.
Whether to use the dart_style
package to format code generated by the transformers. Existing app code that is updated will never be formatted, as doing so may invalidate generated source maps and/or other tool output.
Whether the change detection code should echo property values as attributes on DOM elements.
If you depend on @Injectable
objects defined in other packages, those packages must also declare that they use the angular2
transformer (or the targeted codegen
transformer). All Angular 2 @Directive
s and @Component
s are @Injectable
objects.
Omitting the transformer in your dependencies results in errors like the following when running transformed code:
Cannot find reflection information on <@Injectable Type>
Your dependencies do not need to declare any entry_points
.
You can now run pub serve
or pub build
to see the transformer in action. In short, it will:
- Create
.ng_template.dart
files defininginitReflector
methods - Remove the framework's use of
dart:mirrors
- Inject code to call the generated
initReflector
methods
See the Design Doc for details.
See Dependencies
You must explicitly declare that your class implements
lifecycle interfaces for the corresponding lifecycle hooks to be called, even if your class extends another class and does not override the lifecycle method!
class BaseClass implements OnInit {
@override ngOnInit() => doImportantSetup();
}
class MyClass extends BaseClass /* Oh noes! Does not implement OnInit */ {
// BaseClass#ngOnInit is never called!
}
See this doc and https://github.com/angular/angular/issues/6781 for discussion.
You can add these parameters alongside entry_points
in your pubspec.yaml
file.
-
mirror_mode
, defaults tonone
.-
debug
: Allow reflective access, but log a message if it is used -
none
: Deny reflective access,throw
if it is used -
verbose
: Allow reflective access, log a stack trace if it is used
-
-
init_reflector
, boolean, defaults totrue
. Whether to create calls to our generatedinitReflector
code.
Using mirror_mode: debug
or verbose
allows you to run your application with reflective access but see where it is used. When filing a bug against the transformer relating to missing static registration code, it helps to include the information printed by mirror_mode: verbose
.
Directive aliases allow you to give a name to a list of directives that are commonly used together. For example:
const formDirectives = const [NgControlName, NgControlGroup, ...];
Then, they can be used where the list of directives was expected:
@View(...
directives: const [ formDirectives, NgFor ])
class MyComponent {}
The Dart transformer supports a restricted syntax for directive aliases:
- The value must be a const list. This is because the value will be used within annotations.
- They must be defined as a top-level const variable. In particular, static fields are not supported.
- All entries in the list must be un-prefixed type literals. Support for prefixes is possible, but not implemented (see issue #3232).
- The code for the transformer currently resides in modules_dart/transform.
- Each phase of the transformer has its own subdirectory in
lib/src/transform
.