Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(631)

Unified Diff: pkg/template_binding/lib/src/template.dart

Issue 132403010: big update to observe, template_binding, polymer (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/template_binding/lib/src/template.dart
diff --git a/pkg/template_binding/lib/src/template.dart b/pkg/template_binding/lib/src/template.dart
index 8df84d1bbd28ec5ac621f7a39d59d9fc1ed47957..d085a8962a3f716c397a1bcfc8da35f20aa72d83 100644
--- a/pkg/template_binding/lib/src/template.dart
+++ b/pkg/template_binding/lib/src/template.dart
@@ -9,7 +9,7 @@ class TemplateBindExtension extends _ElementExtension {
var _model;
BindingDelegate _bindingDelegate;
_TemplateIterator _iterator;
- bool _scheduled = false;
+ bool _setModelScheduled = false;
Element _templateInstanceRef;
@@ -19,7 +19,7 @@ class TemplateBindExtension extends _ElementExtension {
HtmlDocument _stagingDocument;
- var _bindingMap;
+ _InstanceBindingMap _bindingMap;
TemplateBindExtension._(Element node) : super(node);
@@ -28,106 +28,70 @@ class TemplateBindExtension extends _ElementExtension {
TemplateBindExtension get _self => super._node is TemplateBindExtension
? _node : this;
- NodeBinding bind(String name, model, [String path]) {
- path = path != null ? path : '';
+ _TemplateIterator _processBindingDirectives(_TemplateBindingMap directives) {
+ if (_iterator != null) _iterator._closeDependencies();
- if (_iterator == null) {
- // TODO(jmesserly): since there's only one iterator, we could just
- // inline it into this object.
- _iterator = new _TemplateIterator(this);
- }
+ if (directives._if == null &&
+ directives._bind == null &&
+ directives._repeat == null) {
- // Dart note: we return _TemplateBinding instead of _iterator.
- // See comment on _TemplateBinding class.
- switch (name) {
- case 'bind':
- _iterator..hasBind = true
- ..bindModel = model
- ..bindPath = path;
- _scheduleIterator();
- return bindings[name] = new _TemplateBinding(this, name, model, path);
- case 'repeat':
- _iterator..hasRepeat = true
- ..repeatModel = model
- ..repeatPath = path;
- _scheduleIterator();
- return bindings[name] = new _TemplateBinding(this, name, model, path);
- case 'if':
- _iterator..hasIf = true
- ..ifModel = model
- ..ifPath = path;
- _scheduleIterator();
- return bindings[name] = new _TemplateBinding(this, name, model, path);
- default:
- return super.bind(name, model, path);
+ if (_iterator != null) {
+ _iterator.close();
+ _iterator = null;
+ bindings.remove('iterator');
+ }
+ return null;
}
- }
- void unbind(String name) {
- switch (name) {
- case 'bind':
- if (_iterator == null) return;
- _iterator..hasBind = false
- ..bindModel = null
- ..bindPath = null;
- _scheduleIterator();
- bindings.remove(name);
- return;
- case 'repeat':
- if (_iterator == null) return;
- _iterator..hasRepeat = false
- ..repeatModel = null
- ..repeatPath = null;
- _scheduleIterator();
- bindings.remove(name);
- return;
- case 'if':
- if (_iterator == null) return;
- _iterator..hasIf = false
- ..ifModel = null
- ..ifPath = null;
- _scheduleIterator();
- bindings.remove(name);
- return;
- default:
- super.unbind(name);
- return;
+ if (_iterator == null) {
+ bindings['iterator'] = _iterator = new _TemplateIterator(this);
}
- }
- void _scheduleIterator() {
- if (!_iterator.depsChanging) {
- _iterator.depsChanging = true;
- scheduleMicrotask(_iterator.resolve);
- }
+ _iterator._updateDependencies(directives, model);
+ return _iterator;
}
/**
- * Creates an instance of the template, using the provided model and optional
- * binding delegate.
+ * Creates an instance of the template, using the provided [model] and
+ * optional binding [delegate].
+ *
+ * If [instanceBindings] is supplied, each [Bindable] in the returned
+ * instance will be added to the list. This makes it easy to close all of the
+ * bindings without walking the tree. This is not normally necesssary, but is
+ * used internally by the system.
*/
DocumentFragment createInstance([model, BindingDelegate delegate,
- List<NodeBinding> bound]) {
- var ref = templateBind(this.ref);
- var content = ref.content;
+ List<Bindable> instanceBindings]) {
+
+ final content = templateBind(ref).content;
// Dart note: we store _bindingMap on the TemplateBindExtension instead of
// the "content" because we already have an expando for it.
- var map = ref._bindingMap;
- if (map == null) {
+ var map = _bindingMap;
+ if (map == null || !identical(map.content, content)) {
// TODO(rafaelw): Setup a MutationObserver on content to detect
// when the instanceMap is invalid.
map = _createInstanceBindingMap(content, delegate);
- ref._bindingMap = map;
+ map.content = content;
+ _bindingMap = map;
+ }
+
+ final staging = _getTemplateStagingDocument();
+ final instance = _stagingDocument.createDocumentFragment();
+ _templateCreator[instance] = _node;
+
+ final instanceRecord = new TemplateInstance(model);
+
+ var i = 0;
+ for (var c = content.firstChild; c != null; c = c.nextNode, i++) {
+ final childMap = map != null ? map.getChild(i) : null;
+ var clone = _cloneAndBindInstance(c, instance, _stagingDocument,
+ childMap, model, delegate, instanceBindings);
+ nodeBindFallback(clone)._templateInstance = instanceRecord;
}
- var staging = _getTemplateStagingDocument();
- var instance = _deepCloneIgnoreTemplateContent(content, staging);
+ instanceRecord._firstNode = instance.firstChild;
+ instanceRecord._lastNode = instance.lastChild;
- _addMapBindings(instance, map, model, delegate, bound);
- // TODO(rafaelw): We can do this more lazily, but setting a sentinel
- // in the parent of the template element, and creating it when it's
- // asked for by walking back to find the iterating template.
- _addTemplateInstanceRecord(instance, model);
return instance;
}
@@ -157,19 +121,27 @@ class TemplateBindExtension extends _ElementExtension {
void set bindingDelegate(BindingDelegate value) {
_bindingDelegate = value;
- _ensureSetModelScheduled();
+
+ // Clear cached state based on the binding delegate.
+ _bindingMap = null;
+ if (_iterator != null) {
+ _iterator._initPrepareFunctions = false;
+ _iterator._instanceModelFn = null;
+ _iterator._instancePositionChangedFn = null;
+ }
}
_ensureSetModelScheduled() {
- if (_scheduled) return;
+ if (_setModelScheduled) return;
_decorate();
- _scheduled = true;
+ _setModelScheduled = true;
scheduleMicrotask(_setModel);
}
void _setModel() {
- _scheduled = false;
- _addBindings(_node, _model, _bindingDelegate);
+ _setModelScheduled = false;
+ var map = _getBindings(_node, _bindingDelegate);
+ _processBindings(_node, map, _model);
}
/** Gets the template this node refers to. */
@@ -183,6 +155,15 @@ class TemplateBindExtension extends _ElementExtension {
if (treeScope != null) {
result = treeScope.getElementById(refId);
}
+ if (result == null) {
+ var instanceRoot = _getInstanceRoot(_node);
+
+ // TODO(jmesserly): this won't work if refId is a number
+ // Similar to bug: https://github.com/Polymer/ShadowDOM/issues/340
+ if (instanceRoot != null) {
+ result = instanceRoot.querySelector('#$refId');
+ }
+ }
}
if (result == null) {
@@ -223,25 +204,32 @@ class TemplateBindExtension extends _ElementExtension {
var templateElementExt = this;
_templateIsDecorated = true;
- var isNative = _node is TemplateElement;
- var bootstrapContents = isNative;
- var liftContents = !isNative;
+ var isNativeHtmlTemplate = _node is TemplateElement;
+ final bootstrapContents = isNativeHtmlTemplate;
+ final liftContents = !isNativeHtmlTemplate;
var liftRoot = false;
- if (!isNative && _isAttributeTemplate(_node)) {
- if (instanceRef != null) {
- // TODO(jmesserly): this is just an assert in TemplateBinding.
- throw new ArgumentError('instanceRef should not be supplied for '
- 'attribute templates.');
+ if (!isNativeHtmlTemplate) {
+ if (_isAttributeTemplate(_node)) {
+ if (instanceRef != null) {
+ // Dart note: this is just an assert in JS.
+ throw new ArgumentError('instanceRef should not be supplied for '
+ 'attribute templates.');
+ }
+ templateElementExt = templateBind(
+ _extractTemplateFromAttributeTemplate(_node));
+ templateElementExt._templateIsDecorated = true;
+ isNativeHtmlTemplate = templateElementExt._node is TemplateElement;
+ liftRoot = true;
+ } else if (_isSvgTemplate(_node)) {
+ templateElementExt = templateBind(
+ _extractTemplateFromSvgTemplate(_node));
+ templateElementExt._templateIsDecorated = true;
+ isNativeHtmlTemplate = templateElementExt._node is TemplateElement;
}
- templateElementExt = templateBind(
- _extractTemplateFromAttributeTemplate(_node));
- templateElementExt._templateIsDecorated = true;
- isNative = templateElementExt._node is TemplateElement;
- liftRoot = true;
- }
-
- if (!isNative) {
+ }
+
+ if (!isNativeHtmlTemplate) {
var doc = _getOrCreateTemplateContentsOwner(templateElementExt._node);
templateElementExt._content = doc.createDocumentFragment();
}
@@ -326,6 +314,16 @@ class TemplateBindExtension extends _ElementExtension {
return template;
}
+ static Element _extractTemplateFromSvgTemplate(Element el) {
+ var template = el.ownerDocument.createElement('template');
+ el.parentNode.insertBefore(template, el);
+ template.attributes.addAll(el.attributes);
+
+ el.attributes.clear();
+ el.remove();
+ return template;
+ }
+
static void _liftNonNativeChildrenIntoContent(TemplateBindExtension template,
Element el, bool useRoot) {
@@ -386,58 +384,19 @@ class TemplateBindExtension extends _ElementExtension {
}
}
-// TODO(jmesserly): https://github.com/polymer/templatebinding uses
-// TemplateIterator as the binding. This is a nice performance optimization,
-// however it means it doesn't share any of the reflective APIs with
-// NodeBinding: https://github.com/Polymer/TemplateBinding/issues/147
-class _TemplateBinding implements NodeBinding {
- TemplateBindExtension _ext;
- Object _model;
- final String property;
- final String path;
-
- Node get node => _ext._node;
-
- get model => _model;
-
- bool get closed => _ext == null;
-
- get value => _observer.value;
-
- set value(newValue) {
- _observer.value = newValue;
- }
-
- // No need to cache this since we only have it to support get/set value.
- get _observer {
- if ((_model is PathObserver || _model is CompoundPathObserver) &&
- path == 'value') {
- return _model;
- }
- return new PathObserver(_model, path);
- }
-
- _TemplateBinding(this._ext, this.property, this._model, this.path);
-
- void valueChanged(newValue) {}
-
- sanitizeBoundValue(value) => value == null ? '' : '$value';
-
- void close() {
- if (closed) return;
-
- // TODO(jmesserly): unlike normal NodeBinding.close methods this will remove
- // the binding from _node.bindings. Is that okay?
- _ext.unbind(property);
-
- _model = null;
- _ext = null;
- }
-}
+final _templateCreator = new Expando();
_getTreeScope(Node node) {
- while (node.parentNode != null) {
- node = node.parentNode;
+ while (true) {
+ var parent = node.parentNode;
+ if (parent != null) {
+ node = parent;
+ } else {
+ var creator = _templateCreator[node];
+ if (creator == null) break;
+
+ node = creator;
+ }
}
// Note: JS code tests that getElementById is present. We can't do that
@@ -447,3 +406,10 @@ _getTreeScope(Node node) {
}
return null;
}
+
+_getInstanceRoot(node) {
+ while (node.parentNode != null) {
+ node = node.parentNode;
+ }
+ return _templateCreator[node] != null ? node : null;
+}

Powered by Google App Engine
This is Rietveld 408576698