Runtime stamped dom-if

This commit is contained in:
Kevin Schaaf
2019-06-18 22:10:50 -07:00
parent 27ed93aff1
commit e690dfe2c1
10 changed files with 295 additions and 140 deletions

View File

@@ -16,6 +16,8 @@ import { microTask } from '../utils/async.js';
import { root } from '../utils/path.js';
import { wrap } from '../utils/wrap.js';
import { hideElementsGlobally } from '../utils/hide-template-controls.js';
import { fastDomIf, strictTemplatePolicy } from '../utils/settings.js';
import { showHideChildren } from '../utils/templatize.js';
/**
* The `<dom-if>` element will stamp a light-dom `<template>` child when
@@ -85,7 +87,7 @@ export class DomIf extends PolymerElement {
constructor() {
super();
this.__renderDebouncer = null;
this.__invalidProps = null;
this.__syncProps = null;
this.__instance = null;
this._lastIf = false;
this.__ctor = null;
@@ -181,8 +183,11 @@ export class DomIf extends PolymerElement {
let parentNode = wrap(this).parentNode;
// Guard against element being detached while render was queued
if (parentNode) {
if (!this.__ctor) {
let template = this._templateInfo ? this : /** @type {HTMLTemplateElement} */(wrap(this).querySelector('template'));
if (!this.__template) {
// When `removeNestedTemplates` is true, the "template" is the element
// itself, which has been given a `_templateInfo` property
let template = this._templateInfo ? this :
/** @type {HTMLTemplateElement} */(wrap(this).querySelector('template'));
if (!template) {
// Wait until childList changes and template should be there by then
let observer = new MutationObserver(() => {
@@ -196,37 +201,63 @@ export class DomIf extends PolymerElement {
observer.observe(this, {childList: true});
return false;
}
this.__ctor = templatize(template, this, {
// dom-if templatizer instances require `mutable: true`, as
// `__syncHostProperties` relies on that behavior to sync objects
mutableData: true,
/**
* @param {string} prop Property to forward
* @param {*} value Value of property
* @this {DomIf}
*/
forwardHostProp: function(prop, value) {
if (this.__instance) {
if (this.if) {
this.__instance.forwardHostProp(prop, value);
} else {
// If we have an instance but are squelching host property
// forwarding due to if being false, note the invalidated
// properties so `__syncHostProperties` can sync them the next
// time `if` becomes true
this.__invalidProps = this.__invalidProps || Object.create(null);
this.__invalidProps[root(prop)] = true;
this.__template = template;
if (!fastDomIf) {
this.__ctor = templatize(template, this, {
// dom-if templatizer instances require `mutable: true`, as
// `__syncHostProperties` relies on that behavior to sync objects
mutableData: true,
/**
* @param {string} prop Property to forward
* @param {*} value Value of property
* @this {DomIf}
*/
forwardHostProp: function(prop, value) {
if (this.__instance) {
if (this.if) {
this.__instance.forwardHostProp(prop, value);
} else {
// If we have an instance but are squelching host property
// forwarding due to if being false, note the invalidated
// properties so `__syncHostProperties` can sync them the next
// time `if` becomes true
this.__syncProps = this.__syncProps || Object.create(null);
this.__syncProps[root(prop)] = true;
}
}
}
}
});
});
}
}
if (!this.__instance) {
this.__instance = new this.__ctor();
wrap(parentNode).insertBefore(this.__instance.root, this);
if (fastDomIf) {
const host = this.__dataHost || this;
if (strictTemplatePolicy) {
if (!this.__dataHost) {
throw new Error('strictTemplatePolicy: template owner not trusted');
}
}
// Pre-bind and link the template into the effects system
const templateInfo = host._bindTemplate(this.__template, true);
// Install runEffects hook that prevents running property effects
// (and any nested template effects) when the `if` is false
templateInfo.runEffects = runEffects => {
if (this.if) {
runEffects();
} else {
this.__syncProps = runEffects;
}
};
// Stamp the template, and set its DocumentFragment to the "instance"
this.__instance = host._stampTemplate(this.__template, templateInfo);
wrap(parentNode).insertBefore(this.__instance, this);
} else {
this.__instance = new this.__ctor();
wrap(parentNode).insertBefore(this.__instance.root, this);
}
} else {
this.__syncHostProperties();
let c$ = this.__instance.children;
let c$ = fastDomIf ? this.__instance.templateInfo.childNodes :
this.__instance.children;
if (c$ && c$.length) {
// Detect case where dom-if was re-attached in new position
let lastChild = wrap(this).previousSibling;
@@ -242,33 +273,41 @@ export class DomIf extends PolymerElement {
}
__syncHostProperties() {
let props = this.__invalidProps;
let props = this.__syncProps;
this.__syncProps = null;
if (props) {
for (let prop in props) {
this.__instance._setPendingProperty(prop, this.__dataHost[prop]);
if (fastDomIf) {
props();
} else {
for (let prop in props) {
this.__instance._setPendingProperty(prop, this.__dataHost[prop]);
}
this.__instance._flushProperties();
}
this.__invalidProps = null;
this.__instance._flushProperties();
}
}
__teardownInstance() {
if (this.__instance) {
let c$ = this.__instance.children;
if (c$ && c$.length) {
// use first child parent, for case when dom-if may have been detached
let parent = wrap(c$[0]).parentNode;
// Instance children may be disconnected from parents when dom-if
// detaches if a tree was innerHTML'ed
if (parent) {
parent = wrap(parent);
for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
parent.removeChild(n);
if (fastDomIf) {
this._removeBoundDom(this.__instance);
} else {
let c$ = this.__instance.children;
if (c$ && c$.length) {
// use first child parent, for case when dom-if may have been detached
let parent = wrap(c$[0]).parentNode;
// Instance children may be disconnected from parents when dom-if
// detaches if a tree was innerHTML'ed
if (parent) {
parent = wrap(parent);
for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
parent.removeChild(n);
}
}
}
}
this.__syncProps = null;
this.__instance = null;
this.__invalidProps = null;
}
}
@@ -283,7 +322,14 @@ export class DomIf extends PolymerElement {
_showHideChildren() {
let hidden = this.__hideTemplateChildren__ || !this.if;
if (this.__instance) {
this.__instance._showHideChildren(hidden);
if (fastDomIf) {
showHideChildren(hidden, this.__instance.templateInfo.childNodes);
} else {
this.__instance._showHideChildren(hidden);
}
if (!hidden) {
this.__syncHostProperties();
}
}
}

View File

@@ -339,7 +339,10 @@ export class DomRepeat extends domRepeatBase {
// until ready, since won't have its template content handed back to
// it until then
if (!this.__ctor) {
let template = this.template = this._templateInfo ? this : /** @type {HTMLTemplateElement} */(this.querySelector('template'));
// When `removeNestedTemplates` is true, the "template" is the element
// itself, which has been given a `_templateInfo` property
let template = this.template = this._templateInfo ? this :
/** @type {HTMLTemplateElement} */(this.querySelector('template'));
if (!template) {
// // Wait until childList changes and template should be there by then
let observer = new MutationObserver(() => {

View File

@@ -19,9 +19,7 @@ import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js';
import { PropertyAccessors } from './property-accessors.js';
/* for annotated effects */
import { TemplateStamp } from './template-stamp.js';
import { sanitizeDOMValue, legacyUndefined, legacyNoBatch, legacyNotifyOrder, orderedComputed } from '../utils/settings.js';
const btp = window.location.search.match(/btp/);
import { sanitizeDOMValue, legacyUndefined, legacyNoBatch, legacyNotifyOrder, orderedComputed, removeNestedTemplates, fastDomIf } from '../utils/settings.js';
// Monotonically increasing unique ID used for de-duping effects triggered
// from multiple properties in the same turn
@@ -1974,11 +1972,23 @@ export const PropertyEffects = dedupingMixin(superClass => {
if (this[TYPES.PROPAGATE]) {
runEffects(this, this[TYPES.PROPAGATE], changedProps, oldProps, hasPaths);
}
let templateInfo = this.__templateInfo;
while (templateInfo) {
if (this.__templateInfo) {
this._runEffectsForTemplate(this.__templateInfo, changedProps, oldProps, hasPaths);
}
}
_runEffectsForTemplate(templateInfo, changedProps, oldProps, hasPaths) {
const baseRunEffects = () => {
runEffects(this, templateInfo.propertyEffects, changedProps, oldProps,
hasPaths, templateInfo.nodeList);
templateInfo = templateInfo.nextTemplateInfo;
for (let info=templateInfo.firstChild; info; info=info.nextSibling) {
this._runEffectsForTemplate(info, changedProps, oldProps, hasPaths);
}
};
if (templateInfo.runEffects) {
templateInfo.runEffects(baseRunEffects);
} else {
baseRunEffects();
}
}
@@ -2684,14 +2694,23 @@ export const PropertyEffects = dedupingMixin(superClass => {
// and link into list of templates if necessary
templateInfo = /** @type {!TemplateInfo} */(Object.create(templateInfo));
templateInfo.wasPreBound = wasPreBound;
if (!wasPreBound && this.__templateInfo) {
let last = this.__templateInfoLast || this.__templateInfo;
this.__templateInfoLast = last.nextTemplateInfo = templateInfo;
templateInfo.previousTemplateInfo = last;
return templateInfo;
if (wasPreBound || !this.__templateInfo) {
this.__templateInfo = templateInfo;
} else {
const parent = templateInfo.parent || this.__templateInfo;
const previous = parent.lastChild;
parent.lastChild = templateInfo;
templateInfo.previousSibling = previous;
if (previous) {
previous.nextSibling = templateInfo;
} else {
parent.firstChild = templateInfo;
}
}
} else {
this.__templateInfo = templateInfo;
}
return this.__templateInfo = templateInfo;
return templateInfo;
}
/**
@@ -2736,13 +2755,13 @@ export const PropertyEffects = dedupingMixin(superClass => {
* @override
* @protected
*/
_stampTemplate(template) {
_stampTemplate(template, templateInfo) {
templateInfo = templateInfo || /** @type {!TemplateInfo} */(this._bindTemplate(template, true));
// Ensures that created dom is `_enqueueClient`'d to this element so
// that it can be flushed on next call to `_flushProperties`
hostStack.beginHosting(this);
let dom = super._stampTemplate(template);
let dom = super._stampTemplate(template, templateInfo);
hostStack.endHosting(this);
let templateInfo = /** @type {!TemplateInfo} */(this._bindTemplate(template, true));
// Add template-instance-specific data to instanced templateInfo
templateInfo.nodeList = dom.nodeList;
// Capture child nodes to allow unstamping of non-prototypical templates
@@ -2757,8 +2776,7 @@ export const PropertyEffects = dedupingMixin(superClass => {
setupBindings(this, templateInfo);
// Flush properties into template nodes if already booted
if (this.__dataReady) {
runEffects(this, templateInfo.propertyEffects, this.__data, null,
false, templateInfo.nodeList);
this._runEffectsForTemplate(templateInfo, this.__data, null, false);
}
return dom;
}
@@ -2776,23 +2794,22 @@ export const PropertyEffects = dedupingMixin(superClass => {
_removeBoundDom(dom) {
// Unlink template info
let templateInfo = dom.templateInfo;
if (templateInfo.previousTemplateInfo) {
templateInfo.previousTemplateInfo.nextTemplateInfo =
templateInfo.nextTemplateInfo;
const {previousSibling, nextSibling, parent} = templateInfo;
if (previousSibling) {
previousSibling.nextSibling = nextSibling;
} else if (parent) {
parent.firstChild = nextSibling;
}
if (templateInfo.nextTemplateInfo) {
templateInfo.nextTemplateInfo.previousTemplateInfo =
templateInfo.previousTemplateInfo;
if (nextSibling) {
nextSibling.previousSibling = previousSibling;
} else if (parent) {
parent.lastChild = previousSibling;
}
if (this.__templateInfoLast == templateInfo) {
this.__templateInfoLast = templateInfo.previousTemplateInfo;
}
templateInfo.previousTemplateInfo = templateInfo.nextTemplateInfo = null;
// Remove stamped nodes
let nodes = templateInfo.childNodes;
for (let i=0; i<nodes.length; i++) {
let node = nodes[i];
node.parentNode.removeChild(node);
wrap(wrap(node).parentNode).removeChild(node);
}
}
@@ -2923,18 +2940,33 @@ export const PropertyEffects = dedupingMixin(superClass => {
this, node, templateInfo, nodeInfo);
const parent = node.parentNode;
const nestedTemplateInfo = nodeInfo.templateInfo;
if (btp && parent.localName.match(/(dom-repeat|dom-if)/)) {
const isDomIf = parent.localName === 'dom-if';
const isDomRepeat = parent.localName === 'dom-repeat';
// Remove nested template and redirect its host bindings & templateInfo
// onto the parent (dom-if/repeat element)'s nodeInfo
if (removeNestedTemplates && (isDomIf || isDomRepeat)) {
parent.removeChild(node);
nodeInfo.parentInfo.templateInfo = nestedTemplateInfo;
nodeInfo = nodeInfo.parentInfo;
nodeInfo.noted = true;
noted = false;
}
// Merge host props into outer template and add bindings
let hostProps = nestedTemplateInfo.hostProps;
let mode = '{';
for (let source in hostProps) {
let parts = [{ mode, source, dependencies: [source], hostProp: true }];
addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
if (fastDomIf && isDomIf) {
if (hostProps) {
if (!removeNestedTemplates) {
nodeInfo.parentInfo.noted = true;
}
templateInfo.hostProps =
Object.assign(nestedTemplateInfo.hostProps || {}, hostProps);
}
} else {
let mode = '{';
for (let source in hostProps) {
let parts = [{ mode, source, dependencies: [source], hostProp: true }];
addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
}
}
return noted;
}

View File

@@ -72,9 +72,11 @@ function applyEventListener(inst, node, nodeInfo) {
}
// push configuration references at configure time
function applyTemplateContent(inst, node, nodeInfo) {
function applyTemplateContent(inst, node, nodeInfo, parentTemplateInfo) {
if (nodeInfo.templateInfo) {
node._templateInfo = nodeInfo.templateInfo;
// Give the node an instance of this templateInfo and set its parent
node._templateInfo = Object.create(nodeInfo.templateInfo);
node._templateInfo.parent = parentTemplateInfo;
}
}
@@ -255,7 +257,7 @@ export const TemplateStamp = dedupingMixin(
if (element.hasAttributes && element.hasAttributes()) {
noted = this._parseTemplateNodeAttributes(element, templateInfo, nodeInfo) || noted;
}
return noted;
return noted || nodeInfo.noted;
}
/**
@@ -437,13 +439,13 @@ export const TemplateStamp = dedupingMixin(
* @return {!StampedTemplate} Cloned template content
* @override
*/
_stampTemplate(template) {
_stampTemplate(template, templateInfo) {
// Polyfill support: bootstrap the template if it has not already been
if (template && !template.content &&
window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
HTMLTemplateElement.decorate(template);
}
let templateInfo = this.constructor._parseTemplate(template);
templateInfo = templateInfo || this.constructor._parseTemplate(template);
let nodeInfo = templateInfo.nodeInfoList;
let content = templateInfo.content || template.content;
let dom = /** @type {DocumentFragment} */ (document.importNode(content, true));
@@ -454,7 +456,7 @@ export const TemplateStamp = dedupingMixin(
for (let i=0, l=nodeInfo.length, info; (i<l) && (info=nodeInfo[i]); i++) {
let node = nodes[i] = findTemplateNode(dom, info);
applyIdToMap(this, dom.$, node, info);
applyTemplateContent(this, node, info);
applyTemplateContent(this, node, info, templateInfo);
applyEventListener(this, node, info);
}
dom = /** @type {!StampedTemplate} */(dom); // eslint-disable-line no-self-assign

View File

@@ -263,3 +263,43 @@ export let cancelSyntheticClickEvents = true;
export const setCancelSyntheticClickEvents = function(useCancelSyntheticClickEvents) {
cancelSyntheticClickEvents = useCancelSyntheticClickEvents;
};
/**
* Setting to remove nested templates inside `dom-if` and `dom-repeat` as
* part of element template parsing. This is a performance optimization that
* eliminates most of the tax of needing two elements due to the loss of
* type-extended templates as a result of the V1 specification changes.
*/
export let removeNestedTemplates = true;
/**
* Sets `removeNestedTemplates` globally, to eliminate nested templates
* inside `dom-if` and `dom-repeat` as part of template parsing.
*
* @param {boolean} useCemoveNestedTemplates enable or disable removing nested
* templates during parsing
* @return {void}
*/
export const setRemoveNestedTemplates = function(useCemoveNestedTemplates) {
removeNestedTemplates = useCemoveNestedTemplates;
};
/**
* Setting to place `dom-if` elements in a performance-optimized mode that takes
* advantage of lighter-weight host runtime template stamping to eliminate the
* need for an intermediate Templatizer `TemplateInstance` to mange the nodes
* stamped by `dom-if`. Under this setting, any Templatizer-provided API's
* such as `modelForElement` will not be available for nodes stamped by
* `dom-if`.
*/
export let fastDomIf = true;
/**
* Sets `fastDomIf` globally, to put `dom-if` in a performance-optimized mode.
*
* @param {boolean} useFastDomIf enable or disable `dom-if` fast-mode
* @return {void}
*/
export const setFastDomIf = function(useFastDomIf) {
fastDomIf = useFastDomIf;
};

View File

@@ -103,6 +103,48 @@ function upgradeTemplate(template, constructor) {
*/
const templateInstanceBase = PropertyEffects(class {});
export function showHideChildren(hide, children) {
for (let i=0; i<children.length; i++) {
let n = children[i];
// Ignore non-changes
if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
// clear and restore text
if (n.nodeType === Node.TEXT_NODE) {
if (hide) {
n.__polymerTextContent__ = n.textContent;
n.textContent = '';
} else {
n.textContent = n.__polymerTextContent__;
}
// remove and replace slot
} else if (n.localName === 'slot') {
if (hide) {
n.__polymerReplaced__ = document.createComment('hidden-slot');
wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n);
} else {
const replace = n.__polymerReplaced__;
if (replace) {
wrap(wrap(replace).parentNode).replaceChild(n, replace);
}
}
}
// hide and show nodes
else if (n.style) {
if (hide) {
n.__polymerDisplay__ = n.style.display;
n.style.display = 'none';
} else {
n.style.display = n.__polymerDisplay__;
}
}
}
n.__hideTemplateChildren__ = hide;
if (n._showHideChildren) {
n._showHideChildren(hide);
}
}
}
/**
* @polymer
* @customElement
@@ -206,45 +248,7 @@ class TemplateInstanceBase extends templateInstanceBase {
* @protected
*/
_showHideChildren(hide) {
let c = this.children;
for (let i=0; i<c.length; i++) {
let n = c[i];
// Ignore non-changes
if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
if (n.nodeType === Node.TEXT_NODE) {
if (hide) {
n.__polymerTextContent__ = n.textContent;
n.textContent = '';
} else {
n.textContent = n.__polymerTextContent__;
}
// remove and replace slot
} else if (n.localName === 'slot') {
if (hide) {
n.__polymerReplaced__ = document.createComment('hidden-slot');
wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n);
} else {
const replace = n.__polymerReplaced__;
if (replace) {
wrap(wrap(replace).parentNode).replaceChild(n, replace);
}
}
}
else if (n.style) {
if (hide) {
n.__polymerDisplay__ = n.style.display;
n.style.display = 'none';
} else {
n.style.display = n.__polymerDisplay__;
}
}
}
n.__hideTemplateChildren__ = hide;
if (n._showHideChildren) {
n._showHideChildren(hide);
}
}
showHideChildren(hide, this.children);
}
/**
* Overrides default property-effects implementation to intercept
@@ -365,10 +369,14 @@ function createTemplatizerClass(template, templateInfo, options) {
*
* @suppress {missingProperties} class.prototype is not defined for some reason
*/
function addPropagateEffects(template, templateInfo, options, methodHost) {
function addPropagateEffects(target, templateInfo, options, methodHost) {
let userForwardHostProp = options.forwardHostProp;
if (userForwardHostProp && templateInfo.hasHostProps) {
const isTemplate = template.localName == 'template';
// Under the `removeNestedTemplates` optimization, a custom element like
// `dom-if` or `dom-repeat` can itself be treated as the "template"; this
// flag is used to switch between upgrading a `<template>` to be a property
// effects client vs. adding the effects directly to the custom element
const isTemplate = target.localName == 'template';
// Provide data API and property effects on memoized template class
let klass = templateInfo.templatizeTemplateClass;
if (!klass) {
@@ -382,7 +390,9 @@ function addPropagateEffects(template, templateInfo, options, methodHost) {
klass = templateInfo.templatizeTemplateClass =
class TemplatizedTemplate extends templatizedBase {};
} else {
klass = class TemplatizedTemplateExtension extends template.constructor {};
// Create a cached subclass of the base custom element class onto which
// to put the template-specific propagate effects
klass = class TemplatizedTemplateExtension extends target.constructor {};
}
// Add template - >instances effects
// and host <- template effects
@@ -398,21 +408,31 @@ function addPropagateEffects(template, templateInfo, options, methodHost) {
}
}
if (isTemplate) {
upgradeTemplate(template, klass);
upgradeTemplate(target, klass);
// Clear any pending data for performance
template.__dataTemp = {};
template.__dataPending = null;
template.__dataOld = null;
template._enableProperties();
target.__dataTemp = {};
target.__dataPending = null;
target.__dataOld = null;
target._enableProperties();
} else {
Object.setPrototypeOf(template, klass.prototype);
// Swizzle the cached subclass prototype onto the custom element
Object.setPrototypeOf(target, klass.prototype);
const hostProps = templateInfo.hostProps;
for (let prop in hostProps) {
prop = '_host_' + prop;
if (prop in target) {
const val = target[prop];
delete target[prop];
target.__data[prop] = val;
}
}
}
// Mix any pre-bound data into __data; no need to flush this to
// instances since they pull from the template at instance-time
if (template.__dataProto) {
if (target.__dataProto) {
// Note, generally `__dataProto` could be chained, but it's guaranteed
// to not be since this is a vanilla template we just added effects to
Object.assign(template.__data, template.__dataProto);
Object.assign(target.__data, target.__dataProto);
}
}
}
@@ -632,7 +652,7 @@ export function modelForElement(template, node) {
// An element with a __templatizeInstance marks the top boundary
// of a scope; walk up until we find one, and then ensure that
// its __dataHost matches `this`, meaning this dom-repeat stamped it
if ((model = node.__templatizeInstance)) {
if ((model = node.__dataHost ? node : node.__templatizeInstance)) {
// Found an element stamped by another template; keep walking up
// from its __dataHost
if (model.__dataHost != template) {

View File

@@ -71,7 +71,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
'unit/path.html',
'unit/templatize.html',
'unit/dom-repeat.html',
'unit/dom-repeat.html?removeNestedTemplates=true',
'unit/dom-if.html',
'unit/dom-if.html?removeNestedTemplates=true',
'unit/dom-if.html?fastDomIf=true',
'unit/dom-if.html?removeNestedTemplates=true&fastDomIf=true',
'unit/dom-bind.html',
'unit/array-selector.html',
'unit/polymer-dom.html',

View File

@@ -14,6 +14,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script src="wct-browser-config.js"></script>
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
<script type="module">
import { setRemoveNestedTemplates, setFastDomIf } from '../../lib/utils/settings.js';
setRemoveNestedTemplates(location.search.match(/removeNestedTemplates/));
setFastDomIf(location.search.match(/fastDomIf/));
</script>
<script type="module" src="../../polymer-legacy.js"></script>
<script type="module" src="./dom-if-elements.js"></script>
</head>

View File

@@ -464,9 +464,9 @@ Polymer({
Polymer({
_template: html`
<template is="dom-repeat" items="{{items}}" id="outer">
<template is="dom-if" if="">
<template is="dom-if" if="" name="outerIf">
<template is="dom-repeat" items="{{item.items}}" id="inner">
<template is="dom-if" if="">
<template is="dom-if" if="" name="innerIf">
<button on-click="handleClick">{{item.prop}}</button>
</template>
</template>

View File

@@ -14,6 +14,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script src="wct-browser-config.js"></script>
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
<script type="module">
import { setRemoveNestedTemplates } from '../../lib/utils/settings.js';
setRemoveNestedTemplates(location.search.match(/removeNestedTemplates/));
</script>
<script type="module" src="../../polymer-legacy.js"></script>
<script type="module" src="./dom-repeat-elements.js"></script>
<style>
@@ -108,7 +112,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</dom-repeat>
</div>
<test-fixture id="primitiveLarge">
<template>
<x-primitive-large></x-primitive-large>