How to query for an element inside a dom-repeat

Issue

I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.

sample.html

<dom-module id="so-sample>
  <style>...</style>
<template>
  <template is="dom-repeat" items="[[cars]] as="car>
    ...
    <paper-button on-click="buttonClicked">Button</paper-button>

    <paper-dialog id="dialog">
      <h2>Title</h2>
    </paper-dialog>
  </template>
</template>

sample.dart

I’ll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.

...
@reflectable
void buttonClicked(e, [_])
{
  PaperDialog infos = this.shadowRoot.querySelector("#dialog");

  infos.open();
}

This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined

I have tried several ‘solutions’, which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?

I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items ‘before’ they are added to the local DOM.

Another advice I didn’t follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.

My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)

Thanks for your time !

Update 1:

After reading Gunter’s advices, although none of them actually worked by themselves, the fact that the IDs aren’t mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !

sample.dart:

PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");

infos.open();

I now hope that each button will call the associated dialog, since I’ll bind data inside the dialog relative to the item I clicked ~

Update 2:

So, nope, the data binding didn’t work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only ‘workaround’ I found is to query all the paper-dialog into a list and then get the ‘index-th’ element from that list.

@reflectable
void buttonClicked(e, [_])
{
  var model = new DomRepeatModel.fromEvent(e);
  List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
  dialogs[model.index].open();
}

This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.

So yeah, my initial problem is solved, but I still wonder why I couldn’t query the dialogs from their id:

...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
@reflectable
void buttonClicked(e, [_])
{
  var model = new DomRepeatModel.fromEvent(e);
  PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
  dialog.open();
}

With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.

Solution

You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.

PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")

Answered By – Günter Zöchbauer

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *