Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat(compiler): support ScopeAware for decorators
Browse files Browse the repository at this point in the history
Even though decorators can safely inject scope (even after the proposed
context change), they might pick up ScopeAware from a base class shared
with components. Instead of manually calling super.scope = scope, we
support ScopeAware for decorators (similarly to Attach/DetachAware.)

There is no performance reduction on the tree benchmark.
  • Loading branch information
rkirov committed Sep 24, 2014
1 parent 97a87a1 commit 943d619
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 11 deletions.
21 changes: 11 additions & 10 deletions lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,28 +110,29 @@ class ScopeLocals implements Map {
}

/**
* When a [Component] or the root context class implements [ScopeAware] the scope setter will be
* called to set the [Scope] on this component.
* When a [Directive] or the root context class implements [ScopeAware] the scope
* setter will be called to set the [Scope] on this component.
*
* Typically classes implementing [ScopeAware] will declare a `Scope scope` property which will get
* initialized after the [Scope] is available. For this reason the `scope` property will not be
* initialized during the execution of the constructor - it will be immediately after.
* The order of calls is as follows:
* - [Component] instance is created.
* - [Scope] instance is created (taking [Component] instance as evaluation context).
* - if [Component] is [ScopeAware], set scope method is called with scope instance.
*
* However, if you need to execute some code as soon as the scope is available you should implement
* a `scope` setter:
* [ScopeAware] is guaranteed to be called before [AttachAware] or [DetachAware] methods.
*
* Example:
* @Component(...)
* class MyComponent implements ScopeAware {
* Watch watch;
*
* MyComponent(Dependency myDep) {
* // It is an error to add a Scope / RootScope argument to the ctor and will result in a DI
* // circular dependency error - the scope is never accessible in the class constructor
* // It is an error to add a Scope argument to the ctor and will result in a DI
* // circular dependency error - the scope has a dependency on the component instance.
* }
*
* void set scope(Scope scope) {
* // This setter gets called to initialize the scope
* watch = scope.rootScope.watch("expression", (v, p) => ...);
* watch = scope.watch("expression", (v, p) => ...);
* }
* }
*/
Expand Down
4 changes: 4 additions & 0 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ class ElementBinder {
_createAttrMappings(directive, scope, ref.mappings, nodeAttrs, tasks);
}

// Component is handled in BoundComponentFactories with the correct scope.
if (directive is ScopeAware && !(ref.annotation is Component)) {
directive.scope = scope;
}
if (directive is AttachAware) {
var taskId = (tasks != null) ? tasks.registerTask() : 0;
Watch watch;
Expand Down
23 changes: 22 additions & 1 deletion test/core_dom/compiler_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1065,12 +1065,26 @@ void main() {
expect(log.result()).toEqual('IncludeTransclude; SimpleTransclude');
}));

it('should call scope setter on ScopeAware components', async((TestBed _, Logger log) {
it('should call scope setter on ScopeAware components', async((TestBed _, Logger log, CompilerConfig config) {
var element = _.compile('<scope-aware-cmp></scope-aware-cmp>');

_.rootScope.apply();

expect(log.result()).toEqual('Scope set');

if (config.elementProbeEnabled) {
expect(ngInjector(element).get(ScopeAwareComponent).
scope.context['foo']).toEqual('bar');
}
}));

it('should call scope setter on ScopeAware decorators', async((TestBed _, Logger log) {
var element = _.compile('<div scope-aware-dec></div>');

_.rootScope.apply();

expect(log.result()).toEqual('Scope set');
expect(_.rootScope.context['foo']).toEqual('bar');
}));
});

Expand Down Expand Up @@ -1547,12 +1561,19 @@ class SameNameDecorator {
@Component(
selector: 'scope-aware-cmp'
)
@Decorator(
selector: '[scope-aware-dec]'
)
class ScopeAwareComponent implements ScopeAware {
Logger log;
Scope _scope;
ScopeAwareComponent(this.log) {}
void set scope(Scope scope) {
log('Scope set');
scope.context['foo'] = 'bar';
_scope = scope;
}
Scope get scope => _scope;
}

@Component(
Expand Down

0 comments on commit 943d619

Please sign in to comment.