Skip to content

Commit

Permalink
feat(ng-repeat): initial implementaion of ng-repeat.
Browse files Browse the repository at this point in the history
- adds support for content bindings via '[]'.
- directives module
  • Loading branch information
rkirov committed Dec 11, 2014
1 parent 59d6d60 commit 60456c8
Show file tree
Hide file tree
Showing 13 changed files with 388 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ node_modules
pubspec.lock
.c9
.idea/
*.swo


/docs/bower_components/
/docs/bower_components/
1 change: 1 addition & 0 deletions karma-dart.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = function(config) {
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection/src',
'/packages/reflection': 'http://localhost:9877/base/modules/reflection/src',
'/packages/di': 'http://localhost:9877/base/modules/di/src',
'/packages/directives': 'http://localhost:9877/base/modules/directives/src',
'/packages/facade': 'http://localhost:9877/base/modules/facade/src',
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib/src',
},
Expand Down
9 changes: 7 additions & 2 deletions modules/core/src/compiler/pipeline/element_binder_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,16 @@ export class ElementBinderBuilder extends CompileStep {
throw new BaseException('No element binding found for property '+elProp
+' which is required by directive '+stringify(typeWithAnnotation.type));
}
var len = dirProp.length;
var dirBindingName = dirProp;
var isContentWatch = dirProp[len - 2] === '[' && dirProp[len - 1] === ']';
if (isContentWatch) dirBindingName = dirProp.substring(0, len - 2);
protoView.bindDirectiveProperty(
directiveIndex++,
expression,
dirProp,
reflector.setter(dirProp)
dirBindingName,
reflector.setter(dirBindingName),
isContentWatch
);
});
});
Expand Down
7 changes: 4 additions & 3 deletions modules/core/src/compiler/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ export class ProtoView {
directiveIndex:number,
expression:AST,
setterName:string,
setter:SetterFn) {
setter:SetterFn,
isContentWatch: boolean) {

var expMemento = new DirectivePropertyMemento(
this.elementBinders.length-1,
Expand All @@ -426,7 +427,7 @@ export class ProtoView {
setter
);
var groupMemento = DirectivePropertyGroupMemento.get(expMemento);
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, false);
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, isContentWatch);
}

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

if (! MapWrapper.contains(_groups, id)) {
if (!MapWrapper.contains(_groups, id)) {
MapWrapper.set(_groups, id, new DirectivePropertyGroupMemento(elementInjectorIndex, directiveIndex));
}
return MapWrapper.get(_groups, id);
Expand Down
6 changes: 5 additions & 1 deletion modules/core/src/compiler/viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export class ViewPort {
dehydrate() {
this.appInjector = null;
this.hostElementInjector = null;
for (var i = 0; i < this._views.length; i++) {
this.clear();
}

clear() {
for (var i = this._views.length - 1; i >= 0; i--) {
this.remove(i);
}
}
Expand Down
13 changes: 7 additions & 6 deletions modules/core/test/compiler/view_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ export function main() {
var pv = new ProtoView(createElement('<div class="ng-binding"></div>'),
new ProtoRecordRange());
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
pv.bindDirectiveProperty(0, parser.parseBinding('foo'), 'prop', reflector.setter('prop'));
pv.bindDirectiveProperty(0, parser.parseBinding('foo'), 'prop', reflector.setter('prop'), false);
createViewAndChangeDetector(pv);

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

pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a'), 'a', reflector.setter('a'));
pv.bindDirectiveProperty( 0, parser.parseBinding('b'), 'b', reflector.setter('b'));
pv.bindDirectiveProperty( 0, parser.parseBinding('a'), 'a', reflector.setter('a'), false);
pv.bindDirectiveProperty( 0, parser.parseBinding('b'), 'b', reflector.setter('b'), false);
createViewAndChangeDetector(pv);

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

pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a').ast, 'a', reflector.setter('a'));
pv.bindDirectiveProperty( 0, parser.parseBinding('b').ast, 'b', reflector.setter('b'));
createView(pv);
pv.bindDirectiveProperty( 0, parser.parseBinding('a').ast, 'a', reflector.setter('a'), false);
pv.bindDirectiveProperty( 0, parser.parseBinding('b').ast, 'b', reflector.setter('b'), false);
createViewAndChangeDetector(pv);

ctx.a = 0;
ctx.b = 0;
cd.detectChanges();
Expand Down
18 changes: 18 additions & 0 deletions modules/directives/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: directives
environment:
sdk: '>=1.4.0'
dependencies:
core:
path: ../core
change_detection:
path: ../change_detection
di:
path: ../di
facade:
path: ../facade
reflection:
path: ../reflection
dev_dependencies:
test_lib:
path: ../test_lib
guinness: ">=0.1.16 <0.2.0"
94 changes: 94 additions & 0 deletions modules/directives/src/ng_repeat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit} from 'test_lib/test_lib';

import {Decorator, Component, Template} from 'core/annotations/annotations';
import {OnChange} from 'core/compiler/interfaces';
import {ViewPort} from 'core/compiler/viewport';
import {View} from 'core/compiler/view';
import {isPresent, isBlank} from 'facade/lang';
import {ListWrapper, List} from 'facade/collection';

@Template({
selector: '[ng-repeat]',
bind: {
'in': 'iterable[]'
}
})
export class NgRepeat extends OnChange {
viewPort: ViewPort;
iterable;
constructor(viewPort: ViewPort) {
this.viewPort = viewPort;
}
onChange(changes) {
var iteratorChanges = changes['iterable'];
if (isBlank(iteratorChanges) || isBlank(iteratorChanges.currentValue)) {
this.viewPort.clear();
return;
}

// TODO(rado): check if change detection can produce a change record that is
// easier to consume than current.
var recordViewTuples = [];
iteratorChanges.currentValue.forEachRemovedItem(
(removedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(removedRecord, null))
);

iteratorChanges.currentValue.forEachMovedItem(
(movedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(movedRecord, null))
);

var insertTuples = NgRepeat.bulkRemove(recordViewTuples, this.viewPort);

iteratorChanges.currentValue.forEachAddedItem(
(addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null))
);

NgRepeat.bulkInsert(insertTuples, this.viewPort);

for (var i = 0; i < insertTuples.length; i++) {
this.perViewChange(insertTuples[i].view, insertTuples[i].record);
}
}

perViewChange(view, record) {
view.setLocal('ng-repeat', record.item);
// Uncomment when binding is ready.
// view.setLocal('index', record.item);
}

static bulkRemove(tuples, viewPort) {
tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
var movedTuples = [];
for (var i = tuples.length - 1; i >= 0; i--) {
var tuple = tuples[i];
var view = viewPort.remove(tuple.record.previousIndex);
if (isPresent(tuple.record.currentIndex)) {
tuple.view = view;
ListWrapper.push(movedTuples, tuple);
}
}
return movedTuples;
}

static bulkInsert(tuples, viewPort) {
tuples.sort((a, b) => a.record.currentIndex - b.record.currentIndex);
for (var i = 0; i < tuples.length; i++) {
var tuple = tuples[i];
if (isPresent(tuple.view)) {
viewPort.insert(tuple.view, tuple.record.currentIndex);
} else {
tuple.view = viewPort.create(tuple.record.currentIndex);
}
}
return tuples;
}
}

class RecordViewTuple {
view: View;
record: any;
constructor(record, view) {
this.record = record;
this.view = view;
}
}
Loading

0 comments on commit 60456c8

Please sign in to comment.