Skip to content

Commit 60456c8

Browse files
committed
feat(ng-repeat): initial implementaion of ng-repeat.
- adds support for content bindings via '[]'. - directives module
1 parent 59d6d60 commit 60456c8

File tree

13 files changed

+388
-14
lines changed

13 files changed

+388
-14
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ node_modules
1717
pubspec.lock
1818
.c9
1919
.idea/
20+
*.swo
2021

2122

22-
/docs/bower_components/
23+
/docs/bower_components/

karma-dart.conf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module.exports = function(config) {
4646
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection/src',
4747
'/packages/reflection': 'http://localhost:9877/base/modules/reflection/src',
4848
'/packages/di': 'http://localhost:9877/base/modules/di/src',
49+
'/packages/directives': 'http://localhost:9877/base/modules/directives/src',
4950
'/packages/facade': 'http://localhost:9877/base/modules/facade/src',
5051
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib/src',
5152
},

modules/core/src/compiler/pipeline/element_binder_builder.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,16 @@ export class ElementBinderBuilder extends CompileStep {
118118
throw new BaseException('No element binding found for property '+elProp
119119
+' which is required by directive '+stringify(typeWithAnnotation.type));
120120
}
121+
var len = dirProp.length;
122+
var dirBindingName = dirProp;
123+
var isContentWatch = dirProp[len - 2] === '[' && dirProp[len - 1] === ']';
124+
if (isContentWatch) dirBindingName = dirProp.substring(0, len - 2);
121125
protoView.bindDirectiveProperty(
122126
directiveIndex++,
123127
expression,
124-
dirProp,
125-
reflector.setter(dirProp)
128+
dirBindingName,
129+
reflector.setter(dirBindingName),
130+
isContentWatch
126131
);
127132
});
128133
});

modules/core/src/compiler/view.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ export class ProtoView {
417417
directiveIndex:number,
418418
expression:AST,
419419
setterName:string,
420-
setter:SetterFn) {
420+
setter:SetterFn,
421+
isContentWatch: boolean) {
421422

422423
var expMemento = new DirectivePropertyMemento(
423424
this.elementBinders.length-1,
@@ -426,7 +427,7 @@ export class ProtoView {
426427
setter
427428
);
428429
var groupMemento = DirectivePropertyGroupMemento.get(expMemento);
429-
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, false);
430+
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, isContentWatch);
430431
}
431432

