3
votes

I'm trying to get familiar with polymer, and have been working my way through trying to build a sample application with it. Following the tutorials at https://www.dartlang.org/docs/tutorials/polymer-intro and reading other StackOverflow questions such as How do I fire a custom event from Polymer Dart? I have managed to build two elements where one element fires an event that is acted upon by the second element. I have, however, only been able to figure out how to do this for cases where the firing element is a child of the listening element. For example, as follows

foo.html

<link rel="import" href="bar.html">
<polymer-element name="foo">
    <template>
        <my-bar on-myevent="{{react}}"></mybar>
    </template>
    <script type="application/dart" src="foo.dart"></script>
</polymer-element>

foo.dart

@CustomTag('my-foo')
class Foo extends PolymerElement {

  Foo() {}
  Foo.created() : super.created();

  void react() {
    print("Event happened and was heard!");
  }
}

bar.html

<polymer-element name="bar">
    <template>
        <button on-click="{{click}}"></button>
    </template>
    <script type="application/dart" src="bar.dart"></script>
</polymer-element>

bar.dart

@CustomTag('my-bar')
class Bar extends PolymerElement {

  Bar() {}
  Bar.created() : super.created();

  void click(Event e, var details, Node node) {
    print("Triggering event");
    fire('my-event');
  }
}

index.html

<!DOCTYPE html>
<html>
<head>
    <script type="application/dart" src="main.dart"></script>
    <script src="packages/browser/dart.js"></script>
    <link rel="import" href="foo.html">
</head>
<body>

<div id="content">
    <foo></foo>
</div>

</body>
</html>

What I'd like to do is be able to move the bar button outside of the foo element, as in the app that I want to design, I would like the input controls to exist is a separate area of the page from the primary output display (the foo element). Basically, I'd like to be able to make foo.html and index.html look like this:

foo.html

<polymer-element name="foo">
    <template>
        <!-- Databound output stuff here -->
    </template>
    <script type="application/dart" src="foo.dart"></script>
</polymer-element>

index.html

<!DOCTYPE html>
<html>
<head>
    <script type="application/dart" src="main.dart"></script>
    <script src="packages/browser/dart.js"></script>
    <link rel="import" href="foo.html">
    <link rel="import" href="bar.html">
</head>
<body>

<div id='control-panel'>
    <bar></bar>
</div>
<div id="content">
    <foo></foo>
</div>

</body>
</html>

I can't seem to find any examples on how, if I move the bar button out of the foo element, to get the event from bar to be visible to foo. What would be the best way to listen to the event from bar when it is not a child of foo?

3

3 Answers

5
votes

Polymer is designed around the idea of controllers. By this I mean, when A and B need to have some interaction, the preferred architecture is to create a third entity C. C can manage the lifecycle of A and B, and orchestrate communication. This kind of structure is excellent for maintainability, because users of C are insulated from the particulars of A and B, and A and B are insulated from everything. It also provides a convenient spot for impedance matching between A and B.

<polymer-element name="my-c">
 <template>
  <my-a on-my-event="{{coolAction}}"></my-a>
  ...
  <my-b id="b"></my-b>
 </template>
 <script>
   Polymer({
    coolAction: function() {
      this.$.b.doCoolThing();
    }
   });
 </script>
</polymer-element>

Users new to component structures often perceive this set-up as inconvenient, but my experience is that it's extraordinarily helpful once you get used to it.

Ultimately your view is a graph of components, where any particular sub-graph can generally be snipped off, replaced, or reattached with a minimum of stress. This is great for building flexible applications.

The signals concept exists for the rare occasion where you really want A and B to talk to each other directly, cutting across the component graph. This ability is almost never needed with a good application design, and should be avoided if possible.

3
votes

Since events bubble, the easiest way to handle this is to listen to a common ancestor of both elements.

Let's say you have this structure:

<div id="event-bus">
  <my-a></my-a>
  <my-b></my-b>
</div>

Then <my-b> can listen for my-event on #event-bus.

To help ` find the event bus, you could set an attribute:

<div id="event-bus">
  <my-a></my-a>
  <my-b event-bus="event-bus"></my-b>
</div>
class MyB extends PolymerElement {

  @published String eventBus;

  ready() {
    querySelector('#$eventBus').on['my-event'].listen((e){ react(); } );
  }

  ...
}
1
votes

The <polymer-signals> element has been ported to Dart, which lets you fire a custom event anywhere, and listen to it anywhere.

Use it like <polymer-signals on-polymer-signalfoo="{{foo}}"></polymer-signals> where the event type you fire is polymer-signal and the data of the fire contains {'name': 'foo', 'data': <your-data>}

The port is here polymer_elements for dart.

A good explanation of events in general for the javascript polymer which also describes how the <polymer-signals> works is described communication and message passing.