So I want to dispatch custom event from the child element.
<dom-module id="layout-dashboard">
<template>
<style></style>
</template>
<script>
class LayoutDashboard extends Polymer.Element {
connectedCallback() {
this.dispatchEvent(new CustomEvent('kick-start', {detail : {}, bubble: true, composed : true}))
}
}
</script>
</dom-module>
And then handle the event from the parent.
<dom-module id="layout-parent">
<template>
<style></style>
<layout-dashboard on-kick-start="handleKick"></layout-dashboard>
</template>
<script>
class LayoutParent extends Polymer.Element {
handleKick(){
console.log("test");
}
}
</script>
</dom-module>
Any idea why handleKick() is not executed?
Your code structure for defining custom-element is wrong. Please read custom element concepts and quick tour of polymer.
The basic structure is like this:
<link rel="import" href="...">
<dom-module id='my-element'>
<template>
<style></style>
</template>
<script>
class MyElement extends Polymer.Element {
static get is() {
return "my-element";
}
}
customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
The mistakes you have done:
<script></script> and then you wrote your scripts.
<script></script> is inside <template> that should be outside <template>and inside <dom-module>.
For the custom element lifecycle you need call the superclass method. This is required so Polymer can hook into the element's lifecycle. i.e:
connectedCallback() {
super.connectedCallback();
// …
}
You need to register the new element with the browser
customElements.define(MyElement.is, MyElement);
I have made a plnkr demo to solve the issue. Please have a look at : http://plnkr.co/edit/p5qvt4cIEJBobrmY8QDg?p=preview .
Dashes in the event name are fine. Start by fully defining and registering your elements.
Example from the Polymer docs here. Note the is() getter and the customElements.define at the end which registers your element.
<script>
class IconToggle extends Polymer.Element {
static get is() {
return "icon-toggle";
}
constructor() {
super();
}
}
customElements.define(IconToggle.is, IconToggle);
</script>
It's not specified in the Polymer doc, but I suppose it's because the event name contains a dash. If you want to handle this event, I suggest you to add a listener once connect.
Add a listener to the LayoutParent ready function:
ready() {
super.ready();
window.addEventListener('kick-start', (e) => this.handleKick(e));
}
nb; for Polymer 2.0 (as described in #Ofisora's post)
Related
I want to create a reusable dialog in Vaadin 10. Therefore I thought of using the tag in vaadin-dialog. I created a html file containing the templated vaadin-dialog.
<dom-module id="show-sera-dialog">
<template>
<vaadin-dialog opened="opened">
<sera-field></sera-field>
<slot></slot>
</vaadin-dialog>
<template>
</dom-module>
And I try to use it like this.
<show-sera-dialog opened="{{showSera}}">
It worked!
</show-sera-dialog>
The dialog will be opened and the sera-field displayed, but the text is never displayed. Is there an error withing these lines? Am I using vaadin-dialog the wrong way?
PS:
It works with this button:
<dom-module id="one-shot-button">
<template>
<vaadin-button on-click="_disable" theme="raised primary" disabled={{disabled}}>
<slot></slot>
</vaadin-button>
</template>
<script>
class OneShotButton extends I18nMixin(Polymer.Element) {
static get is() {
return 'one-shot-button'
}
static get properties() {
return {
disabled: {type: Boolean, notify: true}
}
}
_disable() {
this.disabled = true;
this.onClick();
}
}
customElements.define(OneShotButton.is, OneShotButton);
</script>
You are putting a <slot> inside a <template>. Template means that web component will do whatever it needs when rendering it, e.g. by creating multiple instances like cells in grid, etc.
In this case vaadin-dialog teleports the content to the body, so as it escapes any stacking context. Thus it makes slots not work because they are not in the same DOM hierarchy.
One way to create a reusable dialog would be to create a component like this
<dom-module id="show-sera-dialog">
<template>
<vaadin-dialog opened={{opened}}>
<template>
[[text]]
</template>
</vaadin-dialog>
</template>
<script>
class ShowSeraDialog extends Polymer.Element {
static get is() { return 'show-sera-dialog'; }
static get properties() {
return {
"text" : String,
"opened" : Boolean
}
}
}
window.customElements.define(ShowSeraDialog.is, ShowSeraDialog);
</script>
</dom-module>
And use it like this
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="./show-sera-dialog.html">
<dom-module id="polymer-test-app">
<template>
<show-sera-dialog id="dialog1" text="It worked!"></show-sera-dialog>
<button on-click="showDialog">Show dialog</button>
</template>
<script>
class PolymerTestApp extends Polymer.Element {
static get is() { return 'polymer-test-app'; }
showDialog() {
this.$.dialog1.opened = true;
}
}
window.customElements.define(PolymerTestApp.is, PolymerTestApp);
</script>
</dom-module>
I am trying to download div content of custom element using document.getElementById of the div and trying to implement download option from the JS FIddle - http://jsfiddle.net/evx9stLb/
From console, I am getting below error
pen.js:6 Uncaught TypeError: Cannot read property 'innerHTML' of null
at download (pen.js:6)
at HTMLButtonElement.onclick (index.html:15)
HTML:
<head>
<base href="https://polygit.org/polymer+v2.0.0/shadycss+webcomponents+1.0.0/components/">
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="iron-collapse/iron-collapse.html">
</head>
<body>
<x-foo></x-foo>
<button onClick="download()">Download</button>
<dom-module id="x-foo">
<template>
<button on-click="toggle">toggle collapse</button>
<div id="content">
<iron-collapse id="collapse">
<div>Content goes here...</div>
</iron-collapse>
</div>
</template>
</dom-module>
</body>
JS:
function download(){
var a = document.body.appendChild(
document.createElement("a")
);
a.download = "export.html";
a.href = "data:text/html," + document.getElementById("content").innerHTML;
a.click();
}
class XFoo extends Polymer.Element {
static get is() { return 'x-foo'; }
static get properties() {
return {};
}
toggle() {
this.$.collapse.toggle();
}
}
customElements.define(XFoo.is, XFoo);
Code which I am below - https://codepen.io/nagasai/pen/ZyRKxj
Make some updates, and help this would help,
HTML
<head>
<base href="https://polygit.org/polymer+v2.0.0/shadycss+webcomponents+1.0.0/components/">
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="iron-collapse/iron-collapse.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<button on-click="download">Download</button>
<button on-click="toggle">toggle collapse</button>
<div id="content">
<iron-collapse id="collapse">
<div>Content goes here...</div>
</iron-collapse>
</div>
</template>
</dom-module>
</body>
JS
class XFoo extends Polymer.Element {
static get is() { return 'x-foo'; }
static get properties() {
return {};
}
toggle() {
this.$.collapse.toggle();
}
download(){
var a = document.body.appendChild(
document.createElement("a")
);
a.download = "export.html";
a.href = "data:text/html," + this.$.content.innerHTML;
a.click();
console.log(this.$.content.innerHTML);
}
}
customElements.define(XFoo.is, XFoo);
https://codepen.io/renfeng/pen/BZOQro
a document query (on light dom) won't pierce the shadowDom. To do that you have to specifically select the element and query it's shadowRoot.
it would look something like this
a.href = "data:text/html," + document.getElementsByTagName('x-foo')[0].shadowRoot.querySelector('#content').innerHTML;
BUT only do this if you can't modify the element itself. It's not nice to stir around in someone else's shadowRoot.
As shown by Frank R. its far better to modify the element itself and provide a download functionality.
You can trigger this easily from an external element with something like
document.getElementsByTagName('x-foo')[0].download();
DOM under the shadow Root, or the shadow DOM, can not be accessed via innerHTML. It is not supposed to be. Just the way it is.
So, No, you simply can not get the shadow DOM contents via innerHTML.
There used to be access now deprecated to shadowDOM via vanilla javascript earlier
and also discussed here
However, with shadow DOM V1 beig the norm now, you may have to just wait and watch if you can pierce the shadowDOM
An alternative would be, to move your entire DOM in the custom element, outside of it, Using Slots.
Slots distribute content, so, the page that uses your element, can access it via innerHTML.
You could possibly try hacky ways like the one mentioned here
I need to include a vaadin-grid in a custom Polymer 2 element and need to handle the row selection event, but I can't seem to get it to work. I've managed to cobble together this from the various starter templates and demos on offer, and the basic click event handler works, but I have no idea how to handle the row selection. I'm actually using v3.0.0-beta1 of the vaadin-grid because I couldn't get v2 to work, but I don't think that's my problem.
Does anyone know how to handle events in vaadin components when you include them in your own custom elements?
Thanks.
<link rel="import" href="bower_components/polymer/polymer-element.html">
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="bower_components/vaadin-grid/vaadin-grid.html">
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
}
</style>
<iron-ajax auto url="https://demo.vaadin.com/demo-data/1.0/people?count=200" handle-as="json" last-response="{{users}}"></iron-ajax>
<vaadin-grid aria-label="My Test Grid" items="[[users.result]]" id="grid">
<vaadin-grid-column width="50px" flex-grow="0">
<template class="header">#</template>
<template>[[index]]</template>
</vaadin-grid-column>
<vaadin-grid-column>
<template class="header">First Name</template>
<template>[[item.firstName]]</template>
</vaadin-grid-column>
<vaadin-grid-column>
<template class="header">Last Name</template>
<template>[[item.lastName]]</template>
</vaadin-grid-column>
<vaadin-grid-column width="150px">
<template class="header">Address</template>
<template>
<p style="white-space: normal">[[item.address.street]], [[item.address.city]]</p>
</template>
</vaadin-grid-column>
</vaadin-grid>
</template>
<script>
class MyElement extends Polymer.Element {
static get is() { return 'my-element'; }
ready() {
super.ready();
this.$.grid.addEventListener('click', e => {
this._handleClick(e)
});
// I added this listener code from here: https://vaadin.com/elements/-/element/vaadin-grid#demos
// but it does nothing. I've also tried adding it to this, this.$, this.$.grid without success.
// Should this event listener be added here, or if not, where exactly? The docs are very unclear.
addEventListener('WebComponentsReady', function() {
Polymer({
is: 'my-element',
properties: {
activeItem: {
observer: '_activeItemChanged'
}
},
_activeItemChanged: function(item) {
this.$.grid.selectedItems = item ? [item] : [];
console.info('row clicked');
}
});
// this works and outputs info like this: "vaadin-grid-cell-content-17 was clicked."
_handleClick(e) {
console.info(e.target.id + ' was clicked.');
}
}
window.customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
I think that my code in the addEventListener is Polymer 1.x syntax but I'm not sure how to achieve the same result using Polymer 2.x.
I used only vaadin-grid v2, but I downloaded the v3.0.0-beta1 and took a look inside the package, at demo/row-details.html
There is an example on how to handle the active row change event.
<vaadin-grid on-active-item-changed="_onActiveItemChanged" id="grid" aria-label="Expanded Items Example" data-provider="[[dataProvider]]" size="200">
<template class="row-details">
<div class="details">
<img src="[[item.picture.large]]"></img>
<p>Hi! My name is [[item.name.first]]!</p>
</div>
</template>
</vaadin-grid>
You can then define a function called _onActiveItemChanged inside your polymer element to handle the active item changed event.
I'd like to inject a rendering template from a parent component to a child component using <slot> insertion points. The injected template contains data-binding on a property of the child component (my-child.data in this case).
<dom-module id="my-parent">
<template>
<my-child>
<template>
<div>Child's data property: [[data]]</div>
</template>
</my-child>
</template>
...
The rendering child component basically looks like this:
<dom-module id="my-child">
<template>
<header></header>
<slot></slot>
<footer></footer>
</template>
<script>
class MyChild extends Polymer.Element {
static get is() { return 'my-child'; }
static get properties() {
return {
data: { ... }
};
}
...
I'm not sure whether this is possible at all with Polymer2. Vue2 has a concept called "scoped slot" to achieve this. Thanks in advance for any feedback!
Data binding is by default tied within the current scope of the binding. If you wish to change the scope, you must put your markup inside a <template> tag and stamp in it inside a different scope.
Your HTML code in the question is already OK - you actually wrap the light DOM inside a <template>, but you then use that <template> incorrectly. You must not use <slot>, but must stamp that template manually and insert it somewhere inside the my-child element's shadow DOM.
Here you have a working demo on how to achieve this: http://jsbin.com/loqecucaga/1/edit?html,console,output
I have even added the data property binding to an input element in order to demonstrate that property changes also affect the stamped template.
The stamping is relatively simple and is done inside the connectedCallback method:
var template = this.querySelector('template');
this.__instance = this._stampTemplate(template);
this.$.content.appendChild(this.__instance);
The stamped template is put inside a placeholder div element, which you put somewhere inside the my-child's template:
<div id="content"></div>
To sum up, here is the full code from the demo:
<link href="polymer/polymer-element.html" rel="import"/>
<link href="polymer/lib/mixins/template-stamp.html" rel="import"/>
<dom-module id="my-parent">
<template>
<my-child>
<template>
<div>Child's data property: [[data]]</div>
</template>
</my-child>
</template>
<script>
class MyParent extends Polymer.Element {
static get is() { return 'my-parent'; }
}
window.customElements.define(MyParent.is, MyParent);
</script>
</dom-module>
<dom-module id="my-child">
<template>
<header>Header</header>
<div id="content"></div>
<footer>Footer</footer>
<input type="text" value="{{data::input}}" />
</template>
<script>
class MyChild extends Polymer.TemplateStamp(Polymer.Element) {
static get is() { return 'my-child'; }
static get properties() {
return {
data: {
type: String,
value: 'Hello, World!'
},
};
}
connectedCallback() {
super.connectedCallback();
var template = this.querySelector('template');
this.__instance = this._stampTemplate(template);
this.$.content.appendChild(this.__instance);
}
}
window.customElements.define(MyChild.is, MyChild);
</script>
</dom-module>
<my-parent></my-parent>
I'm using polymer 0.8 which I understand has a new API, but I can't find a reference to this issue anywhere. I'm trying to accomplish this:
<center-focusable>
<template is="x-autobind">
<span on-click="setFocus">test?</span>
</template>
</center-focusable>
Which errors out with:
[span].[click]: event handler [setFocus] is null in scope ()
My element is defined as such:
<dom-module id="center-focusable">
<template>
<span on-click="setFocus">I work</span>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: "center-focusable",
setFocus: function () {
console.log("test");
}
});
</script>
Is this not possible?
What I've ended up doing is to defined another custom element that will trigger a custom event for me to catch.
<dom-module id="focusable-element">
<content></content>
</dom-module>
<script>
Polymer({
is: "focusable-element",
listeners: {
'click': 'setFocus'
},
setFocus: function () {
this.fire("center-focusable:center");
}
});
</script>
Then I'll catch it:
<script>
Polymer({
is: "center-focusable",
listeners: {
'center-focusable:center': 'setFocus'
},
setFocus: function (e) {
e.stopPropagation();
console.log("test");
}
});
</script>
Together:
<center-focusable>
<focusable-element>
This works.
</focusable-element>
</center-focusable>
Seems like an unnecessary amount of effort to handle this way though.
<span> is a regular HTML element, not a polymer element.
on-click is a polymer syntax.
I think you should use a regular html event : onclick (no dash).
Ex : <span onclick="console.log('test')">I work</span>