Skip to content

Commit 9659dd1

Browse files
committed
feat(compiler): support ScopeAware for decorators
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.
1 parent caac098 commit 9659dd1

File tree

5 files changed

+24
-12
lines changed

5 files changed

+24
-12
lines changed

lib/core/scope.dart

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,28 +110,29 @@ class ScopeLocals implements Map {
110110
}
111111

112112
/**
113-
* When a [Component] or the root context class implements [ScopeAware] the scope setter will be
114-
* called to set the [Scope] on this component.
113+
* When a [Directive] or the root context class implements [ScopeAware] the scope
114+
* setter will be called to set the [Scope] on this component.
115115
*
116-
* Typically classes implementing [ScopeAware] will declare a `Scope scope` property which will get
117-
* initialized after the [Scope] is available. For this reason the `scope` property will not be
118-
* initialized during the execution of the constructor - it will be immediately after.
116+
* The order of calls is as follows:
117+
* - [Component] instance is created.
118+
* - [Scope] instance is created (taking [Component] instance as evaluation context).
119+
* - if [Component] is [ScopeAware], set scope method is called with scope instance.
119120
*
120-
* However, if you need to execute some code as soon as the scope is available you should implement
121-
* a `scope` setter:
121+
* [ScopeAware] is guaranteed to be called before [AttachAware] or [DetachAware] methods.
122122
*
123+
* Example:
123124
* @Component(...)
124125
* class MyComponent implements ScopeAware {
125126
* Watch watch;
126127
*
127128
* MyComponent(Dependency myDep) {
128-
* // It is an error to add a Scope / RootScope argument to the ctor and will result in a DI
129-
* // circular dependency error - the scope is never accessible in the class constructor
129+
* // It is an error to add a Scope argument to the ctor and will result in a DI
130+
* // circular dependency error - the scope has a dependency on the component instance.
130131
* }
131132
*
132133
* void set scope(Scope scope) {
133134
* // This setter gets called to initialize the scope
134-
* watch = scope.rootScope.watch("expression", (v, p) => ...);
135+
* watch = scope.watch("expression", (v, p) => ...);
135136
* }
136137
* }
137138
*/

lib/core_dom/element_binder.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ class ElementBinder {
226226
_createAttrMappings(directive, scope, ref.mappings, nodeAttrs, tasks);
227227
}
228228

229+
if (directive is ScopeAware) directive.scope = scope;
230+
229231
if (directive is AttachAware) {
230232
var taskId = (tasks != null) ? tasks.registerTask() : 0;
231233
Watch watch;

lib/core_dom/shadow_dom_component_factory.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory {
145145
}
146146

147147
var controller = shadowInjector.getByKey(_ref.typeKey);
148-
if (controller is ScopeAware) controller.scope = shadowScope;
149148
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
150149
shadowScope.context[_component.publishAs] = controller;
151150

lib/core_dom/transcluding_component_factory.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory {
114114

115115
var controller = childInjector.getByKey(_ref.typeKey);
116116
shadowScope.context[component.publishAs] = controller;
117-
if (controller is ScopeAware) controller.scope = shadowScope;
118117
BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
119118
return controller;
120119
};

test/core_dom/compiler_spec.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,14 @@ void main() {
10721072

10731073
expect(log.result()).toEqual('Scope set');
10741074
}));
1075+
1076+
it('should call scope setter on ScopeAware decorators', async((TestBed _, Logger log) {
1077+
var element = _.compile('<div scope-aware-dec></div>');
1078+
1079+
_.rootScope.apply();
1080+
1081+
expect(log.result()).toEqual('Scope set');
1082+
}));
10751083
});
10761084

10771085

@@ -1547,6 +1555,9 @@ class SameNameDecorator {
15471555
@Component(
15481556
selector: 'scope-aware-cmp'
15491557
)
1558+
@Decorator(
1559+
selector: '[scope-aware-dec]'
1560+
)
15501561
class ScopeAwareComponent implements ScopeAware {
15511562
Logger log;
15521563
ScopeAwareComponent(this.log) {}

0 commit comments

Comments
 (0)