1 | /**
|
2 | @license
|
3 | Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
4 | This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
5 | The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
6 | The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
7 | Code distributed by Google as part of the polymer project is also
|
8 | subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
9 | */
|
10 | import { PolymerElement } from '../../polymer-element.js';
|
11 |
|
12 | import { Debouncer } from '../utils/debounce.js';
|
13 | import { enqueueDebouncer, flush } from '../utils/flush.js';
|
14 | import { microTask } from '../utils/async.js';
|
15 | import { root } from '../utils/path.js';
|
16 | import { wrap } from '../utils/wrap.js';
|
17 | import { hideElementsGlobally } from '../utils/hide-template-controls.js';
|
18 | import { fastDomIf, strictTemplatePolicy, suppressTemplateNotifications } from '../utils/settings.js';
|
19 | import { showHideChildren, templatize } from '../utils/templatize.js';
|
20 |
|
21 | /**
|
22 | * @customElement
|
23 | * @polymer
|
24 | * @extends PolymerElement
|
25 | * @summary Base class for dom-if element; subclassed into concrete
|
26 | * implementation.
|
27 | */
|
28 | class DomIfBase extends PolymerElement {
|
29 |
|
30 | // Not needed to find template; can be removed once the analyzer
|
31 | // can find the tag name from customElements.define call
|
32 | static get is() { return 'dom-if'; }
|
33 |
|
34 | static get template() { return null; }
|
35 |
|
36 | static get properties() {
|
37 |
|
38 | return {
|
39 |
|
40 | /**
|
41 | * Fired whenever DOM is added or removed/hidden by this template (by
|
42 | * default, rendering occurs lazily). To force immediate rendering, call
|
43 | * `render`.
|
44 | *
|
45 | * @event dom-change
|
46 | */
|
47 |
|
48 | /**
|
49 | * A boolean indicating whether this template should stamp.
|
50 | */
|
51 | if: {
|
52 | type: Boolean,
|
53 | observer: '__debounceRender'
|
54 | },
|
55 |
|
56 | /**
|
57 | * When true, elements will be removed from DOM and discarded when `if`
|
58 | * becomes false and re-created and added back to the DOM when `if`
|
59 | * becomes true. By default, stamped elements will be hidden but left
|
60 | * in the DOM when `if` becomes false, which is generally results
|
61 | * in better performance.
|
62 | */
|
63 | restamp: {
|
64 | type: Boolean,
|
65 | observer: '__debounceRender'
|
66 | },
|
67 |
|
68 | /**
|
69 | * When the global `suppressTemplateNotifications` setting is used, setting
|
70 | * `notifyDomChange: true` will enable firing `dom-change` events on this
|
71 | * element.
|
72 | */
|
73 | notifyDomChange: {
|
74 | type: Boolean
|
75 | }
|
76 | };
|
77 |
|
78 | }
|
79 |
|
80 | constructor() {
|
81 | super();
|
82 | this.__renderDebouncer = null;
|
83 | this._lastIf = false;
|
84 | this.__hideTemplateChildren__ = false;
|
85 | /** @type {!HTMLTemplateElement|undefined} */
|
86 | this.__template;
|
87 | /** @type {!TemplateInfo|undefined} */
|
88 | this._templateInfo;
|
89 | }
|
90 |
|
91 | __debounceRender() {
|
92 | // Render is async for 2 reasons:
|
93 | // 1. To eliminate dom creation trashing if user code thrashes `if` in the
|
94 | // same turn. This was more common in 1.x where a compound computed
|
95 | // property could result in the result changing multiple times, but is
|
96 | // mitigated to a large extent by batched property processing in 2.x.
|
97 | // 2. To avoid double object propagation when a bag including values bound
|
98 | // to the `if` property as well as one or more hostProps could enqueue
|
99 | // the <dom-if> to flush before the <template>'s host property
|
100 | // forwarding. In that scenario creating an instance would result in
|
101 | // the host props being set once, and then the enqueued changes on the
|
102 | // template would set properties a second time, potentially causing an
|
103 | // object to be set to an instance more than once. Creating the
|
104 | // instance async from flushing data ensures this doesn't happen. If
|
105 | // we wanted a sync option in the future, simply having <dom-if> flush
|
106 | // (or clear) its template's pending host properties before creating
|
107 | // the instance would also avoid the problem.
|
108 | this.__renderDebouncer = Debouncer.debounce(
|
109 | this.__renderDebouncer
|
110 | , microTask
|
111 | , () => this.__render());
|
112 | enqueueDebouncer(this.__renderDebouncer);
|
113 | }
|
114 |
|
115 | /**
|
116 | * @override
|
117 | * @return {void}
|
118 | */
|
119 | disconnectedCallback() {
|
120 | super.disconnectedCallback();
|
121 | const parent = wrap(this).parentNode;
|
122 | if (!parent || (parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE &&
|
123 | !wrap(parent).host)) {
|
124 | this.__teardownInstance();
|
125 | }
|
126 | }
|
127 |
|
128 | /**
|
129 | * @override
|
130 | * @return {void}
|
131 | */
|
132 | connectedCallback() {
|
133 | super.connectedCallback();
|
134 | if (!hideElementsGlobally()) {
|
135 | this.style.display = 'none';
|
136 | }
|
137 | if (this.if) {
|
138 | this.__debounceRender();
|
139 | }
|
140 | }
|
141 |
|
142 | /**
|
143 | * Ensures a template has been assigned to `this.__template`. If it has not
|
144 | * yet been, it querySelectors for it in its children and if it does not yet
|
145 | * exist (e.g. in parser-generated case), opens a mutation observer and
|
146 | * waits for it to appear (returns false if it has not yet been found,
|
147 | * otherwise true). In the `removeNestedTemplates` case, the "template" will
|
148 | * be the `dom-if` element itself.
|
149 | *
|
150 | * @return {boolean} True when a template has been found, false otherwise
|
151 | */
|
152 | __ensureTemplate() {
|
153 | if (!this.__template) {
|
154 | // When `removeNestedTemplates` is true, the "template" is the element
|
155 | // itself, which has been given a `_templateInfo` property
|
156 | const thisAsTemplate = /** @type {!HTMLTemplateElement} */ (
|
157 | /** @type {!HTMLElement} */ (this));
|
158 | let template = thisAsTemplate._templateInfo ?
|
159 | thisAsTemplate :
|
160 | /** @type {!HTMLTemplateElement} */
|
161 | (wrap(thisAsTemplate).querySelector('template'));
|
162 | if (!template) {
|
163 | // Wait until childList changes and template should be there by then
|
164 | let observer = new MutationObserver(() => {
|
165 | if (wrap(this).querySelector('template')) {
|
166 | observer.disconnect();
|
167 | this.__render();
|
168 | } else {
|
169 | throw new Error('dom-if requires a <template> child');
|
170 | }
|
171 | });
|
172 | observer.observe(this, {childList: true});
|
173 | return false;
|
174 | }
|
175 | this.__template = template;
|
176 | }
|
177 | return true;
|
178 | }
|
179 |
|
180 | /**
|
181 | * Ensures a an instance of the template has been created and inserted. This
|
182 | * method may return false if the template has not yet been found or if
|
183 | * there is no `parentNode` to insert the template into (in either case,
|
184 | * connection or the template-finding mutation observer firing will queue
|
185 | * another render, causing this method to be called again at a more
|
186 | * appropriate time).
|
187 | *
|
188 | * Subclasses should implement the following methods called here:
|
189 | * - `__hasInstance`
|
190 | * - `__createAndInsertInstance`
|
191 | * - `__getInstanceNodes`
|
192 | *
|
193 | * @return {boolean} True if the instance was created, false otherwise.
|
194 | */
|
195 | __ensureInstance() {
|
196 | let parentNode = wrap(this).parentNode;
|
197 | if (!this.__hasInstance()) {
|
198 | // Guard against element being detached while render was queued
|
199 | if (!parentNode) {
|
200 | return false;
|
201 | }
|
202 | // Find the template (when false, there was no template yet)
|
203 | if (!this.__ensureTemplate()) {
|
204 | return false;
|
205 | }
|
206 | this.__createAndInsertInstance(parentNode);
|
207 | } else {
|
208 | // Move instance children if necessary
|
209 | let children = this.__getInstanceNodes();
|
210 | if (children && children.length) {
|
211 | // Detect case where dom-if was re-attached in new position
|
212 | let lastChild = wrap(this).previousSibling;
|
213 | if (lastChild !== children[children.length-1]) {
|
214 | for (let i=0, n; (i<children.length) && (n=children[i]); i++) {
|
215 | wrap(parentNode).insertBefore(n, this);
|
216 | }
|
217 | }
|
218 | }
|
219 | }
|
220 | return true;
|
221 | }
|
222 |
|
223 | /**
|
224 | * Forces the element to render its content. Normally rendering is
|
225 | * asynchronous to a provoking change. This is done for efficiency so
|
226 | * that multiple changes trigger only a single render. The render method
|
227 | * should be called if, for example, template rendering is required to
|
228 | * validate application state.
|
229 | *
|
230 | * @return {void}
|
231 | */
|
232 | render() {
|
233 | flush();
|
234 | }
|
235 |
|
236 | /**
|
237 | * Performs the key rendering steps:
|
238 | * 1. Ensure a template instance has been stamped (when true)
|
239 | * 2. Remove the template instance (when false and restamp:true)
|
240 | * 3. Sync the hidden state of the instance nodes with the if/restamp state
|
241 | * 4. Fires the `dom-change` event when necessary
|
242 | *
|
243 | * @return {void}
|
244 | */
|
245 | __render() {
|
246 | if (this.if) {
|
247 | if (!this.__ensureInstance()) {
|
248 | // No template found yet
|
249 | return;
|
250 | }
|
251 | } else if (this.restamp) {
|
252 | this.__teardownInstance();
|
253 | }
|
254 | this._showHideChildren();
|
255 | if ((!suppressTemplateNotifications || this.notifyDomChange)
|
256 | && this.if != this._lastIf) {
|
257 | this.dispatchEvent(new CustomEvent('dom-change', {
|
258 | bubbles: true,
|
259 | composed: true
|
260 | }));
|
261 | this._lastIf = this.if;
|
262 | }
|
263 | }
|
264 |
|
265 | // Ideally these would be annotated as abstract methods in an abstract class,
|
266 | // but closure compiler is finnicky
|
267 | /* eslint-disable valid-jsdoc */
|
268 | /**
|
269 | * Abstract API to be implemented by subclass: Returns true if a template
|
270 | * instance has been created and inserted.
|
271 | *
|
272 | * @protected
|
273 | * @return {boolean} True when an instance has been created.
|
274 | */
|
275 | __hasInstance() { }
|
276 |
|
277 | /**
|
278 | * Abstract API to be implemented by subclass: Returns the child nodes stamped
|
279 | * from a template instance.
|
280 | *
|
281 | * @protected
|
282 | * @return {Array<Node>} Array of child nodes stamped from the template
|
283 | * instance.
|
284 | */
|
285 | __getInstanceNodes() { }
|
286 |
|
287 | /**
|
288 | * Abstract API to be implemented by subclass: Creates an instance of the
|
289 | * template and inserts it into the given parent node.
|
290 | *
|
291 | * @protected
|
292 | * @param {Node} parentNode The parent node to insert the instance into
|
293 | * @return {void}
|
294 | */
|
295 | __createAndInsertInstance(parentNode) { } // eslint-disable-line no-unused-vars
|
296 |
|
297 | /**
|
298 | * Abstract API to be implemented by subclass: Removes nodes created by an
|
299 | * instance of a template and any associated cleanup.
|
300 | *
|
301 | * @protected
|
302 | * @return {void}
|
303 | */
|
304 | __teardownInstance() { }
|
305 |
|
306 | /**
|
307 | * Abstract API to be implemented by subclass: Shows or hides any template
|
308 | * instance childNodes based on the `if` state of the element and its
|
309 | * `__hideTemplateChildren__` property.
|
310 | *
|
311 | * @protected
|
312 | * @return {void}
|
313 | */
|
314 | _showHideChildren() { }
|
315 | /* eslint-enable valid-jsdoc */
|
316 | }
|
317 |
|
318 | /**
|
319 | * The version of DomIf used when `fastDomIf` setting is in use, which is
|
320 | * optimized for first-render (but adds a tax to all subsequent property updates
|
321 | * on the host, whether they were used in a given `dom-if` or not).
|
322 | *
|
323 | * This implementation avoids use of `Templatizer`, which introduces a new scope
|
324 | * (a non-element PropertyEffects instance), which is not strictly necessary
|
325 | * since `dom-if` never introduces new properties to its scope (unlike
|
326 | * `dom-repeat`). Taking advantage of this fact, the `dom-if` reaches up to its
|
327 | * `__dataHost` and stamps the template directly from the host using the host's
|
328 | * runtime `_stampTemplate` API, which binds the property effects of the
|
329 | * template directly to the host. This both avoids the intermediary
|
330 | * `Templatizer` instance, but also avoids the need to bind host properties to
|
331 | * the `<template>` element and forward those into the template instance.
|
332 | *
|
333 | * In this version of `dom-if`, the `this.__instance` method is the
|
334 | * `DocumentFragment` returned from `_stampTemplate`, which also serves as the
|
335 | * handle for later removing it using the `_removeBoundDom` method.
|
336 | */
|
337 | class DomIfFast extends DomIfBase {
|
338 |
|
339 | constructor() {
|
340 | super();
|
341 | this.__instance = null;
|
342 | this.__syncInfo = null;
|
343 | }
|
344 |
|
345 | /**
|
346 | * Implementation of abstract API needed by DomIfBase.
|
347 | *
|
348 | * @override
|
349 | * @return {boolean} True when an instance has been created.
|
350 | */
|
351 | __hasInstance() {
|
352 | return Boolean(this.__instance);
|
353 | }
|
354 |
|
355 | /**
|
356 | * Implementation of abstract API needed by DomIfBase.
|
357 | *
|
358 | * @override
|
359 | * @return {Array<Node>} Array of child nodes stamped from the template
|
360 | * instance.
|
361 | */
|
362 | __getInstanceNodes() {
|
363 | return this.__instance.templateInfo.childNodes;
|
364 | }
|
365 |
|
366 | /**
|
367 | * Implementation of abstract API needed by DomIfBase.
|
368 | *
|
369 | * Stamps the template by calling `_stampTemplate` on the `__dataHost` of this
|
370 | * element and then inserts the resulting nodes into the given `parentNode`.
|
371 | *
|
372 | * @override
|
373 | * @param {Node} parentNode The parent node to insert the instance into
|
374 | * @return {void}
|
375 | */
|
376 | __createAndInsertInstance(parentNode) {
|
377 | const host = this.__dataHost || this;
|
378 | if (strictTemplatePolicy) {
|
379 | if (!this.__dataHost) {
|
380 | throw new Error('strictTemplatePolicy: template owner not trusted');
|
381 | }
|
382 | }
|
383 | // Pre-bind and link the template into the effects system
|
384 | const templateInfo = host._bindTemplate(
|
385 | /** @type {!HTMLTemplateElement} */ (this.__template), true);
|
386 | // Install runEffects hook that prevents running property effects
|
387 | // (and any nested template effects) when the `if` is false
|
388 | templateInfo.runEffects = (runEffects, changedProps, hasPaths) => {
|
389 | let syncInfo = this.__syncInfo;
|
390 | if (this.if) {
|
391 | // Mix any props that changed while the `if` was false into `changedProps`
|
392 | if (syncInfo) {
|
393 | // If there were properties received while the `if` was false, it is
|
394 | // important to sync the hidden state with the element _first_, so that
|
395 | // new bindings to e.g. `textContent` do not get stomped on by
|
396 | // pre-hidden values if `_showHideChildren` were to be called later at
|
397 | // the next render. Clearing `__invalidProps` here ensures
|
398 | // `_showHideChildren`'s call to `__syncHostProperties` no-ops, so
|
399 | // that we don't call `runEffects` more often than necessary.
|
400 | this.__syncInfo = null;
|
401 | this._showHideChildren();
|
402 | changedProps = Object.assign(syncInfo.changedProps, changedProps);
|
403 | }
|
404 | runEffects(changedProps, hasPaths);
|
405 | } else {
|
406 | // Accumulate any values changed while `if` was false, along with the
|
407 | // runEffects method to sync them, so that we can replay them once `if`
|
408 | // becomes true
|
409 | if (this.__instance) {
|
410 | if (!syncInfo) {
|
411 | syncInfo = this.__syncInfo = { runEffects, changedProps: {} };
|
412 | }
|
413 | if (hasPaths) {
|
414 | // Store root object of any paths; this will ensure direct bindings
|
415 | // like [[obj.foo]] bindings run after a `set('obj.foo', v)`, but
|
416 | // note that path notifications like `set('obj.foo.bar', v)` will
|
417 | // not propagate. Since batched path notifications are not
|
418 | // supported, we cannot simply accumulate path notifications. This
|
419 | // is equivalent to the non-fastDomIf case, which stores root(p) in
|
420 | // __invalidProps.
|
421 | for (const p in changedProps) {
|
422 | const rootProp = root(p);
|
423 | syncInfo.changedProps[rootProp] = this.__dataHost[rootProp];
|
424 | }
|
425 | } else {
|
426 | Object.assign(syncInfo.changedProps, changedProps);
|
427 | }
|
428 | }
|
429 | }
|
430 | };
|
431 | // Stamp the template, and set its DocumentFragment to the "instance"
|
432 | this.__instance = host._stampTemplate(
|
433 | /** @type {!HTMLTemplateElement} */ (this.__template), templateInfo);
|
434 | wrap(parentNode).insertBefore(this.__instance, this);
|
435 | }
|
436 |
|
437 | /**
|
438 | * Run effects for any properties that changed while the `if` was false.
|
439 | *
|
440 | * @return {void}
|
441 | */
|
442 | __syncHostProperties() {
|
443 | const syncInfo = this.__syncInfo;
|
444 | if (syncInfo) {
|
445 | this.__syncInfo = null;
|
446 | syncInfo.runEffects(syncInfo.changedProps, false);
|
447 | }
|
448 | }
|
449 |
|
450 | /**
|
451 | * Implementation of abstract API needed by DomIfBase.
|
452 | *
|
453 | * Remove the instance and any nodes it created. Uses the `__dataHost`'s
|
454 | * runtime `_removeBoundDom` method.
|
455 | *
|
456 | * @override
|
457 | * @return {void}
|
458 | */
|
459 | __teardownInstance() {
|
460 | const host = this.__dataHost || this;
|
461 | if (this.__instance) {
|
462 | host._removeBoundDom(this.__instance);
|
463 | this.__instance = null;
|
464 | this.__syncInfo = null;
|
465 | }
|
466 | }
|
467 |
|
468 | /**
|
469 | * Implementation of abstract API needed by DomIfBase.
|
470 | *
|
471 | * Shows or hides the template instance top level child nodes. For
|
472 | * text nodes, `textContent` is removed while "hidden" and replaced when
|
473 | * "shown."
|
474 | *
|
475 | * @override
|
476 | * @return {void}
|
477 | * @protected
|
478 | * @suppress {visibility}
|
479 | */
|
480 | _showHideChildren() {
|
481 | const hidden = this.__hideTemplateChildren__ || !this.if;
|
482 | if (this.__instance && Boolean(this.__instance.__hidden) !== hidden) {
|
483 | this.__instance.__hidden = hidden;
|
484 | showHideChildren(hidden, this.__instance.templateInfo.childNodes);
|
485 | }
|
486 | if (!hidden) {
|
487 | this.__syncHostProperties();
|
488 | }
|
489 | }
|
490 | }
|
491 |
|
492 | /**
|
493 | * The "legacy" implementation of `dom-if`, implemented using `Templatizer`.
|
494 | *
|
495 | * In this version, `this.__instance` is the `TemplateInstance` returned
|
496 | * from the templatized constructor.
|
497 | */
|
498 | class DomIfLegacy extends DomIfBase {
|
499 |
|
500 | constructor() {
|
501 | super();
|
502 | this.__ctor = null;
|
503 | this.__instance = null;
|
504 | this.__invalidProps = null;
|
505 | }
|
506 |
|
507 | /**
|
508 | * Implementation of abstract API needed by DomIfBase.
|
509 | *
|
510 | * @override
|
511 | * @return {boolean} True when an instance has been created.
|
512 | */
|
513 | __hasInstance() {
|
514 | return Boolean(this.__instance);
|
515 | }
|
516 |
|
517 | /**
|
518 | * Implementation of abstract API needed by DomIfBase.
|
519 | *
|
520 | * @override
|
521 | * @return {Array<Node>} Array of child nodes stamped from the template
|
522 | * instance.
|
523 | */
|
524 | __getInstanceNodes() {
|
525 | return this.__instance.children;
|
526 | }
|
527 |
|
528 | /**
|
529 | * Implementation of abstract API needed by DomIfBase.
|
530 | *
|
531 | * Stamps the template by creating a new instance of the templatized
|
532 | * constructor (which is created lazily if it does not yet exist), and then
|
533 | * inserts its resulting `root` doc fragment into the given `parentNode`.
|
534 | *
|
535 | * @override
|
536 | * @param {Node} parentNode The parent node to insert the instance into
|
537 | * @return {void}
|
538 | */
|
539 | __createAndInsertInstance(parentNode) {
|
540 | // Ensure we have an instance constructor
|
541 | if (!this.__ctor) {
|
542 | this.__ctor = templatize(
|
543 | /** @type {!HTMLTemplateElement} */ (this.__template), this, {
|
544 | // dom-if templatizer instances require `mutable: true`, as
|
545 | // `__syncHostProperties` relies on that behavior to sync objects
|
546 | mutableData: true,
|
547 | /**
|
548 | * @param {string} prop Property to forward
|
549 | * @param {*} value Value of property
|
550 | * @this {DomIfLegacy}
|
551 | */
|
552 | forwardHostProp: function(prop, value) {
|
553 | if (this.__instance) {
|
554 | if (this.if) {
|
555 | this.__instance.forwardHostProp(prop, value);
|
556 | } else {
|
557 | // If we have an instance but are squelching host property
|
558 | // forwarding due to if being false, note the invalidated
|
559 | // properties so `__syncHostProperties` can sync them the next
|
560 | // time `if` becomes true
|
561 | this.__invalidProps =
|
562 | this.__invalidProps || Object.create(null);
|
563 | this.__invalidProps[root(prop)] = true;
|
564 | }
|
565 | }
|
566 | }
|
567 | });
|
568 | }
|
569 | // Create and insert the instance
|
570 | this.__instance = new this.__ctor();
|
571 | wrap(parentNode).insertBefore(this.__instance.root, this);
|
572 | }
|
573 |
|
574 | /**
|
575 | * Implementation of abstract API needed by DomIfBase.
|
576 | *
|
577 | * Removes the instance and any nodes it created.
|
578 | *
|
579 | * @override
|
580 | * @return {void}
|
581 | */
|
582 | __teardownInstance() {
|
583 | if (this.__instance) {
|
584 | let c$ = this.__instance.children;
|
585 | if (c$ && c$.length) {
|
586 | // use first child parent, for case when dom-if may have been detached
|
587 | let parent = wrap(c$[0]).parentNode;
|
588 | // Instance children may be disconnected from parents when dom-if
|
589 | // detaches if a tree was innerHTML'ed
|
590 | if (parent) {
|
591 | parent = wrap(parent);
|
592 | for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
|
593 | parent.removeChild(n);
|
594 | }
|
595 | }
|
596 | }
|
597 | this.__invalidProps = null;
|
598 | this.__instance = null;
|
599 | }
|
600 | }
|
601 |
|
602 | /**
|
603 | * Forwards any properties that changed while the `if` was false into the
|
604 | * template instance and flushes it.
|
605 | *
|
606 | * @return {void}
|
607 | */
|
608 | __syncHostProperties() {
|
609 | let props = this.__invalidProps;
|
610 | if (props) {
|
611 | this.__invalidProps = null;
|
612 | for (let prop in props) {
|
613 | this.__instance._setPendingProperty(prop, this.__dataHost[prop]);
|
614 | }
|
615 | this.__instance._flushProperties();
|
616 | }
|
617 | }
|
618 |
|
619 | /**
|
620 | * Implementation of abstract API needed by DomIfBase.
|
621 | *
|
622 | * Shows or hides the template instance top level child elements. For
|
623 | * text nodes, `textContent` is removed while "hidden" and replaced when
|
624 | * "shown."
|
625 | *
|
626 | * @override
|
627 | * @protected
|
628 | * @return {void}
|
629 | * @suppress {visibility}
|
630 | */
|
631 | _showHideChildren() {
|
632 | const hidden = this.__hideTemplateChildren__ || !this.if;
|
633 | if (this.__instance && Boolean(this.__instance.__hidden) !== hidden) {
|
634 | this.__instance.__hidden = hidden;
|
635 | this.__instance._showHideChildren(hidden);
|
636 | }
|
637 | if (!hidden) {
|
638 | this.__syncHostProperties();
|
639 | }
|
640 | }
|
641 | }
|
642 |
|
643 | /**
|
644 | * The `<dom-if>` element will stamp a light-dom `<template>` child when
|
645 | * the `if` property becomes truthy, and the template can use Polymer
|
646 | * data-binding and declarative event features when used in the context of
|
647 | * a Polymer element's template.
|
648 | *
|
649 | * When `if` becomes falsy, the stamped content is hidden but not
|
650 | * removed from dom. When `if` subsequently becomes truthy again, the content
|
651 | * is simply re-shown. This approach is used due to its favorable performance
|
652 | * characteristics: the expense of creating template content is paid only
|
653 | * once and lazily.
|
654 | *
|
655 | * Set the `restamp` property to true to force the stamped content to be
|
656 | * created / destroyed when the `if` condition changes.
|
657 | *
|
658 | * @customElement
|
659 | * @polymer
|
660 | * @extends DomIfBase
|
661 | * @constructor
|
662 | * @summary Custom element that conditionally stamps and hides or removes
|
663 | * template content based on a boolean flag.
|
664 | */
|
665 | export const DomIf = fastDomIf ? DomIfFast : DomIfLegacy;
|
666 |
|
667 | customElements.define(DomIf.is, DomIf);
|