How do I add an Event Listener to a Dynamic Element?

Issue

I have the following HTML:

<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Description</th>
            <th>Actions</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>Item 1</td>
            <td>Description of Item 1</td>
            <td>
                <a href="#" data-action="edit" data-item-id="1">Edit</a>
                <a href="#" data-action="delete" data-item-id="1">Delete</a>
            </td>
        </tr>
        <tr>
            <td>2</td>
            <td>Item 2</td>
            <td>Description of Item 2</td>
            <td>
                <a href="#" data-action="edit" data-item-id="2">Edit</a>
                <a href="#" data-action="delete" data-item-id="2">Delete</a>
            </td>
        </tr>
    </tbody>
</table>

The table rows (tr elements) are added dynamically.

I wire up a click event to all Edit links like this:

void wireUpTableEvents() {
  var editLinks = queryAll('#order-items table tbody [data-action="edit"]');

  editLinks.forEach((element) {
    element.on.click.add((event){
      print(element.attributes['data-item-id']);
    });
  });
}

As said above, the table rows (tr elements) are added dynamically so the above code only works if I call wireUpEvents after I execute the method which adds the rows.

Does anyone know the syntax or adding a event listener to elements using DART‘s on.click.add() when the elements are dynamcially added in the future?

I tried checking the DART documentation but the documentation on Event Listeners is blank.

If I would be using jQuery I could be using something similar to:

$("#order-items table")on("click", "tbody [data-action="edit"]", function(){...})

…but I want to write my sample app only using DART.

Edit
Though future sounds great for callbacks it seemed slightly overkill for what I needed as there is no long running task in my scenario.

The closest I was able to get to attach my event listener to a static element but processing the click events of future sub-elements was this:

void wireUpTableEvents() {
    var tableBody = query('#order-items table tbody');

    // Attach Event Listener to the static tbody, which always exists.
    tableBody.on.click.add((event) {
        var clickedElement = event.srcElement;
        var itemId = clickedElement.attributes['data-item-id'];

        // Check if the clicked element was either one of the edit links or one of the delete links.
        switch (clickedElement.attributes['data-action']) {
        case 'edit':
            // Replace print with calling a method to process edit request for this item.
            print('processing edit click from item with id: $itemId');
            break;
        case 'delete':
            // Replace print with calling a method to process delete request for this item.
            print('processing delete click from item with id: $itemId');
            break;
        }
    });
}‚Äč

The above code can execute before any of the actual tr elements are loaded and still works after the tr elements are loaded at some unknown later stage.

I also found that it now covers any dynamically added row, pre-loaded ones as well as other dynamically added ones for new records etc.

Solution

It sounds like you need to use Dart’s Future object. John Evans has a recent post that gives an excellent overview. I’ll try to give a simple example:

Let’s say I have a class called htmlInDart which I call as follows:

void main() {
  var htmlExample = new HtmlInDart().createStyles();
  htmlExample
      ..then((htmlExample) => htmlExample.buildPage())
      ..then((htmlExample) => htmlExample.addListeners());
}

The class might look something like this:

class htmlInDart {

  htmlInDart();

  Future<htmlInDart> createStyles() {
    final c = new Completer();
    // create some styles
    c.complete(this);
    return c.future;
  }

  Future<htmlInDart> buildPage() {
    final c = new Completer();
    // build the page
    c.complete(this);
    return c.future;
  }

  Future<htmlInDart> addListeners() {
    final c = new Completer();
    // add some listeners
    c.complete(this);
    return c.future;
  }

Hopefully this gives you some idea of how to implement it for your case.

Answered By – scribeGriff

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.