I have just started exploring polymer.js. I want to get name from paper-input element. It isn't working the alert is empty.
<dom-module id="hello-world">
<template>
<h1> Hello [[name]]</h1>
<paper-input value="{{name}}"></paper-input>
<button onClick="{{getData}}">Get data</button>
</template>
<script>
Polymer({
is: "hello-world",
properties: {
name: {
type: String,
value: '1'
}
},
getData: function () {
alert(this.name);
}
})
</script>
If you want onClick event, use on-click="getData" in polymer template.
....To add event listeners to local DOM children, use on-event
annotations in your template. This often eliminates the need to give
an element an id solely for the purpose of binding an event listener.
Because the event name is specified using an HTML attribute, the event
name is always converted to lowercase. This is because HTML attribute
names are case insensitive. So specifying on-myEvent adds a listener
for myevent. The event handler name (for example, handleClick) is case
sensitive. To avoid confusion, always use lowercase event names.
DEMO
Related
const continueButton = $("<button>Doesn't work.</button>").click(() => {alert("hello")});
$(".content").append(continueButton.prop('outerHTML'));
$(".content").append ($("<button>Works.</button>").click(() => {alert("hello")}))
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
</div>
The reason I'm asking this question is because I need to pass the string version of some HTML to a function. For that reason, I can't use .append. but when i use the code above, it seems that the click event no longer works.
How can I get the HTML as a string, but have the click event still work?
More context: I am using a library that expects me to add HTML to it as a string. But I want to add HTML with a button on it that functions when it's clicked. I'm using jQuery to create the HTML, but when I try to pass the HTML string to the library, the buttons don't function.
You can delegate the event to the content element and use button as target selector
// add delegated event listener before inserting buttons
$('.content').on('click', 'button', (e) => console.log($(e.target).text()))
const continueButton = $("<button>Doesn't work.</button>");
$(".content").append(continueButton.prop('outerHTML'))
.append ("<button>Works.</button>");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
</div>
In line 1 the object has an event attached to it but this isn't reflected in HTML. Therefore when you add the outerHTML to an element, the browser creates a new element but events are not defined in the HTML so they don't exist.
If you embed the script inside the button HTML then it will work when you apply this HTML in different places: $('<button onclick="alert(\'hello\');">Test</button>').
I have a jQuery plugin (which I don't want to modify) that is dynamically creating a div. Aside from that, I have a webcomponent scrollable-div, which is a customized built-in extended from HTMLDivElement. As I have no control over how that div is created by the jQuery plugin, I need to upgrade it after creation and after it has already been added to the DOM.
class myDiv extends HTMLDivElement {
constructor(...args) {
const self = super(...args);
self.addEventListener('click', (e) => {
e.target.textContent = 'clicked'
})
return self;
}
}
customElements.define('my-div', myDiv, { extends: 'div' });
document.addEventListener('DOMContentLoaded', () => {
// this is where I'm trying to turn the div#upgradeMe into a my-div
upgradeMe.setAttribute('is', 'my-div');
});
<div id="upgradeMe">Click me</div>
Simply adding the is="my-div" attribute obviously does not do the trick, the div simply stays a regular HTMLDivElement. How can I programmatically upgrade a native element that is already in the DOM to a customized built-in web component?
It's not possible because the element is already created as a standard <div> element and not identified when parsed as upgradable (extendable) due to the lack of the is attribute.
If the custom element is already defined, the only possible workaround is to replace the existing by a clone (as suggested in the comments by #barbsan).
The short way:
create a <template> element
copy the div's outerHTML into its innerHTML property
replace the orginal element with the template's content with replaceChild()
class myDiv extends HTMLDivElement {
constructor(...args) {
const self = super(...args);
self.addEventListener('click', (e) => {
e.target.textContent = 'clicked'
})
return self;
}
}
customElements.define('my-div', myDiv, { extends: 'div' });
document.addEventListener('DOMContentLoaded', () => {
// this is where I'm trying to turn the div#upgradeMe into a my-div
upgradeMe.setAttribute('is', 'my-div');
var t = document.createElement( 'template' )
t.innerHTML = upgradeMe.outerHTML
upgradeMe.parentElement.replaceChild( t.content, upgradeMe )
});
<div id="upgradeMe">Click me</div>
Précisions
When an element is parsed, an is value is affected according to the DOM spec:
Elements have an associated namespace, namespace prefix, local name, custom element state, custom element definition, is value. When an element is created, all of these values are initialized.
Only elements with a valid is attribute are identified as customizable:
An element’s custom element state is one of "undefined", "failed", "uncustomized", or "custom". An element whose custom element state is "uncustomized" or "custom" is said to be defined. An element whose custom element state is "custom" is said to be custom.
Therefore if the element has no is attribute at parse time, it will not be customizable. That's why you cannot add the is attribute afterward.
Also in the HTML specs:
After a custom element is created, changing the value of the is attribute does not change the element's behavior, as it is saved on the element as its is value.
The is attribute is used only at element creation (at parse time) to initialize the is value and has no effect if changed when the element is already created. In that sense is value is read-only.
If you want to support all modern browser's you can't customize built in components, Apple said they will never support is="" https://github.com/w3c/webcomponents/issues/509#issuecomment-222860736
I'd like to declare attributes directly on a Polymer element, which are then passed inside the element and are readable/accessible outside of the element script.
I'd like to use the values of such attributes for deciding how to register the element.
tl;dr
I'm having an issue where I need to register an element some time after the whole page has loaded - i.e I want to manually register the element.
A solution for registering elements on demand:
<dom-module id="foo-element">
<template>
<span> Foo element </span>
</template>
</dom-module>
<script>
window.addEventListener("app-ready", function() {
"use strict";
Polymer({
is: "foo-element",
properties: {
//..... rest of element properties, methods, etc
Explaining what I'm doing above:
Instead of using HTMLImports.whenReady(<element-registration-code>), I use addEventListener(event, <element-registration-code>
I broadcast app-ready when I want the registration to happen
This allows me to register the element, on-demand
The reusability problem of the above solution
This poses a severe reusability problem - while this element in one of context needs to be registered at some specific point in time, in other context it might not - it should register itself using the standard HTMLImports.whenReady(<elementCode>) method.
An ideal example:
<!-- Registers automatically when `HTMLImports` are ready, the "regular" way-->
<foo-element></foo-element>
<!-- Registers only when it picks up an `app-ready` event-->
<foo-element no-auto-register register-on-event="app-ready"></foo-element>
and the element could look something like this:
<dom-module id="foo-element">
<template>
<span> Foo element </span>
</template>
</dom-module>
<script>
// if `no-auto-register` is set on the element,
// do not use `HTMLImports.whenReady()` and use
// `addEventListener` to register when an event
// with the value of `register-on-event` property fires.
</script>
Long story, short
Is there any way to declare a flag/property/attribute directly on the element which would decide how the registration should happen?
Passing an attribute to element might not work as element needs to be in ready state for that. Below are three ways that might help you with what you are trying
One way to lazy register your elements in Polymer is to use Polymer.Class instead of Polymer constructor. This way until you register your element manually it will not get rendered. Below is an working example of same.
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="poly-form">
<template>
<style></style>
I am parent Element
<div>
<button onclick='register()'>Save</button>
</div>
<show-form></show-form>
</template>
</dom-module>
<script>
Polymer({
is: 'poly-form',
});
//element to lazy register
var showForm = Polymer.Class({
is: 'show-form',
ready: function() {
this.textContent = 'I am working...';
}
});
function register() {
var form = document.querySelector('poly-form');
//lazy register the element
document.registerElement('show-form', showForm);
}
</script>
<poly-form></poly-form>
In this example show-form element does not render until the button is clicked.
Note I've never really tried this with element containing dom.
Second way this should also be possible with importHref method.
Third way is global setting lazy-register where element gets register only when its first instance is called.
Sorry, the snippet is not as well constructed as it could have been. Hope it helps.
Class Style Constructor
If you want to set up your custom element's prototype chain but not register it immediately, you can use the Polymer.Class function. Polymer.Class takes the same prototype argument as the Polymer function, and sets up the prototype chain, but does not register the element. Instead it returns a constructor that can be passed to document.registerElement to register your element with the browser, and after which can be used to instantiate new instances of your element via code.
var MyElement = Polymer.Class({
is: 'my-element',
// See below for lifecycle callbacks
created: function() {
this.textContent = 'My element!';
}
});
document.registerElement('my-element', MyElement);
// Equivalent:
var el1 = new MyElement();
var el2 = document.createElement('my-element');
In Polymer 1.0 we can bind an element property to a variable:
<paper-textarea id='area' value={{some_variable}}></paper-textarea>
How can I unbind it?
Below is a solution that doesn't works for me. When some_variable changes it updates area value.
this.$.area.value = "foo";
You can't dynamically bind and/or unbind to element attributes in Polymer 1.0, because bindings are baked during element registration time, not during created/ready/attached. Ref Binding imperatively
Honestly I'm not exactly sure what your use-case is; however, it highly likely that you can achieve it by a) binding your value attribute to a computed function; or b) bind to a dummy variable
<paper-textarea id='area' value={{letsCompute(some_variable)}}></paper-textarea>
...
letsCompute: function (v) {
//return your logic to bind or unbind
},
...
It is not 100% clear what you are trying to achieve, but with Polymer you can do one-way data binding.
Downward binding:
<script>
Polymer({
is: 'custom-element',
properties: {
prop: String // no notify:true!
}
});
</script>
...
<!-- changes to "value" propagate downward to "prop" on child -->
<!-- changes to "prop" are not notified to host due to notify:falsey -->
<custom-element prop="{{value}}"></custom-element>
Upwards binding:
<script>
Polymer({
is: 'custom-element',
properties: {
prop: {
type: String,
notify: true,
readOnly: true
}
}
});
</script>
...
<!-- changes to "value" are ignored by child due to readOnly:true -->
<!-- changes to "prop" propagate upward to "value" on host -->
<custom-element prop="{{value}}"></custom-element>
Check out the documentation for more information.
I want to append this to my document:
$('#myDiv).append("<div id='myDiv2' onclick="+extElementConfig.onClickDo+">Do</div>");
The snippet above has it's onClick populated by a certain object with properties,
this:
var extElementConfig={onClickDo:sampleFunc()};
Unfortunately declaring a function into the object property also fires it, as was expected.
How can I achieve the above functionality without triggering the
sampleFunc()?
I just need to dynamically populate the onClick event through an object property.
Assuming you have control over the extElementConfig object, remove the parenthesis from sampleFunc
var extElementConfig={extElementPosition :10,extElementId:'mailOrderBtn',onClickDo:sampleFunc};
As Rob. M. pointed out the real problem with your code was the fact you were running the code when it was in the object instead of assigning a reference. And when you tried to assigning the onclick, that had issues too since you are trying to use a function reference when it was a string.
To get your code to run, it would be something like
function sampleFunc () {
alert("clicked");
}
var extElementConfig={onClickDo:"sampleFunc()"};
$('#myDiv').append("<div id='myDiv2' onclick='"+extElementConfig.onClickDo+"()'>Do</div>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="myDiv"></div>
To get rid of this, do not use the sting method to build up the element, instead leverage the power of jQuery. Below is an example where I build a div and pass in some arguments to change the element's text and css when clicked.
function sampleFunc(txt, css) {
$(this).text(txt).css(css);
}
var extElementConfig = {
onClickDo: sampleFunc,
onClickArgs : ["Clicked", {"background-color":"green"}]
};
$('<div/>', {
id: 'myDiv2',
text: 'Do!'
}
).on("click",
function() {
extElementConfig.onClickDo.apply(this, extElementConfig.onClickArgs);
}
).appendTo("#myDiv");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="myDiv"></div>