diff --git a/modules/change_detection/src/dynamic_change_detector.js b/modules/change_detection/src/dynamic_change_detector.js index 620d11f69d..b1988bc6a7 100644 --- a/modules/change_detection/src/dynamic_change_detector.js +++ b/modules/change_detection/src/dynamic_change_detector.js @@ -21,6 +21,8 @@ import { import {ChangeDetector, ChangeRecord, ChangeDispatcher} from './interfaces'; import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './exceptions'; +var _uninitialized = new Object(); + class SimpleChange { previousValue:any; currentValue:any; @@ -43,6 +45,7 @@ export class DynamicChangeDetector extends ChangeDetector { this.dispatcher = dispatcher; this.formatters = formatters; this.values = ListWrapper.createFixedSize(protoRecords.length + 1); + ListWrapper.fill(this.values, _uninitialized); this.protos = protoRecords; this.children = []; @@ -123,10 +126,9 @@ export class DynamicChangeDetector extends ChangeDetector { var prevValue = this._readSelf(proto); var currValue = this._calculateCurrValue(proto); - if (! isSame(prevValue, currValue)) { + if (!isSame(prevValue, currValue)) { this._writeSelf(proto, currValue); - return new SimpleChange(prevValue, currValue); - + return new SimpleChange(prevValue === _uninitialized ? null : prevValue, currValue); } else { return null; } @@ -174,7 +176,7 @@ export class DynamicChangeDetector extends ChangeDetector { var self = this._readSelf(proto); var context = this._readContext(proto); - if (isBlank(self)) { + if (isBlank(self) || self === _uninitialized) { if (ArrayChanges.supports(context)) { self = new ArrayChanges(); } else if (KeyValueChanges.supports(context)) { diff --git a/modules/change_detection/test/change_detection_spec.js b/modules/change_detection/test/change_detection_spec.js index d224544cb1..cbfae7f09e 100644 --- a/modules/change_detection/test/change_detection_spec.js +++ b/modules/change_detection/test/change_detection_spec.js @@ -57,6 +57,16 @@ export function main() { expect(dispatcher.log).toEqual(['name=Misko']); }); + it('should report all changes on the first run including uninitialized values', () => { + var uninit = new Uninitialized(); + var c = createChangeDetector('value', 'value', uninit); + var cd = c["changeDetector"]; + var dispatcher = c["dispatcher"]; + + cd.detectChanges(); + expect(dispatcher.log).toEqual(['value=null']); + }); + it("should support literals", () => { expect(executeWatch('const', '10')).toEqual(['const=10']); expect(executeWatch('const', '"str"')).toEqual(['const=str']); @@ -237,10 +247,10 @@ export function main() { pcd.addAst(ast('invalidProp', 'someComponent'), "a", 1); var cd = pcd.instantiate(new TestDispatcher(), null); + cd.setContext(null); try { cd.detectChanges(); - throw new BaseException("fail"); } catch (e) { expect(e).toBeAnInstanceOf(ChangeDetectionError); @@ -430,6 +440,10 @@ class Address { } } +class Uninitialized { + value:any; +} + class TestData { a; @@ -474,4 +488,4 @@ class TestDispatcher extends ChangeDispatcher { _asString(value) { return (isBlank(value) ? 'null' : value.toString()); } -} \ No newline at end of file +} diff --git a/modules/facade/src/collection.dart b/modules/facade/src/collection.dart index 4557fedf41..23080448ed 100644 --- a/modules/facade/src/collection.dart +++ b/modules/facade/src/collection.dart @@ -2,6 +2,7 @@ library facade.collection; import 'dart:collection' show HashMap, IterableBase, Iterator; export 'dart:core' show Map, List, Set; +import 'dart:math' show max, min; class MapIterator extends Iterator { Iterator _iterator; @@ -105,6 +106,18 @@ class ListWrapper { static void clear(List l) { l.clear(); } static String join(List l, String s) => l.join(s); static bool isEmpty(list) => list.isEmpty; + static void fill(List l, value, [int start = 0, int end]) { + // JS semantics + // see https://github.com/google/traceur-compiler/blob/81880cd3f17bac7de90a4cd0339e9f1a9f61d24c/src/runtime/polyfills/Array.js#L94 + int len = l.length; + start = start < 0 ? max(len + start, 0) : min(start, len); + if (end == null) { + end = len; + } else { + end = end < 0 ? max(len + end, 0) : min(end, len); + } + l.fillRange(start, end, value); + } } bool isListLikeIterable(obj) => obj is Iterable; diff --git a/modules/facade/src/collection.es6 b/modules/facade/src/collection.es6 index 49ebc0b088..e5804f6e50 100644 --- a/modules/facade/src/collection.es6 +++ b/modules/facade/src/collection.es6 @@ -166,6 +166,9 @@ export class ListWrapper { static isEmpty(list) { return list.length == 0; } + static fill(list:List, value, start:int = 0, end:int = undefined) { + list.fill(value, start, end); + } } export function isListLikeIterable(obj):boolean {