How to access content of dom-if inside a custom element?
Asked Answered
S

4

5

In a custom element I want to access a span and append a child to it but all usual accessors give undefined:

<template>
    <template is="dom-if" if="[[condition]]" restamp>
        <span id="myspan"></span>
    </template>
</template>

 ready() {
   var a = this.$.myspan;                     //<------- is undefined
   var b = this.$$.myspan;                    //<------- is undefined
   var c = document.getElementById("myspan"); //<------- is undefined
   var d = this.$$("#myspan");                //<------- is undefined
}

How to access a span in this case?

UPDATE: here is plunk

Sausage answered 7/12, 2015 at 16:43 Comment(3)
The last one should work when condition is true, when condition is false none will work.Philps
@Günter Zöchbauer, doesn't work, here is a proof: plnkr.co/edit/cU3RK0Sausage
Seems Polymer needs some additional time to process dom-if. This works this.async(function () {console.log('this.$$("#myspan")',this.$$("#myspan"));}); (same as in Flavio Ochoa's answer).Philps
S
7

The reason this didn't work inside the lifecycle callback without setTimeout or this.async is that right after attaching your element the dom-if template has not yet rendered. Upon attaching your element, Polymer calls the attached callback. However, when the value gets set on the the dom-if, an observer runs and debounces its own _render function. The debounce waits an amount of time to catch any other calls to it, and then it executes the ._render function and attaches the element to the DOM. In other words, when the attached callback runs, normally the dom-if template hasn't rendered yet.

The reason for this debounce is performance. If several changes were made within a very short span of time, this debounce prevents the template from rendering several times when the result we would care about is the end result.

Fortunately, dom-if provides a .render() method which allows you to make it render synchronously. All you need to do is add an id to your dom-if, switch to an attached callback and call like this:

<template>
    <template id="someDomIf" is="dom-if" if="[[condition]]" restamp>
        <span id="myspan"></span>
    </template>
</template>

 attached() {
   this.$.someDomIf.render();
   var c = document.getElementById("myspan"); //<------- should be defined
   var d = this.$$("#myspan");                //<------- should be defined
}

Triggering a synchronous render on the dom-if shouldn't be a huge performance problem, since luckily your element should only be getting attached once. Edit: As it turns it, this even works in a ready callback:

<template>
    <template id="someDomIf" is="dom-if" if="[[condition]]" restamp>
        <span id="myspan"></span>
    </template>
</template>

 ready() {
   this.$.someDomIf.render();
   var c = document.getElementById("myspan"); //<------- should be defined
   var d = this.$$("#myspan");                //<------- should be defined
}

See this fork of your plunker: http://plnkr.co/edit/u3richtnt4COpEfx1CSN?p=preview

Samira answered 7/12, 2015 at 20:38 Comment(4)
according to the following conversation this.$.someDomIf.render(); doesn't guarantees that myspan element is ready.... github.com/Polymer/docs/issues/1456Sausage
@3141526 That's not what they're discussing. They're discussing the issue you encountered without using .render(); The local dom gets initialized before ready under normal circumstances, but that doesn't mean that the dom-if has rendered it's children yet. This is the purpose of the .render() call: to render the template synchronously instead of asynchronously, to ensure it's rendered when you needed.Samira
this.$.someDomIf.render(); only renders if and only if the condition is truthyWindshield
@HariPrasad correct. If the condition is falsey, then there is nothing to render.Samira
E
2

Try to do it asynchronously in the attached method as follows, this method works:

attached: function(){
   this.async(function(){
     var d = this.$$("#myspan"); 
     console.log(d); 
   },someTimeIfThereAreManyItemsToLoad);
}
Eye answered 7/12, 2015 at 19:22 Comment(4)
that works even without someTimeIfThereAreManyItemsToLoad. Any ideas why it doesn't work in ready(). And how to be sure that this.$$("#myspan") will always work even if "there are many items to load"Sausage
@Sausage See my answer below. There is a way to do it in an attached callback without using this.async.Samira
This answer is incredibly hackish? What if I'm running on a 486 machine (so to speak)? I think @Samira seems to be on the right track - however I haven't done much testing on whether .render() is truly synchronous.Handlebar
@Handlebar based on the dom-if source, it should be. github.com/Polymer/polymer/blob/master/src/lib/template/…Samira
S
0

The responses above only work if your condition is true initially. Please see my answer to your initial question that lead to this one : https://mcmap.net/q/2031716/-how-to-dynamically-append-an-element-to-dom-if-in-polymer

Not sure if you should mix in the .render-stuff from Dogs, but I still think the observer would be the right place for it as it otherwise does not work if condition is false initially.

Selfrenunciation answered 8/12, 2015 at 12:21 Comment(0)
N
0

As in Polymer documentation:

Note: Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash. The hash includes only statically created local DOM nodes (that is, the nodes defined in the element’s outermost template).

You will need to use this.$$('#yourElementId");

Ninnyhammer answered 1/4, 2016 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.