432433
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
@@ -500,7 +501,7 @@ class DirectivePropertyGroupMemento {
500501
var directiveIndex = memento._directiveIndex;
501502
var id = elementInjectorIndex * 100 + directiveIndex;
502503

503-
if (! MapWrapper.contains(_groups, id)) {
504+
if (!MapWrapper.contains(_groups, id)) {
504505
MapWrapper.set(_groups, id, new DirectivePropertyGroupMemento(elementInjectorIndex, directiveIndex));
505506
}
506507
return MapWrapper.get(_groups, id);

modules/core/src/compiler/viewport.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ export class ViewPort {
3737
dehydrate() {
3838
this.appInjector = null;
3939
this.hostElementInjector = null;
40-
for (var i = 0; i < this._views.length; i++) {
40+
this.clear();
41+
}
42+
43+
clear() {
44+
for (var i = this._views.length - 1; i >= 0; i--) {
4145
this.remove(i);
4246
}
4347
}

modules/core/test/compiler/view_spec.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ export function main() {
382382
var pv = new ProtoView(createElement('<div class="ng-binding"></div>'),
383383
new ProtoRecordRange());
384384
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
385-
pv.bindDirectiveProperty(0, parser.parseBinding('foo'), 'prop', reflector.setter('prop'));
385+
pv.bindDirectiveProperty(0, parser.parseBinding('foo'), 'prop', reflector.setter('prop'), false);
386386
createViewAndChangeDetector(pv);
387387

388388
ctx.foo = 'buz';
@@ -395,8 +395,8 @@ export function main() {
395395
new ProtoRecordRange());
396396

397397
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
398-
pv.bindDirectiveProperty( 0, parser.parseBinding('a'), 'a', reflector.setter('a'));
399-
pv.bindDirectiveProperty( 0, parser.parseBinding('b'), 'b', reflector.setter('b'));
398+
pv.bindDirectiveProperty( 0, parser.parseBinding('a'), 'a', reflector.setter('a'), false);
399+
pv.bindDirectiveProperty( 0, parser.parseBinding('b'), 'b', reflector.setter('b'), false);
400400
createViewAndChangeDetector(pv);
401401

402402
ctx.a = 100;
@@ -412,9 +412,10 @@ export function main() {
412412
new ProtoRecordRange());
413413

414414
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
415-
pv.bindDirectiveProperty( 0, parser.parseBinding('a').ast, 'a', reflector.setter('a'));
416-
pv.bindDirectiveProperty( 0, parser.parseBinding('b').ast, 'b', reflector.setter('b'));
417-
createView(pv);
415+
pv.bindDirectiveProperty( 0, parser.parseBinding('a').ast, 'a', reflector.setter('a'), false);
416+
pv.bindDirectiveProperty( 0, parser.parseBinding('b').ast, 'b', reflector.setter('b'), false);
417+
createViewAndChangeDetector(pv);
418+
418419
ctx.a = 0;
419420
ctx.b = 0;
420421
cd.detectChanges();

modules/directives/pubspec.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: directives
2+
environment:
3+
sdk: '>=1.4.0'
4+
dependencies:
5+
core:
6+
path: ../core
7+
change_detection:
8+
path: ../change_detection
9+
di:
10+
path: ../di
11+
facade:
12+
path: ../facade
13+
reflection:
14+
path: ../reflection
15+
dev_dependencies:
16+
test_lib:
17+
path: ../test_lib
18+
guinness: ">=0.1.16 <0.2.0"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import {describe, xit, it, expect, beforeEach, ddescribe, iit} from 'test_lib/test_lib';
2+
3+
import {Decorator, Component, Template} from 'core/annotations/annotations';
4+
import {OnChange} from 'core/compiler/interfaces';
5+
import {ViewPort} from 'core/compiler/viewport';
6+
import {View} from 'core/compiler/view';
7+
import {isPresent, isBlank} from 'facade/lang';
8+
import {ListWrapper, List} from 'facade/collection';
9+
10+
@Template({
11+
selector: '[ng-repeat]',
12+
bind: {
13+
'in': 'iterable[]'
14+
}
15+
})
16+
export class NgRepeat extends OnChange {
17+
viewPort: ViewPort;
18+
iterable;
19+
constructor(viewPort: ViewPort) {
20+
this.viewPort = viewPort;
21+
}
22+
onChange(changes) {
23+
var iteratorChanges = changes['iterable'];
24+
if (isBlank(iteratorChanges) || isBlank(iteratorChanges.currentValue)) {
25+
this.viewPort.clear();
26+
return;
27+
}
28+
29+
// TODO(rado): check if change detection can produce a change record that is
30+
// easier to consume than current.
31+
var recordViewTuples = [];
32+
iteratorChanges.currentValue.forEachRemovedItem(
33+
(removedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(removedRecord, null))
34+
);
35+
36+
iteratorChanges.currentValue.forEachMovedItem(
37+
(movedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(movedRecord, null))
38+
);
39+
40+
var insertTuples = NgRepeat.bulkRemove(recordViewTuples, this.viewPort);
41+
42+
iteratorChanges.currentValue.forEachAddedItem(
43+
(addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null))
44+
);
45+
46+
NgRepeat.bulkInsert(insertTuples, this.viewPort);
47+
48+
for (var i = 0; i < insertTuples.length; i++) {
49+
this.perViewChange(insertTuples[i].view, insertTuples[i].record);
50+
}
51+
}
52+
53+
perViewChange(view, record) {
54+
view.setLocal('ng-repeat', record.item);
55+
// Uncomment when binding is ready.
56+
// view.setLocal('index', record.item);
57+
}
58+
59+
static bulkRemove(tuples, viewPort) {
60+
tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
61+
var movedTuples = [];
62+
for (var i = tuples.length - 1; i >= 0; i--) {
63+
var tuple = tuples[i];
64+
var view = viewPort.remove(tuple.record.previousIndex);
65+
if (isPresent(tuple.record.currentIndex)) {
66+
tuple.view = view;
67+
ListWrapper.push(movedTuples, tuple);
68+
}
69+
}
70+
return movedTuples;
71+
}
72+
73+
static bulkInsert(tuples, viewPort) {
74+
tuples.sort((a, b) => a.record.currentIndex - b.record.currentIndex);
75+
for (var i = 0; i < tuples.length; i++) {
76+
var tuple = tuples[i];
77+
if (isPresent(tuple.view)) {
78+
viewPort.insert(tuple.view, tuple.record.currentIndex);
79+
} else {
80+
tuple.view = viewPort.create(tuple.record.currentIndex);
81+
}
82+
}
83+
return tuples;
84+
}
85+
}
86+
87+
class RecordViewTuple {
88+
view: View;
89+
record: any;
90+
constructor(record, view) {
91+
this.record = record;
92+
this.view = view;
93+
}
94+
}

0 commit comments

Comments
 (0)