Polymer 2.0 - Trying to create HTML template from JSON with events - polymer

I am trying to create HTML templates from JSON object and able to render elements but the events are not getting added to the element and not showing up in the developer tools/Shadow DOM.
Codepen for reference - https://codepen.io/nagasai/pen/bRjWbd
Issue: Events - onkeypress, onkeyup,onchange are not showing on input and checkbox elements and couldn't add them but other options are showing up like name, type(Again type is getting displayed only for checkbox but not for textbox)
Screenshot for actual issue
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 attr='[{
"type":"text",
"title":"Textbox Name",
"name":"temp",
"requried":"requried",
"onkeypress":"testKeyPress()",
"onkeyup":"testKeyUp()",
"onchange":""
},{
"type":"checkbox",
"title":"CheckBox Name",
"name":"temp",
"requried":"requried",
"disabled":"disabled",
"onkeypress":"",
"onkeyup":"",
"onchange":"testChange()"
}]'></x-foo>
<dom-module id="x-foo">
<template>
<template is="dom-repeat" items="{{attr}}">
<label>{{item.title}}</label>
<input type="{{item.type}}"
required="{{item.required}}"
name="{{item.name}}"
onchange="{{item.onchange}}"
onkeypress="{{item.onkeypress}}"
onkeyup="{{item.onkeyup}}()"
>
</template>
</template>
</dom-module>
</body>
JS:
class XFoo extends Polymer.Element {
static get is() { return 'x-foo'; }
static get properties() {
return {
attr:{
type:Array
}
};
}
}
customElements.define(XFoo.is, XFoo);

Correct me if I'm wrong but it should be:
on-change="{{item.onchange}}"
on-keypress="{{item.onkeypress}}"
on-keyup="{{item.onkeyup}}()"
Reference: https://www.polymer-project.org/2.0/docs/devguide/gesture-events

Related

Vaadin 10 slot in vaadin-dialog

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>

Why doesn't my this._setPropertyName function work?

I have the following polymer element :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../shared-styles.html">
<link rel="import" href="../../bower_components/dfw-styles/dfw-styles.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-menu-button/paper-menu-button.html">
<link rel="import" href="../../bower_components/paper-listbox/paper-listbox.html">
<dom-module id="baseline-policy-create">
<template>
<style include="dfw-styles">
:host {
display: block;
}
.top-button{
float : right;
}
</style>
<div class="outer-buttons">
<paper-menu-button horizontal-align="right" no-overlap="true" no-animations class="top-button">
<paper-button slot="dropdown-trigger" class="dropdown-trigger create-button btn-primary">
<iron-icon icon="menu"></iron-icon>
<span>Create Baseline Policy</span>
</paper-button>
<paper-listbox slot="dropdown-content" class="dropdown-content">
<template is="dom-repeat" items="{{_domains}}">
<paper-item on-tap="getDomainSchema">[[item.label]]</paper-item>
</template>
</paper-listbox>
</paper-menu-button>
</div>
</template>
<script>
class BaselinePolicyCreate extends Polymer.Element {
static get is() {
return 'baseline-policy-create';
}
static get properties() {
return {
_domains: {
type: Array,
value: [{'name':'Package', 'label':'Package'},
{'name':'Subscription', 'label':'Subscription'}] //TODO: is it possible to get these values from an API source
},
requestedDomain: {
type: String,
value : "",
notify : true,
readOnly : true
}
}
}
getDomainSchema(evt) {
console.info("Get the schema for the following domain:", evt.target.innerText);
console.log(this.requestedDomain);
this._setRequestedDomain(evt.target.innerText);
//this.requestedDomain = evt.target.innerText;
console.log(this.requestedDomain);
}
}
customElements.define(BaselinePolicyCreate.is, BaselinePolicyCreate);
</script>
</dom-module>
I've been following this tutorial on data binding : https://www.tutorialspoint.com/polymer/polymer_data_system.htm
In the example, the code for prop-element has a property, myProp, with the attribute readOnly set to true. In the Onclick function, its still able to change the value of the property using this._setMyProp() which isn't defined anywhere explicitly.
I want to do the same in my code. That is, I want to set requestedDomain using this method. I can set it using this line :
this.requestedDomain = evt.target.innerText;
But to do so, I can't set the readOnly flag to true. If I use this._setRequestedDomain, I get an error saying it is not a function. Am I missing some import at the top to allow this to work, or maybe its been removed in Polymer 2?
I have been testing your code and it's ok.
You only will get the error "this.setRequestedDomain is not a function" if your property is set as readOnly = false.
Please, try again and tell me if its ok.
pen code example (without styling)

Polymer2.0- Trying to download DIV content of custom element

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

How to inject template using slot with data-binding in Polymer2

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>

Dynamic class name for a div after an iron-ajax

I need to set the name of a class dynamically executing a function after an iron-ajax request.
In the template section of my element I have:
<div class="{{dynamicClass}}"></div>
I have defined this property like this:
Polymer({
is: "fila-contenido"
,
properties: {
dynamicClass: {
type: Number
}
}
...
I have an on-response function executed when iron-ajax responses:
_onResponse: function (e) {
var nro = e.detail.response.length;
switch(nro){
case 2:
this.dynamicClass = "class2";
break;
case 3:
this.dynamicClass = "class3";
break;
...
}
}
This function set the name of the class correctly.
The problem is the output is not showing the dynamic class but:
<div class="style-scope fila-contenido"></div>
How may I do this?
Thanks!
To bind to the native class attribute, use attribute binding (i.e., class$=). Otherwise, Polymer treats it as a property binding.
<div class$="{{dynamicClass}}">
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo'
});
});
<head>
<base href="https://polygit.org/polymer+1.7.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<x-foo clazz="highlight"></x-foo>
<dom-module id="x-foo">
<template>
<style>
.highlight {
color: blue;
background: lightgray;
}
</style>
<span class$="{{clazz}}">hello world</span>
</template>
</dom-module>
</body>
codepen