Is there any way to declare a global function in Polymer 2.0 that can be used in other components? I have a moment.html file for using moment.js in the project:
<script src="../bower_components/moment/moment.js"></script>
In the same file I also declare a function instead of declaring it in every single component where I want to use it:
<script>
function format(date) {
return moment(date).format('dddd, D MMMM YYYY');
}
</script>
The moment object is available after importing the file but when I try to call the format function it gives me the warning method 'format' not defined. How can I make the function publicly available?
Edit: I can call the format function from within the script tags of another component, but I cannot access it from within a template, i.e. with:
<strong>[[format(event.date)]]</strong>
I want to render the result from the function on the page, not access it programmatically.
I think, for your task, the best documentation is Monica Dinculescu's own cheat sheet.
https://meowni.ca/posts/polymer-2-cheatsheet/#defining-a-mixin
She's a Polymer developer. Below is me copy pasting from the link. It's the extends MyMixin(Polymer.Element) that does the magic.
Defining a class expression mixin to share implementation between different elements:
<script>
MyMixin = function(superClass) {
return class extends superClass {
// Code that you want common to elements.
// If you're going to override a lifecycle method, remember that a) you
// might need to call super but b) it might not exist
connectedCallback() {
if (super.connectedCallback) {
super.connectedCallback();
}
/* ... */
}
}
}
</script>
Using the mixin in an element definition:
<dom-module id="element-name">
<template><!-- ... --></template>
<script>
// This could also be a sequence:
//class MyElement extends AnotherMixin(MyMixin(Polymer.Element)) { … }
class MyElement extends MyMixin(Polymer.Element) {
static get is() { return 'element-name' }
/* ... */
}
customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
Here is the sample how I work it ;
<paper-button on-tap="customfunc"> Test </paper-button>
<div><strong>[[format(date)]]</strong></div> // result at screen: Saturday, 20 January 2018
...
<script>
class MyTest extends Polymer.Element {
static get is() { return 'test-component'; }
ready(){
super.ready();
this.set('date', new Date())
}
customfunc() {
var d = new Date();
var dd = this.format(d);
console.log("d ", d, " - dd = ", dd);// d Sat Jan 20 2018 17:02:38 GMT+0300 (Türkiye Standart Saati) - dd = Saturday, 20 January 2018
}
format(date){
return moment(date).format('dddd, D MMMM YYYY');
}
As Your format_function is in shadow root, you have to use .shadowRoot.querySelector
below is my working code, in this i have format_funtion in page1 and am calling it in page2
<script src="https://polygit.org/components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<!-- my-app element -->
<dom-module id="my-app">
<template>
<my-page1></my-page1>
<my-page2></my-page2>
</template>
<script>
class MyApp extends Polymer.Element {
static get is() {
return 'my-app'
}
constructor() {
super();
}
}
window.customElements.define(MyApp.is, MyApp);
</script>
</dom-module>
<!-- page1 element -->
<dom-module id="my-page1">
<script>
class MyPage1 extends Polymer.Element {
static get is() {
return 'my-page1';
}
constructor() {
super();
}
format_function() {
alert("in format function");
}
}
window.customElements.define(MyPage1.is, MyPage1);
</script>
</dom-module>
<!-- page2 element -->
<dom-module id="my-page2">
<template> <div on-click="test">click to test format_funtion.....!</div></template>
<script>
class MyPage2 extends Polymer.Element {
static get is() {return 'my-page2';}
test() {
var host = document.querySelector('my-app').shadowRoot.querySelector('my-page1');
host. format_function();
}
}
window.customElements.define(MyPage2.is, MyPage2);
</script>
</dom-module>
<!-- calling element -->
<my-app></my-app>
Don't forget to import files e.g page.html or page2.html
Related
My query selector or getElementById always returns null, can someone explain to me why? tried everything I can think off (and found on the internet) but nothing works.
#customElement('my-element')
export class MyElement extends LitElement {
#property({type : String}) carousel = document.querySelectorAll("[data-target='carousel']");
connectedCallback(): void {
super.connectedCallback();
console.log(this.shadowRoot.querySelector('slider'));
console.log(this.shadowRoot.querySelector('.slider'));
console.log(this.shadowRoot.querySelector('#slider'));
console.log(this.shadowRoot.getElementById('slider'));
console.log(document.getElementById('slider'));
}
render(){
return html`
<div class="slider" id="slider">
<ul class="carousel" data-target="carousel">
Please try with the following:
#customElement('my-element')
export class MyElement extends LitElement {
get root() {
return this.shadowRoot || this
}
render(){
return html`<div class="slider" id="slider">...`
}
firstUpdated() {
console.log(this.root.getElementById('slider')
}
}
I'm trying to create a HTMLPortalElement with Lit-Element, but when i'm ready to activate() the Portal i get this Error in Web Dev Console :
DOMException: Failed to execute 'activate' on 'HTMLPortalElement': The HTMLPortalElement is not associated with a portal context.
class WikiPortal extends LitElement {
static get properties() { return {
_portalSrc: String
}};
constructor() {
super();
this._portalSrc = 'https://wicg.github.io/portals/';
}
render() { return html`
<portal src="${this._portalSrc}" #click="${this._portalClickHandler}">
</portal>`;
}
_portalClickHandler() {
this.shadowRoot.querySelector('portal').activate();
}
Before answering I just want to mention that portal is an experimental element and is only available in Chrome Canary after activating it by visiting chrome://flags/#enable-portals.
There seems to be an issue with the creation of the portal element within a template/shadowDOM. The following is a workaround that worked for me. It is basically creating the portal element programmatically in the main DOM and appending it as a child to the custom element. Note I added a test button within the component so I could see something to click.
import { LitElement, html, css } from 'lit-element';
class TestElement extends LitElement {
constructor() {
super();
this._portalSrc = 'https://wicg.github.io/portals/';
}
render() {
const template = html`<button type="button" #click="${this._portalClickHandler}">TEST</button>`;
return template;
}
_portalClickHandler() {
var node = document.createElement("portal");
node.src = this._portalSrc;
document.querySelector('test-element').appendChild(node);
console.log(document.querySelector('portal').src);
document.querySelector('portal').activate();
}
}
customElements.define('test-element', TestElement);
If my component has explicitly been placed in another shadow-dom component, how to get this component?
For instance:
Here is a generic way to get the parent element when you are placed in their shadow DOM. I did not add any error checking, but if you know you are in their shadow then this works just fine.
class OuterEl extends HTMLElement {
constructor() {
super();
var root = this.attachShadow({mode:'open'});
root.innerHTML = "<inner-el></inner-el>";
}
}
class InnerEl extends HTMLElement {
constructor() {
super();
var root = this.attachShadow({mode:'open'});
root.innerHTML = `<button>Click</button><hr/><output></output>`;
this.button = root.querySelector('button');
this.output = root.querySelector('output');
this.button.addEventListener('click', () => {
let parentSrc = this.getRootNode();
this.output.textContent = parentSrc.host.outerHTML;
})
}
}
customElements.define('outer-el', OuterEl);
customElements.define('inner-el', InnerEl);
</script>
<outer-el></outer-el>
The magic is found in this call:
this.getRootNode().host
This will be the element that has the shadow DOM in which your component lives.
We are using Polymer 2 for Building an App. We have Web Components deriving from Polymer Elements and pure ES6 classes that handles business logics. We have defined ES6 classes in html files and imported them via html import to where classes should be used.
myclass.html
<script>
class MyClass {
constructor(){
this.text = "MyClass";
}
getText(){
return this.text;
}
}
</script>
my-app.html
<link rel="import" href="../bower_components/polymer/polymer-element.html">
<link rel="import" href="myclass.html">
<dom-module id="my-app">
<template>
<div>{{text}}</div>
</template>
<script>
class MyApp extends Polymer.Element {
static get is() { return 'my-app'; }
static get properties() {
return {};
}
constructor(){
super();
var myObject = new MyClass();
this.text = myObject.getText();
}
}
window.customElements.define(MyApp.is, MyApp);
</script>
</dom-module>
This is running perfect with polymer serve via the source.
But when we do a Polymer Build and run via the build it gives the following error.
my-app.html:1 Uncaught ReferenceError: MyClass is not defined
at new MyApp (my-app.html:1)
at my-app.html:1
This was solved by modifying the MyClass like below. adding static get properties method.
<script>
class MyClass {
constructor(){
this.text = "MyClass";
}
static get properties() {
return {
};
}
getText(){
return this.text;
}
}
I'm trying to upgrade a Polymer 1.8 component to Polymer 2.
As explained in the documentation, behaviors are replaces by class mixin, but I'm not really confident with theses. After some search, about how to replace the iron-resizable-behavior, I'm not able to find how to build it.
Is there someone to show me where I can find some documentation on it or/and explain how can I design mixins to react on events?
Thanks
Hybrid Behavior
In the 2.0-preview branch of <iron-resizable-behavior>, Polymer.IronResizableBehavior is a hybrid behavior (i.e., defined as an object instead of a class mixin). Polymer 2 provides Polymer.mixinBehaviors() to merge one or more hybrid mixins with a class.
Usage:
class NEW_CLASSNAME extends Polymer.mixinBehaviors(HYBRID_MIXINS_ARRAY, SUPERCLASSNAME) { ... }
Example:
class MyView1 extends Polymer.mixinBehaviors([Polymer.IronResizableBehavior], Polymer.Element) {
static get is() { return 'my-view1'; }
connectedCallback() {
super.connectedCallback();
this.addEventListener('iron-resize', this._logResizeEvent);
}
disconnectedCallback() {
super.disconnectedCallback();
this.removeEventListener('iron-resize', this._logResizeEvent);
}
_logResizeEvent(e) {
console.debug('resize', e);
}
}
window.customElements.define(MyView1.is, MyView1);
Behavior-Class Mixin
You could create a behavior-class mixin like this:
const MyMixin = (superclass) => class extends superclass {
foo() {
console.log('foo from MyMixin');
}
};
Then, you'd use it in your Polymer element like this:
class MyView1 extends MyMixin(Polmer.Element) {
onClick() {
this.foo(); // <-- from MyMixin
}
}
Hybrid Behavior + Behavior-Class Mixin
You could use hybrid behaviors and behavior-class mixins together like this:
class MyView1 extends Polymer.mixinBehaviors([Polymer.IronResizableBehavior], MyMixin(Polmer.Element)) {
onClick() {
this.foo(); // <-- from MyMixin
console.log(this._interestedResizables); // <-- from Polymer.IronResizableBehavior
}
}
As per documentation, you only need to do the following:
Change:
class MyElement extends Polymer.Element {
// Implementation
}
To:
class MyElement extends Polymer.IronResizableBehavior(Polymer.Element) {
// Implementation
}
Does this work for you?