LitElement with Typescript, cannot access shadowRoot doms - polymer

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')
}
}

Related

HTML Portal - Failed to Execute 'activate' on 'HTMLPortalElement' Portal Element is not associated with a portal context

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);

Polymer Build cannot find ES6 classes which are not Polymer Elements

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;
}
}

Angular (4): pass a class object from child to parent component

I can pass a class object like Person into a child component from parent component without any problems. But I would like to also manipulate that object in child component and pass it back to parent component.
This is the child component class:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify: EventEmitter = new EventEmitter();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
This is the html of this component (excerpt):
<div class="row" (change)="onChange()">
<div class="col-xs-2">
<button md-icon-button >
<md-icon>skip_previous</md-icon>
</button>
</div>
This is the parent component class (excerpt):
public onNotify(company: Company):void {
this.company = company;
}
And the parent component html (excerpt):
<action-panel [company]="company" (notify)="onNotify($event)"></action-panel>
I am doing something wrong because I cannot pass my company object inside the .emit and nothing works.
What is the correct way of achieving two way object binding between components?
Thanks in advance!
You were missing the type on the initialization of the EventEmitter.
You could use the Output binding to implement the two way object binding:
Child component (ts)
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() companyChange: EventEmitter = new EventEmitter<Company>();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.companyChange.emit(this.company);
}
}
Parent component (html)
<action-panel [(company)]="company"></action-panel>
So like this you don't need to declare an extra function onNotify. If you do need the onNotify function, use another name for the output binding:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify: EventEmitter = new EventEmitter<Company>();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
Change it like this to tell TS which Type the EventEmitter should emit:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify = new EventEmitter<Company>(); //<---- On this line!
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
It is a workaround that worked for me, if it is helpful for anyone.
Your parent parent-component.ts would be like;
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'parent',
templateUrl:'./parent.component.html',
styleUrls: ['./parent.component.css']
})
export class Parent implements OnInit {
let parentInstance= this; //passing instance of the component to a variable
constructor() { }
parentMethod(var:<classtyepyourchoice>){
console.log(var);
}
ngOnInit() {
}
}
In you parent.component.html, you would have your child
<child [parent]="parentInstance" ></child>
This object will be available in the child component
Now, in your child component you will receive this like
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'child',
templateUrl:'./child.component.html',
styleUrls: ['./child.component.css']
})
export class Child implements OnInit {
#Input('parent') parent;
constructor() { }
childMethod(yourClassObject){
this.parent.parentMethod(yourClassObject);
}
ngOnInit() {
}
}
Thus, you can pass classobject from your child, like this, it worked for me.

How to create a custom-element "resize" mixin for Polymer 2?

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?

How can I get a list of all properties in a mixin in Polmyer 2.x?

I have a mixin and a class that uses it, but within the mixin (or the class itself), I can't seem to get a list of all properties. this.config is undefined, and this.constructor.config only gives me the list of properties in the current class (not those in the mixin itself).
See below for a more detailed example.
const myMixin = subclass => class extends subclass {
static get config() {
return {
properties: {
mixInVariable: {
type: String,
}
}
}
}
constructor() {
super();
console.log(this.constructor.config.properties);
}
}
class ContainerGrid extends myMixin(Polymer.Element) {
static get is() { return 'container-grid' }
static get config() {
// properties, observers meta data
return {
properties: {
something: {
type: String
}
}
};
}
}
customElements.define(ContainerGrid.is, ContainerGrid);
In the console, I only see the variable something. How can I get a list of all properties (e.g. mixInVariable and something)?
From the constructor, use super.constructor.config.properties to access the inherited properties.
class XFoo extends MyMixin(Polymer.Element) {
...
constructor() {
super();
console.log('constructor(): ', super.constructor.config.properties);
}
}
From get properties(), use super.config.properties.
class XFoo extends MyMixin(Polymer.Element) {
...
static get properties() {
console.log('get properties(): ', super.config.properties);
}
}
codepen