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
16 changes: 10 additions & 6 deletions lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,14 @@ class Scope {
this._readWriteGroup, this._readOnlyGroup, this.id);

/**
* A [watch] sets up a watch in the [digest] phase of the [apply] cycle.
* Use [watch] to set up a watch in the [apply] cycle.
*
* Use [watch] if the reaction function can cause updates to model. In your
* controller code you will most likely use [watch].
* When [readOnly] is [:true:], the watch will be executed in the [flush]
* cycle. It should be used when the [reactionFn] does not change the model
* and allows the [digest] phase to converge faster.
*
* On the opposite, [readOnly] should be set to [:false:] if the [reactionFn]
* could change the model so that the watch is observed in the [digest] cycle.
*/
Watch watch(expression, ReactionFn reactionFn,
{context, FilterMap filters, bool readOnly: false}) {
Expand Down Expand Up @@ -831,8 +835,8 @@ class AstParser {
AstParser(this._parser);

AST call(String exp, { FilterMap filters,
bool collection:false,
Object context:null }) {
bool collection: false,
Object context: null }) {
_visitor.filters = filters;
AST contextRef = _visitor.contextRef;
try {
Expand Down Expand Up @@ -1009,7 +1013,7 @@ class MapFn extends FunctionApply {

MapFn(this.keys);

apply(List values) {
Map apply(List values) {
// TODO(misko): figure out why do we need to make a copy instead of reusing instance?
assert(values.length == keys.length);
return new Map.fromIterables(keys, values);
Expand Down
22 changes: 19 additions & 3 deletions lib/core_dom/directive.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
part of angular.core.dom;

/**
* Callback function used to notify of attribute changes.
*/
/// Callback function used to notify of attribute changes.
typedef AttributeChanged(String newValue);

/// Callback function used to notify of observer changes.
typedef void ObserverChanged(bool hasListeners);

/**
* NodeAttrs is a facade for element attributes. The facade is responsible
* for normalizing attribute names as well as allowing access to the
Expand All @@ -15,6 +16,8 @@ class NodeAttrs {

Map<String, List<AttributeChanged>> _observers;

Map<String, List<ObserverChanged>> _observerListeners = {};

NodeAttrs(this.element);

operator [](String attributeName) => element.attributes[attributeName];
Expand All @@ -41,6 +44,10 @@ class NodeAttrs {
.add(notifyFn);

notifyFn(this[attributeName]);

if (_observerListeners.containsKey(attributeName)) {
_observerListeners[attributeName].forEach((cb) => cb(true));
}
}

void forEach(void f(String k, String v)) {
Expand All @@ -51,6 +58,15 @@ class NodeAttrs {
element.attributes.containsKey(attributeName);

Iterable<String> get keys => element.attributes.keys;

void listenObserverChanges(String attributeName, ObserverChanged fn) {
if (_observerListeners == null) {
_observerListeners = <String, List<ObserverChanged>>{};
}
_observerListeners.putIfAbsent(attributeName, () => <ObserverChanged>[])
.add(fn);
fn(false);
}
}

/**
Expand Down
166 changes: 74 additions & 92 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,21 @@ class ElementBinderFactory {

ElementBinderFactory(this._parser, this._perf, this._expando);

binder() {
return new ElementBinder(_parser, _perf, _expando);
}
ElementBinder binder() => new ElementBinder(_parser, _perf, _expando);
}

/**
* ElementBinder is created by the Selector and is responsible for instantiating individual directives
* and binding element properties.
* ElementBinder is created by the Selector and is responsible for instantiating
* individual directives and binding element properties.
*/

class ElementBinder {
// DI Services
Parser _parser;
Profiler _perf;
Expando _expando;
final Parser _parser;
final Profiler _perf;
final Expando _expando;

// Member fields
List<DirectiveRef> decorators = [];
var decorators = <DirectiveRef>[];
DirectiveRef template;
ViewFactory templateViewFactory;

Expand All @@ -34,18 +31,15 @@ class ElementBinder {
// Can be either COMPILE_CHILDREN or IGNORE_CHILDREN
String childMode = NgAnnotation.COMPILE_CHILDREN;


ElementBinder(this._parser, this._perf, this._expando);

ElementBinder.forTransclusion(ElementBinder other) {
_parser = other._parser;
_perf = other._perf;
_expando = other._expando;

decorators = other.decorators;
component = other.component;
childMode = other.childMode;
}
ElementBinder.forTransclusion(ElementBinder other)
: _parser = other._parser,
_perf = other._perf,
_expando = other._expando,
decorators = other.decorators,
component = other.component,
childMode = other.childMode;

addDirective(DirectiveRef ref) {
var annotation = ref.annotation;
Expand All @@ -66,49 +60,35 @@ class ElementBinder {
createMappings(ref);
}

bool get hasTemplate {
return template != null;
}
bool get hasTemplate => template != null;

bool get shouldCompileChildren {
return childMode == NgAnnotation.COMPILE_CHILDREN;
}
bool get shouldCompileChildren =>
childMode == NgAnnotation.COMPILE_CHILDREN;

ElementBinder get templateBinder {
return new ElementBinder.forTransclusion(this);
}
ElementBinder get templateBinder => new ElementBinder.forTransclusion(this);

List<DirectiveRef> get _usableDirectiveRefs {
if (template != null) {
return [template];
}
if (component != null) {
return new List.from(decorators)..add(component);
}
if (template != null) return [template];
if (component != null) return new List.from(decorators)..add(component);
return decorators;
}

bool get hasDirectives {
return (_usableDirectiveRefs != null && _usableDirectiveRefs.length != 0);
}

// DI visibility callback allowing node-local visibility.
bool get hasDirectives =>
_usableDirectiveRefs != null && _usableDirectiveRefs.length != 0;

// DI visibility strategy allowing node-local visibility.
static final Function _elementOnly = (Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
if (requesting.name == _SHADOW) requesting = requesting.parent;
return identical(requesting, defining);
};

// DI visibility callback allowing visibility from direct child into parent.

static final Function _elementDirectChildren = (Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
return _elementOnly(requesting, defining) || identical(requesting.parent, defining);
};
// DI visibility strategy allowing visibility from direct child into parent.
static final Function _elementDirectChildren =
(Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) requesting = requesting.parent;
return _elementOnly(requesting, defining) ||
identical(requesting.parent, defining);
};

Injector bind(View view, Injector parentInjector, dom.Node node) {
var timerId;
Expand All @@ -122,30 +102,36 @@ class ElementBinder {

var directiveRefs = _usableDirectiveRefs;
try {
if (directiveRefs == null || directiveRefs.length == 0) return parentInjector;
var nodeModule = new Module();
if (directiveRefs == null || directiveRefs.length == 0) {
return parentInjector;
}
var viewPortFactory = (_) => null;
var viewFactory = (_) => null;
var boundViewFactory = (_) => null;
var nodesAttrsDirectives = null;
var nodeModule = new Module()..type(NgElement)
..value(View, view)
..value(dom.Element, node)
..value(dom.Node, node)
..value(NodeAttrs, nodeAttrs);

nodeModule.type(NgElement);
nodeModule.value(View, view);
nodeModule.value(dom.Element, node);
nodeModule.value(dom.Node, node);
nodeModule.value(NodeAttrs, nodeAttrs);
directiveRefs.forEach((DirectiveRef ref) {
NgAnnotation annotation = ref.annotation;
var visibility = _elementOnly;
if (ref.annotation is NgController) {
scope = scope.createChild(new PrototypeMap(scope.context));
nodeModule.value(Scope, scope);
}
if (ref.annotation.visibility == NgDirective.CHILDREN_VISIBILITY) {
visibility = null;
} else if (ref.annotation.visibility == NgDirective.DIRECT_CHILDREN_VISIBILITY) {
visibility = _elementDirectChildren;

switch (ref.annotation.visibility) {
case NgDirective.CHILDREN_VISIBILITY:
visibility = null;
break;
case NgDirective.DIRECT_CHILDREN_VISIBILITY:
visibility = _elementDirectChildren;
break;
}

if (ref.type == NgTextMustacheDirective) {
nodeModule.factory(NgTextMustacheDirective, (Injector injector) {
return new NgTextMustacheDirective(
Expand Down Expand Up @@ -187,28 +173,30 @@ class ElementBinder {
nodeModule.type(ref.type, visibility: visibility);
}
for (var publishType in ref.annotation.publishTypes) {
nodeModule.factory(publishType, (Injector injector) => injector.get(ref.type), visibility: visibility);
nodeModule.factory(publishType, (Injector injector) =>
injector.get(ref.type), visibility: visibility);
}
if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) {
// Currently, transclude is only supported for NgDirective.
assert(annotation is NgDirective);
viewPortFactory = (_) => new ViewPort(node,
parentInjector.get(NgAnimate));
parentInjector.get(NgAnimate));
viewFactory = (_) => templateViewFactory;
boundViewFactory = (Injector injector) => templateViewFactory.bind(injector);
boundViewFactory = (Injector injector) =>
templateViewFactory.bind(injector);
}
});
nodeModule
..factory(ViewPort, viewPortFactory)
..factory(ViewFactory, viewFactory)
..factory(BoundViewFactory, boundViewFactory)
..factory(ElementProbe, (_) => probe);
nodeModule..factory(ViewPort, viewPortFactory)
..factory(ViewFactory, viewFactory)
..factory(BoundViewFactory, boundViewFactory)
..factory(ElementProbe, (_) => probe);
nodeInjector = parentInjector.createChild([nodeModule]);
probe = _expando[node] = new ElementProbe(
parentInjector.get(ElementProbe), node, nodeInjector, scope);
} finally {
assert(_perf.stopTimer(timerId) != false);
}

directiveRefs.forEach((DirectiveRef ref) {
var linkTimer;
try {
Expand Down Expand Up @@ -311,8 +299,7 @@ class ElementBinder {
var viewOutbound = false;
var viewInbound = false;
scope.watch(
expression,
(inboundValue, _) {
expression, (inboundValue, _) {
if (!viewInbound) {
viewOutbound = true;
scope.rootScope.runAsync(() => viewOutbound = false);
Expand All @@ -325,8 +312,7 @@ class ElementBinder {
);
if (expressionFn.isAssignable) {
scope.watch(
dstExpression,
(outboundValue, _) {
dstExpression, (outboundValue, _) {
if (!viewOutbound) {
viewInbound = true;
scope.rootScope.runAsync(() => viewInbound = false);
Expand All @@ -346,8 +332,7 @@ class ElementBinder {
if (attrs[attrName] == null) return notify();
Expression attrExprFn = _parser(attrs[attrName]);
var shadowValue = null;
scope.watch(attrs[attrName],
(v, _) {
scope.watch(attrs[attrName], (v, _) {
dstPathFn.assign(controller, shadowValue = v);
notify();
},
Expand All @@ -360,23 +345,20 @@ class ElementBinder {
if (attrs[attrName] == null) return notify();
Expression attrExprFn = _parser(attrs[attrName]);
var watch;
watch = scope.watch(
attrs[attrName],
(value, _) {
if (dstPathFn.assign(controller, value) != null) {
watch.remove();
}
},
filters: filters);
watch = scope.watch(attrs[attrName], (value, _) {
if (dstPathFn.assign(controller, value) != null) {
watch.remove();
}
},
filters: filters);
notify();
};
break;
case '&':
mappingFn = (NodeAttrs attrs, Scope scope, Object dst,
FilterMap filters, notify()) {
dstPathFn
.assign(dst, _parser(attrs[attrName])
.bind(scope.context, ScopeLocals.wrapper));
dstPathFn.assign(dst, _parser(attrs[attrName])
.bind(scope.context, ScopeLocals.wrapper));
notify();
};
break;
Expand All @@ -386,7 +368,6 @@ class ElementBinder {
}
}


// Used for walking the DOM
class ElementBinderTreeRef {
final int offsetIndex;
Expand All @@ -401,7 +382,6 @@ class ElementBinderTree {
ElementBinderTree(this.binder, this.subtrees);
}


class TaggedTextBinder {
ElementBinder binder;
final int offsetIndex;
Expand All @@ -411,8 +391,8 @@ class TaggedTextBinder {

// Used for the tagging compiler
class TaggedElementBinder {
ElementBinder binder;
int parentBinderOffset;
final ElementBinder binder;
final int parentBinderOffset;
var injector;

List<TaggedTextBinder> textBinders;
Expand All @@ -424,5 +404,7 @@ class TaggedElementBinder {
textBinders.add(tagged);
}

toString() => "[TaggedElementBinder binder:$binder parentBinderOffset:$parentBinderOffset textBinders:$textBinders injector:$injector]";
String toString() => "[TaggedElementBinder binder:$binder parentBinderOffset:"
"$parentBinderOffset textBinders:$textBinders "
"injector:$injector]";
}
Loading