I want to fire/send/emit a custom event from inside a Polymer element. For example, I want to convert a normal DOM event like "changed" to a more semantic event like "todoupdated".
This is the HTML that I have:
<polymer-element name="todo-item" extends="li" attributes="item">
<template>
<style>
label.done {
color: gray;
text-decoration: line-through;
}
</style>
<label class="checkbox {{item.doneClass}}">
<input type="checkbox" checked="{{item.done}}">
{{item.text}}
</label>
</template>
<script type="application/dart" src="todo_item.dart"></script>
</polymer-element>
I want the change events on checkbox to bubble out of the custom element as something more... useful. :)
Step 1
Capture the change events on the <input>. Notice the on-change below.
<!-- from inside todo_item.html -->
<input type="checkbox" checked="{{item.done}}" on-change="{{change}}">
Step 2
Handle the change event in the custom element code that contains the checkbox.
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'models.dart';
#CustomTag('todo-item')
class TodoItemElement extends PolymerElement with ObservableMixin {
#observable Item item;
bool get applyAuthorStyles => true;
void change(Event e, var details, Node target) {
// do stuff here
}
}
Notice the change event handler. That method is run any time the checkbox state changes.
Step 3
Dispatch a custom event.
void change(Event e, var details, Node target) {
dispatchEvent(new CustomEvent('todochange'));
}
NOTE: the custom event name must not contain dashes.
Step 4
Listen for the custom event in a parent custom element.
<template repeat="{{item in items}}" >
<li is="todo-item" class="{{item.doneClass}}" item="{{item}}" on-todochange="todoChanged"></li>
</template>
Notice the use of on-todochange.
Enjoy!
Polymer has a helper method that simplifies firing events
// dispatch a custom event
this.fire('polymer-select', detail: {'item': item, 'isSelected': isSelected});
Additional info:
To make the event available to subscriber that want to add a listener programmatically
// getter
async.Stream<dom.CustomEvent> get onPolymerSelect =>
PolymerSelection._onPolymerSelect.forTarget(this);
// private EventStreamProvider
static const dom.EventStreamProvider<dom.CustomEvent> _onPolymerSelect =
const dom.EventStreamProvider<dom.CustomEvent>('polymer-select');
subscribe to the event programmatically instead of declaratively:
($['#ps'] as PolymerSelect) // get the children and cast it to its actual type
.onPolymerSelect.listen((e) => print(e['isSelected'])); // subscribe
I managed this using <core-signals> and the polymer helper method fire. This way you are able to listen to events fired from elements that are not children. source.
todochange.html
<!doctype html>
<polymer-element name="todo-item" extends="li">
<template>
<style>
label.done {
color: gray;
text-decoration: line-through;
}
</style>
<label class="checkbox {{item.doneClass}}">
<input type="checkbox" checked="{{item.done}}">
{{item.text}}
</label>
</template>
<script type="application/dart" src="todo_item.dart"></script>
</polymer-element>
todochange.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('todo-item')
class TodoItemElement extends PolymerElement {
#observable Item item;
void change(Event e, var details, Node target) {
// the name is the name of your custom event
this.fire( "core-signal", detail: { "name": "todochange" } );
}
}
Then any subscriber just has to do this
subscriber.html
...
<link rel="import" href="packages/core_elements/core_signals.html>
...
<template>
<core-signals on-core-signal-todochange="{{handleToDoChange}}"></core-signals>
...
</template>
subscriber.dart
#CustomTag( "subscriber" )
class Sub extends PolymerElement {
...
void handleToDoChange( Event e, var detail, Node target ) {
print( "Got event from <todo-item>" );
}
...
}
Related
Two-way binding works in Dart Polymer 1.0 - RC2?
When I change the field #Property(nofity: true) in the .dart, it does not reflect (change) into {{}} in the .html.
See the following example.
Whem I click on paper-button, it´s fire clicar, the property text is changed, but {{text}} does not change!
main_app.html
<dom-module id="main-app">
<style>
:host {
display: block;
}
</style>
<template>
<paper-input label="Type something..." value="{{text}}"></paper-input>
<p>
Text: <span>{{text}}</span><br />
</p>
<paper-button on-click="clicar">cliqueme</paper-button>
</p>
</template>
</dom-module>
main_app.dart
#HtmlImport('main_app.html')
library untitled8.lib.main_app;
import 'dart:html';
import 'package:polymer_elements/paper_button.dart';
import 'package:polymer_elements/paper_input.dart';
import 'package:polymer/polymer.dart';
import 'package:web_components/web_components.dart';
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
#Property(notify: true)
String text;
MainApp.created() : super.created();
#reflectable
void clicar(e, detail) {
text = "super teste";
}
}
You need to use the provided methods to updated properties like
set('text', "super teste");
notify: true is only to notify parent elements (fires an test-changed event)
There are quite a lot such methods in PolymerBase mixin which you get automatically added by extending PolymerElement and which notify Polymer about changes.
notifyPath (currently the same as set)
for collections there are
add
addAll
clear
fillRange
insert
insertAll
removeItem
removeAt
removeLast
removeRange
removeWhere
replaceRange
retainWhere
setAll
setRange
there is also a
get
not sure about what to use it for.
I am working on a web app in polymer.dart. When I use core-signal, I am unable to access the detail. Here's some of the code that I'm using...
main_app.dart
class MainApp extends PolymerElement {
onClick(Event event, var detail, Node sender) {
print('button clicked');
fire("core-signal", detail:{'name':'button-click', 'data':0});
}
ready() {
super.ready();
}
}
canvasContainer.html
<link rel="import" href="../../packages/polymer/polymer.html">
<link rel="import" href="../../packages/core_elements/core_signals.html">
<polymer-element name="canvas-container">
<div>
<template>
<core-signals on-core-signal-button-click="{{testAction}}"></core-signals>
<div>
<canvas id="canvas"></canvas>
</div>
</template>
</div>
<script type="application/dart" src="canvasContainer.dart"></script>
</polymer-element>
canvasContainer.dart
class CanvasContainer extends PolymerElement {
// more code up here, but irrelevent
testAction(Event e, var detail, Node sender) {
print('event has been received');
print(detail);
context.moveTo(0, 0);
context.lineTo(1000, 1000);
context.stroke();
}
}
MainApp receives the button click, and sends the "core-signals" event.
In the canvasContainer though, the event does get received, but when I print the detail, it just says "0". Any insight into the problem is appreciated.
The way core-signal works, the data key of your detail object is what actually gets set as the detail of the event. The name property is then used to build the event type (it will be core-signal-$name).
See the docs here https://github.com/Polymer/core-signals/blob/master/core-signals.html#L79
Hello:
I have a paper-dialog element in a page:
<paper-dialog ... id="autom_desc_dialog" autoCloseDisabled>
...
<paper-button ... id="automatizar" affirmative autofocus disabled></paper-button>
</paper-dialog>
and I have an event listener that handles the paper-button click:
var auto_btn = querySelector('#automatizar');
auto_btn.on["click"].listen((Event e) {
// Some AJAX stuff
});
What I want is that in some cases, to be able to prevent the dialog from closing, I've tried event.preventDefault(), event.stopImmediatePropagation(), event.stopPropagation() but no success.
Thanks in advance.
You don't need to remove affirmative/dismissive attributes as they are used for layout. Polymer dialog docs are wrong (I've opened a GH issue) the default value for closeSelector is '[dismissive],[affirmative]' and not "", you just need to set closeSelector to "" and it won't close the dialog on clicking the buttons.
You just need to remove the affirmative attribute from the button then you have full control of the behavior.
app-element.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'package:paper_elements/paper_dialog.dart';
/**
* A Polymer app-element element.
*/
#CustomTag('app-element')
class AppElement extends PolymerElement {
/// Constructor used to create instance of AppElement.
AppElement.created() : super.created() {
}
void openClickHandler(Event e) {
print(e);
($['autom_desc_dialog'] as PaperDialog).opened = true;
}
void closeClickHandler(Event e){
if(true /* some condition */) {
($['autom_desc_dialog'] as PaperDialog).opened = false;
}
}
}
app_element.html
<!-- import polymer-element's definition -->
<link rel="import" href="../../packages/polymer/polymer.html">
<link rel="import" href="../../packages/paper_elements/paper_dialog.html">
<link rel="import" href="../../packages/paper_elements/paper_button.html">
<polymer-element name="app-element">
<template>
<style>
:host {
display: block;
}
</style>
<paper-dialog id="autom_desc_dialog" autoCloseDisabled>
<div>paper dialog</div>
<paper-button id="automatizar" autofocus label="close" on-click="{{closeClickHandler}}"></paper-button>
</paper-dialog>
<paper-button id="open" autofocus label="open" on-click="{{openClickHandler}}"></paper-button>
</template>
<script type="application/dart" src="app_element.dart"></script>
</polymer-element>
I have an email component (email-tag.html) that consist of a label, a select and a delete button element.
The email-tag.html component is hosted in its parent email-view-tag.html. email-view-tag contains an add-email-button that adds the email-tag element to the DOM each time it is clicked.
I need help in removing an added email-tag component when its delete-button is clicked. It is the compnoent that contains the delete-button that should be removed.
The two components are shown below:
email-tag.html
<!DOCTYPE html>
<polymer-element name='email-tag'>
<template>
<style>
.main-flex-container
{
display:flex;
flex-flow:row wrap;
align-content:flex-start;
}
.col
{
display:flex;
flex-flow:column;
align-content:flex-start;
flex-grow:1;
}
</style>
<div id='email' class='main-flex-container'>
<section id='col1' class='col'>
<input id=emailTxt
type='text'
list='_emails'
value='{{webContact.homeEmail}}'>
<datalist id='_emails'>
<template repeat='{{email in emails}}'>
<option value='{{email}}'>{{email}}</option>
</template>
</datalist>
</section>
<section id='col2' class='col'>
<button id='delete-email-btn' type='button' on-click='{{deletePhone}}'>Delete</button>
</section>
</div>
</template>
<script type="application/dart">
import 'package:polymer/polymer.dart' show CustomTag, PolymerElement;
import 'dart:html' show Event, Node;
#CustomTag( 'email-tag' )
class EmailElement extends PolymerElement
{
//#observable
EmailElement.created() : super.created();
List<String> emails = [ '', 'Home', 'Personal', 'Private', 'Work', ];
void deletePhone( Event e, var detail, Node target)
{
//shadowRoot.querySelector('#new-phone' ).remove();
//print( 'Current row deleted' );
}
}
</script>
</polymer-element>
email-view-tag.html
<!DOCTYPE html>
<link rel="import" href="email-tag.html">
<polymer-element name='email-view-tag'>
<template>
<style>
.main-flex-container
{
display:flex;
flex-flow:row wrap;
align-content:flex-start;
}
.col
{
display:flex;
flex-flow:column;
align-content:flex-start;
flex-grow:1;
}
</style>
<div id='email-view' class='main-flex-container'>
<section id='row0' >
<button id='add-email-btn' type='button' on-click='{{addPhone}}'>Add Phone</button>
</section >
<section id='rows' class='col'>
<!-- <epimss-phone-header-tag id='col1' class='col'></epimss-phone-header-tag> -->
</section>
</div>
</template>
<script type="application/dart">
import 'package:polymer/polymer.dart' show CustomTag, PolymerElement;
import 'dart:html' show Event, Node, Element;
#CustomTag( 'email-view-tag' )
class EmailViewElement extends PolymerElement
{
//#observable
EmailViewElement.created() : super.created();
void addPhone( Event e, var detail, Node target )
{
$[ 'rows' ].children.add( new Element.tag( 'email-tag' ) );
}
#override
void attached() {
super.attached();
$[ 'add-email-btn' ].click();
}
}
</script>
</polymer-element>
The application does execute normally and clicking the add button does add the email component. The delete button does not work - it is here I am asking for help.
Thanks
The child component, <email-tag> should not be in the business of deleting itself. Instead, it should delegate that responsibility to the the parent component, email-view-tag, by dispatching a custom event.
Here is the code for dispatching a custom event from deletePhone:
void deletePhone( Event e, var detail, Node target){
dispatchEvent(new CustomEvent('notneeded'));
}
Then, in the parent, <custom-view>, change your code for adding <email-tag>s like so:
void addPhone( Event e, var detail, Node target ) {
$['rows'].children.add( new Element.tag('email-tag'));
$['rows'].on["notneeded"].listen((Event e) {
(e.target as Element).remove();
});
}
Also, I would change the name of deletePhone, since the method no longer deletes the record but merely informs the parent that it is not needed. Call it 'notNeeded' or something similar.
EDIT
#ShailenTuli is right about encapsulation should not be broken.
But also JS Polymer elements access the parent in their layout elements because it's still convenient in some scenarios.
This works now in PolymerDart too.
(this.parentNode as ShadowRoot).host
ORIGINAL
You can fire an event and make the email-view-tag listen to this tag and the event handler can remove the event target from it's childs.
I had a similar question a while ago:
How to access parent model from polymer component
This was actually the question I wanted refer to
How can I access the host of a custom element
but the first one may be of some use too.
PolymerJS FAQ - When is the best time to access an element’s parent node?
attached() currently still named enteredView() in Dart, but will be renamed probably soon.
Trying to use Select component in custom element as follows. button click works but when an item is selected in the list, the 'selected' and 'value' attribute does not change and list always shows the first element selected. Binding seems to work from dart to html but not from html to dart. Help please!
<html>
<head>
<title>index</title>
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<polymer-element name="my-element" extends="div">
<template >
<button on-click='bclick'>Add new fruit</button>
<select selectedIndex="{{selected}}" value="{{value}}">
<option template repeat="{{fruit in fruits}}">{{fruit}}</option>
</select>
<div>
You selected option {{selected}} with value-from-list
{{fruits[selected]}} and value-from-binding {{value}}
</div>
</template>
<script type="application/dart" src="polyselect.dart"></script>
</polymer-element>
<my-element></my-element>
<script type="application/dart">main() {}</script>
</body>
</html>
Dart file is as follows:
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('my-element')
class MyElement extends PolymerElement {
#observable int selected = 1; // Make sure this is not null.
// Set it to the default selection index.
List fruits = toObservable(['apples', 'bananas', 'pears', 'cherry', 'grapes']);
#observable String value = '';
void bclick(Event e) {
fruits.add("passion fruit");
}
}
I had to mixin the ObservableMixin class.
class MyElement extends PolymerElement with ObservableMixin