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

Commit 443df96

Browse files
committed
refactor(NgAttachAware): wait until all bindings fire.
This changes the behavior in that it will wait for one time bindings to resolve before calling attach();
1 parent 1136cd8 commit 443df96

File tree

5 files changed

+67
-18
lines changed

5 files changed

+67
-18
lines changed

lib/core_dom/block_factory.dart

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,37 @@ class BlockFactory {
201201
shadowScope.context[(ref.annotation as NgComponent).publishAs] = controller;
202202
}
203203
if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
204+
var attachDelayStatus = controller is NgAttachAware ? [false] : null;
205+
checkAttachReady() {
206+
if (attachDelayStatus.reduce((a, b) => a && b)) {
207+
attachDelayStatus = null;
208+
controller.attach();
209+
}
210+
}
204211
for(var map in ref.mappings) {
205-
map(nodeAttrs, scope, controller, filters);
212+
var notify;
213+
if (attachDelayStatus != null) {
214+
var index = attachDelayStatus.length;
215+
attachDelayStatus.add(false);
216+
notify = () {
217+
if (attachDelayStatus != null) {
218+
attachDelayStatus[index] = true;
219+
checkAttachReady();
220+
}
221+
};
222+
} else {
223+
notify = () => null;
224+
}
225+
map(nodeAttrs, scope, controller, filters, notify);
206226
}
207-
if (controller is NgAttachAware) {
227+
if (attachDelayStatus != null) {
208228
Watch watch;
209229
watch = scope.watch(
210230
'1', // Cheat a bit.
211231
(_, __) {
212232
watch.remove();
213-
controller.attach();
233+
attachDelayStatus[0] = true;
234+
checkAttachReady();
214235
});
215236
}
216237
if (controller is NgDetachAware) {

lib/core_dom/common.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ List<dom.Node> cloneElements(elements) {
44
return elements.map((el) => el.clone(true)).toList();
55
}
66

7-
typedef ApplyMapping(NodeAttrs attrs, Scope scope, Object dst, FilterMap filters);
7+
typedef ApplyMapping(NodeAttrs attrs, Scope scope, Object dst,
8+
FilterMap filters, notify());
89

910
class DirectiveRef {
1011
final dom.Node element;

lib/core_dom/compiler.dart

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,15 @@ class Compiler {
147147
ApplyMapping mappingFn;
148148
switch (mode) {
149149
case '@':
150-
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters) {
151-
attrs.observe(attrName, (value) => dstPathFn.assign(controller, value));
150+
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters, notify()) {
151+
attrs.observe(attrName, (value) {
152+
dstPathFn.assign(controller, value);
153+
notify();
154+
});
152155
};
153156
break;
154157
case '<=>':
155-
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters) {
158+
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters, notify()) {
156159
if (attrs[attrName] == null) return;
157160
String expression = attrs[attrName];
158161
Expression expressionFn = _parser(expression);
@@ -164,7 +167,9 @@ class Compiler {
164167
if (!blockInbound) {
165168
blockOutbound = true;
166169
scope.rootScope.runAsync(() => blockOutbound = false);
167-
return dstPathFn.assign(controller, inboundValue);
170+
var value = dstPathFn.assign(controller, inboundValue);
171+
notify();
172+
return value;
168173
}
169174
},
170175
filters: filters
@@ -177,6 +182,7 @@ class Compiler {
177182
blockInbound = true;
178183
scope.rootScope.runAsync(() => blockInbound = false);
179184
expressionFn.assign(scope.context, outboundValue);
185+
notify();
180186
}
181187
},
182188
context: controller,
@@ -186,17 +192,20 @@ class Compiler {
186192
};
187193
break;
188194
case '=>':
189-
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters) {
195+
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters, notify()) {
190196
if (attrs[attrName] == null) return;
191197
Expression attrExprFn = _parser(attrs[attrName]);
192198
var shadowValue = null;
193199
scope.watch(attrs[attrName],
194-
(v, _) => dstPathFn.assign(controller, shadowValue = v),
200+
(v, _) {
201+
dstPathFn.assign(controller, shadowValue = v);
202+
notify();
203+
},
195204
filters: filters);
196205
};
197206
break;
198207
case '=>!':
199-
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters) {
208+
mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMap filters, notify()) {
200209
if (attrs[attrName] == null) return;
201210
Expression attrExprFn = _parser(attrs[attrName]);
202211
var watch;
@@ -205,14 +214,16 @@ class Compiler {
205214
(value, _) {
206215
if (dstPathFn.assign(controller, value) != null) {
207216
watch.remove();
217+
notify();
208218
}
209219
},
210220
filters: filters);
211221
};
212222
break;
213223
case '&':
214-
mappingFn = (NodeAttrs attrs, Scope scope, Object dst, FilterMap filters) {
224+
mappingFn = (NodeAttrs attrs, Scope scope, Object dst, FilterMap filters, notify()) {
215225
dstPathFn.assign(dst, _parser(attrs[attrName]).bind(scope.context, ScopeLocals.wrapper));
226+
notify();
216227
};
217228
break;
218229
}

test/core_dom/compiler_spec.dart

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,14 +453,28 @@ main() => describe('dte.compiler', () {
453453
var scope = rootScope.createChild({});
454454
scope.context['isReady'] = 'ready';
455455
scope.context['logger'] = logger;
456-
var element = $('<attach-detach attr-value="{{isReady}}" expr-value="isReady">{{logger("inner")}}</attach-detach>');
456+
scope.context['once'] = null;
457+
var element = $('<attach-detach attr-value="{{isReady}}" expr-value="isReady" once-value="once">{{logger("inner")}}</attach-detach>');
457458
$compile(element, directives)(injector.createChild([new Module()..value(Scope, scope)]), element);
458459
expect(logger).toEqual(['new']);
459460

460461
expect(logger).toEqual(['new']);
461462

462463
rootScope.apply();
463-
var expected = ['new', 'attach:@ready; =>ready', 'inner'];
464+
var expected = ['new', 'inner'];
465+
assert((() {
466+
// there is an assertion in flush which double checks that
467+
// flushes do not change model. This assertion creates one
468+
// more 'inner';
469+
expected.add('inner');
470+
return true;
471+
})());
472+
expect(logger).toEqual(expected);
473+
logger.clear();
474+
475+
scope.context['once'] = '123';
476+
rootScope.apply();
477+
expected = ['attach:@ready; =>ready; =>!123', 'inner'];
464478
assert((() {
465479
// there is an assertion in flush which double checks that
466480
// flushes do not change model. This assertion creates one
@@ -771,21 +785,23 @@ class LogComponent {
771785
templateUrl: 'some/template.url',
772786
map: const {
773787
'attr-value': '@attrValue',
774-
'expr-value': '<=>exprValue'
788+
'expr-value': '<=>exprValue',
789+
'once-value': '=>!onceValue'
775790
}
776791
)
777792
class AttachDetachComponent implements NgAttachAware, NgDetachAware, NgShadowRootAware {
778793
Logger logger;
779794
Scope scope;
780795
String attrValue = 'too early';
781796
String exprValue = 'too early';
797+
String onceValue = 'too early';
782798

783799
AttachDetachComponent(Logger this.logger, TemplateLoader templateLoader, Scope this.scope) {
784800
logger('new');
785801
templateLoader.template.then((_) => logger('templateLoaded'));
786802
}
787803

788-
attach() => logger('attach:@$attrValue; =>$exprValue');
804+
attach() => logger('attach:@$attrValue; =>$exprValue; =>!$onceValue');
789805
detach() => logger('detach');
790806
onShadowRoot(shadowRoot) {
791807
scope.rootScope.context['shadowRoot'] = shadowRoot;

test/directive/ng_model_spec.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,12 +577,12 @@ describe('ng-model', () {
577577

578578
// NOTE(deboer): This test passes on Dartium, but fails in the content_shell.
579579
// The Dart team is looking into this bug.
580-
xit('should write to input only if value is different', inject((Injector i) {
580+
xit('should write to input only if value is different', inject((Injector i, AstParser parser) {
581581
var scope = _.rootScope;
582582
var element = new dom.TextAreaElement();
583583
NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
584584
nodeAttrs['ng-model'] = 'model';
585-
var model = new NgModel(scope, new NodeAttrs(new DivElement()), element, null, null);
585+
var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
586586
dom.querySelector('body').append(element);
587587
var input = new InputTextLikeDirective(element, model, scope);
588588

0 commit comments

Comments
 (0)