Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 38 additions & 81 deletions lib/core_dom/block.dart
Original file line number Diff line number Diff line change
@@ -1,104 +1,61 @@
part of angular.core.dom;

/**
* ElementWrapper is an interface for [Block]s and [BlockHole]s. Its purpose is
* to allow treating [Block] and [BlockHole] under same interface so that
* [Block]s can be added after [BlockHole].
*/
abstract class ElementWrapper {
List<dom.Node> elements;
ElementWrapper next;
ElementWrapper previous;
}

/**
* A Block is a fundamental building block of DOM. It is a chunk of DOM which
* can not be structural changed. It can only have its attributes changed.
* A Block can have [BlockHole]s embedded in its DOM. A [BlockHole] can
* contain other [Block]s and it is the only way in which DOM can be changed
* structurally.
* can not be structurally changed. A Block can have [BlockHole] placeholders
* embedded in its DOM. A [BlockHole] can contain other [Block]s and it is the
* only way in which DOM structure can be modified.
*
* A [Block] is a collection of DOM nodes
*

* A [Block] can be created from [BlockFactory].
*
*/
class Block implements ElementWrapper {
List<dom.Node> elements;
ElementWrapper next;
ElementWrapper previous;
class Block {
final List<dom.Node> nodes;
Block(this.nodes);
}

/**
* A BlockHole maintains an ordered list of [Block]'s. It contains a
* [placeholder] node that is used as the insertion point for block nodes.
*/
class BlockHole {
final dom.Node placeholder;
final NgAnimate _animate;
final List<Block> _blocks = <Block>[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_childBlocks ?


Block(this.elements, this._animate);

Block insertAfter(ElementWrapper previousBlock) {
// Update Link List.
next = previousBlock.next;
if (next != null) {
next.previous = this;
}
previous = previousBlock;
previousBlock.next = this;
BlockHole(this.placeholder, this._animate);

// Update DOM
List<dom.Node> previousElements = previousBlock.elements;
dom.Node previousElement = previousElements[previousElements.length - 1];
dom.Node insertBeforeElement = previousElement.nextNode;
dom.Node parentElement = previousElement.parentNode;
void insert(Block block, { Block insertAfter }) {
dom.Node previousNode = _lastNode(insertAfter);
_blocksInsertAfter(block, insertAfter);

_animate.insert(elements, parentElement, insertBefore: insertBeforeElement);

return this;
_animate.insert(block.nodes, placeholder.parentNode,
insertBefore: previousNode.nextNode);
}

Block remove() {
bool preventDefault = false;

_animate.remove(elements);

// Remove block from list
if (previous != null && (previous.next = next) != null) {
next.previous = previous;
}
next = previous = null;
return this;
void remove(Block block) {
_blocks.remove(block);
_animate.remove(block.nodes);
}

Block moveAfter(ElementWrapper previousBlock) {
var previousElements = previousBlock.elements,
previousElement = previousElements[previousElements.length - 1],
insertBeforeElement = previousElement.nextNode,
parentElement = previousElement.parentNode;

elements.forEach((el) => parentElement.insertBefore(el, insertBeforeElement));
void move(Block block, { Block moveAfter }) {
dom.Node previousNode = _lastNode(moveAfter);
_blocks.remove(block);
_blocksInsertAfter(block, moveAfter);

// Remove block from list
previous.next = next;
if (next != null) {
next.previous = previous;
}
// Add block to list
next = previousBlock.next;
if (next != null) {
next.previous = this;
}
previous = previousBlock;
previousBlock.next = this;
return this;
_animate.move(block.nodes, placeholder.parentNode,
insertBefore: previousNode.nextNode);
}
}

/**
* A BlockHole is an instance of a hole. BlockHoles designate where child
* [Block]s can be added in parent [Block]. BlockHoles wrap a DOM element,
* and act as references which allows more blocks to be added.
*/
class BlockHole extends ElementWrapper {
List<dom.Node> elements;
ElementWrapper previous;
ElementWrapper next;
void _blocksInsertAfter(Block block, Block insertAfter) {
int index = (insertAfter != null) ? _blocks.indexOf(insertAfter) : -1;
_blocks.insert(index + 1, block);
}

BlockHole(this.elements);
dom.Node _lastNode(Block insertAfter) =>
insertAfter == null
? placeholder
: insertAfter.nodes[insertAfter.nodes.length - 1];
}

13 changes: 7 additions & 6 deletions lib/core_dom/block_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ class BlockFactory implements Function {
BoundBlockFactory bind(Injector injector) =>
new BoundBlockFactory(this, injector);

Block call(Injector injector, [List<dom.Node> elements]) {
if (elements == null) elements = cloneElements(templateElements);
Block call(Injector injector, [List<dom.Node> nodes]) {
if (nodes == null) nodes = cloneElements(templateElements);
var timerId;
try {
assert((timerId = _perf.startTimer('ng.block')) != false);
var block = new Block(elements, injector.get(NgAnimate));
_link(block, elements, directivePositions, injector);
var block = new Block(nodes);
_link(block, nodes, directivePositions, injector);
return block;
} finally {
assert(_perf.stopTimer(timerId) != false);
Expand Down Expand Up @@ -176,7 +176,8 @@ class BlockFactory implements Function {
if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) {
// Currently, transclude is only supported for NgDirective.
assert(annotation is NgDirective);
blockHoleFactory = (_) => new BlockHole([node]);
blockHoleFactory = (_) => new BlockHole(node,
parentInjector.get(NgAnimate));
blockFactory = (_) => ref.blockFactory;
boundBlockFactory = (Injector injector) =>
ref.blockFactory.bind(injector);
Expand Down Expand Up @@ -384,7 +385,7 @@ class _ComponentFactory implements Function {

attachBlockToShadowDom(BlockFactory blockFactory) {
var block = blockFactory(shadowInjector);
shadowDom.nodes.addAll(block.elements);
shadowDom.nodes.addAll(block.nodes);
return shadowDom;
}

Expand Down
8 changes: 4 additions & 4 deletions lib/directive/ng_if.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ abstract class _NgUnlessIfAttrDirectiveBase {
if (_block == null) {
_childScope = _scope.createChild(new PrototypeMap(_scope.context));
_block = _boundBlockFactory(_childScope);
var insertBlock = _block;
var block = _block;
_scope.rootScope.domWrite(() {
insertBlock.insertAfter(_blockHole);
_blockHole.insert(block);
});
}
}

void _ensureBlockDestroyed() {
if (_block != null) {
var removeBlock = _block;
var block = _block;
_scope.rootScope.domWrite(() {
removeBlock.remove();
_blockHole.remove(block);
});
_childScope.destroy();
_block = null;
Expand Down
22 changes: 11 additions & 11 deletions lib/directive/ng_include.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@ class NgIncludeDirective {
final Injector injector;
final DirectiveMap directives;

Block _previousBlock;
Scope _previousScope;
Block _block;
Scope _scope;

NgIncludeDirective(this.element, this.scope, this.blockCache, this.injector, this.directives);

_cleanUp() {
if (_previousBlock == null) return;
if (_block == null) return;

_previousBlock.remove();
_previousScope.destroy();
_block.nodes.forEach((node) => node.remove);
_scope.destroy();
element.innerHtml = '';

_previousBlock = null;
_previousScope = null;
_block = null;
_scope = null;
}

_updateContent(createBlock) {
// create a new scope
_previousScope = scope.createChild(new PrototypeMap(scope.context));
_previousBlock = createBlock(injector.createChild([new Module()
..value(Scope, _previousScope)]));
_scope = scope.createChild(new PrototypeMap(scope.context));
_block = createBlock(injector.createChild([new Module()
..value(Scope, _scope)]));

_previousBlock.elements.forEach((elm) => element.append(elm));
_block.nodes.forEach((node) => element.append(node));
}


Expand Down
19 changes: 10 additions & 9 deletions lib/directive/ng_repeat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class _Row {
Block block;
dom.Element startNode;
dom.Element endNode;
List<dom.Element> elements;
List<dom.Element> nodes;

_Row(this.id);
}
Expand Down Expand Up @@ -177,20 +177,21 @@ class NgRepeatDirective {
}
// remove existing items
_rows.forEach((key, row) {
row.block.remove();
_blockHole.remove(row.block);
row.scope.destroy();
});
_rows = newRows;
return newRowOrder;
}

_onCollectionChange(Iterable collection) {
dom.Node previousNode = _blockHole.elements[0]; // current position of the node
dom.Node previousNode = _blockHole.placeholder; // current position of the
// node
dom.Node nextNode;
Scope childScope;
Map childContext;
Scope trackById;
ElementWrapper cursor = _blockHole;
Block cursor;

List<_Row> newRowOrder = _computeNewRows(collection, trackById);

Expand All @@ -211,7 +212,7 @@ class NgRepeatDirective {

if (row.startNode != nextNode) {
// existing item which got moved
row.block.moveAfter(cursor);
_blockHole.move(row.block, moveAfter: cursor);
}
previousNode = row.endNode;
} else {
Expand All @@ -237,10 +238,10 @@ class NgRepeatDirective {
_rows[row.id] = row
..block = block
..scope = childScope
..elements = block.elements
..startNode = row.elements[0]
..endNode = row.elements[row.elements.length - 1];
block.insertAfter(cursor);
..nodes = block.nodes
..startNode = row.nodes[0]
..endNode = row.nodes[row.nodes.length - 1];
_blockHole.insert(block, insertAfter: cursor);
}
cursor = row.block;
}
Expand Down
12 changes: 7 additions & 5 deletions lib/directive/ng_switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class NgSwitchDirective {
set value(val) {
currentBlocks
..forEach((_BlockScopePair pair) {
pair.block.remove();
pair.hole.remove(pair.block);
pair.scope.destroy();
})
..clear();
Expand All @@ -83,8 +83,10 @@ class NgSwitchDirective {
(cases.containsKey(val) ? cases[val] : cases['?'])
.forEach((_Case caze) {
Scope childScope = scope.createChild(new PrototypeMap(scope.context));
var block = caze.blockFactory(childScope)..insertAfter(caze.anchor);
currentBlocks.add(new _BlockScopePair(block, childScope));
var block = caze.blockFactory(childScope);
caze.anchor.insert(block);
currentBlocks.add(new _BlockScopePair(block, caze.anchor,
childScope));
});
if (onChange != null) {
onChange();
Expand All @@ -94,9 +96,10 @@ class NgSwitchDirective {

class _BlockScopePair {
final Block block;
final BlockHole hole;
final Scope scope;

_BlockScopePair(this.block, this.scope);
_BlockScopePair(this.block, this.hole, this.scope);
}

class _Case {
Expand All @@ -121,7 +124,6 @@ class NgSwitchWhenDirective {
set value(String value) => ngSwitch.addCase('!$value', hole, blockFactory);
}


@NgDirective(
children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector: '[ng-switch-default]')
Expand Down
22 changes: 11 additions & 11 deletions lib/routing/ng_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class NgViewDirective implements NgDetachAware, RouteProvider {
final Scope scope;
RouteHandle _route;

Block _previousBlock;
Scope _previousScope;
Block _block;
Scope _scope;
Route _viewRoute;

NgViewDirective(this.element, this.blockCache,
Expand Down Expand Up @@ -116,23 +116,23 @@ class NgViewDirective implements NgDetachAware, RouteProvider {
var newDirectives = viewInjector.get(DirectiveMap);
blockCache.fromUrl(templateUrl, newDirectives).then((blockFactory) {
_cleanUp();
_previousScope = scope.createChild(new PrototypeMap(scope.context));
_previousBlock = blockFactory(
_scope = scope.createChild(new PrototypeMap(scope.context));
_block = blockFactory(
viewInjector.createChild(
[new Module()..value(Scope, _previousScope)]));
[new Module()..value(Scope, _scope)]));

_previousBlock.elements.forEach((elm) => element.append(elm));
_block.nodes.forEach((elm) => element.append(elm));
});
}

_cleanUp() {
if (_previousBlock == null) return;
if (_block == null) return;

_previousBlock.remove();
_previousScope.destroy();
_block.nodes.forEach((node) => node.remove());
_scope.destroy();

_previousBlock = null;
_previousScope = null;
_block = null;
_scope = null;
}

Route get route => _viewRoute;
Expand Down
Loading