How to pass a function as a property to lit element? - lit-element

I have the following lit web component
<my-element></my-element>
Inside my-element is a button that should call the parent web component test function when clicked.
How can I do this using lit elements?

This can be done by directly setting the property on my-element using Property Expressions.
For example, in the block of code below. my-element has a property onPress. The parent-el can set the property directly by setting .onPress=${/* Function */}.
This is a declarative alternative to the following that could be put in the parent element:
this.shadowRoot.querySelector('my-element').onPress = () => console.log('pressed');
Full example:
#customElement('parent-el')
export class ParentEl extends LitElement {
render() {
return html`<my-element
.onPress=${() => console.log('called')
}></my-element>`;
}
}
#customElement('my-element')
export class MyEl extends LitElement {
#property()
onPress = () => {};
render () {
return html`<button #click="${this.onPress}">Call passed function</button>`;
}
}
And live example: https://lit.dev/playground/#gist=5da1cb16373f7fca9c8a7cbdc5a22e4a

Related

child property from parent Promise

I want to create a navigation component for my project. The shell fetches a json with chapter info, these are passed to nav-element, which recursively calls itself to render the navigation tree.
shell.js
import { LitElement, html, css } from 'lit-element';
import {until} from 'lit-html/directives/until.js';
import './nav-element.js';
export class Shell extends LitElement {
static get properties() {
return {
configjson : { type: Array }
};
}
constructor() {
super();
this.configjson = fetch('./src/convertjson_test.json').then(res => res.json());
}
render() {
return html`
<main>
some content
<nav-element .chapters=${until(this.configjson, [])} root></nav-element>
</main>
`;
}
}
customElements.define('shell', Shell);
nav-element.js
import { LitElement, html, css } from 'lit-element';
import {until} from 'lit-html/directives/until.js';
import {repeat} from 'lit-html/directives/repeat.js';
export class NavElement extends LitElement {
static get properties() {
return {
chapters: {type: Array},
root: {type: Boolean} //to mark the root node
};
}
static get styles() {
return css`
.navheader {
display: none;
}
.navheader[active] {
display: block;
}
`;
}
render() {
return html`
<div class="navHeader" ?active="${this.root}">header</div>
${until(repeat(this.chapters, (chapter) => chapter.pos, (chapter) => html`<div>${chapter.n}<nav-element .chapters=${chapter.c}></nav-element></div>`))}
`;
}
}
customElements.define('nav-element', NavElement);
The problem is, that the configjson Promise is passed as property and not yet resolved by the time the nav-element is called, so i get the error:
Uncaught (in promise) TypeError: this.chapters is undefined
Searched all lit-element and lit-html documentation, the until directive resolved the issue in the shell, but not in the nav-element.
The same coding pattern worked fine in Polymer 2 (&3, although with ajax instead of fetch). Does anyone know how to solve this using lit-element only?
There is a time frame between the construction of NavElement and the assignment of the chapters property where chapters is undefined. It might be safe to initialize chapters in the component itself rather than in Shell's until directive, or at least provide a fallback value in the template:
export class NavElement extends LitElement {
static get properties() {
return {
chapters: {type: Array},
// ...
};
}
constructor() {
super();
this.chapters = [];
}
// or
render() {
return html`
...
${repeat(this.chapters || [], chapter => ...)}
`;
}
}
Also, you've (correctly) declared the chapters property as an Array but you're wrapping the repeat directive in an until (as if it was a Promise I guess?). Here there are two things going on:
the repeat() call returns a DirectiveFn and not a Promise like until expects. If chapters was a Promise, the correct way to combine until and repeat would have been:
until(
this.chaptersPromise.then(chapters => repeat(chapters, chapter => html`...`)),
html`Loading...`,
)
but...
chapters is not a Promise: the until call in the parent component resolves it and passes the result.
As for the Shell component: until used in this way should work, however its intended use is to
Render placeholder content until the final content is available
[from lit-html docs]
To make the most of it, use it to temporarily render a loading template instead of <nav-element>:
render() {
return html`
${until(
this.configJson.then(chapters => html`<nav-element .chapters=${chapters}></nav-element>`),
html`Loading chapters`,
)}
`;
}
Also, not a big deal, but here the configJson property is declared as an Array but is actually a Promise.

Adonis: ReferenceError view is not defined

I have controller like this
class TicketController {
index(){
return view.render('tickets')
}
}
and create file in resource\view\tickets.edge and my route is
const Route = use('Route')
Route.resource('tickets', 'TicketController');
when I go to http://127.0.0.1:3333/tickets show me this error
ReferenceError
view is not defined
You need to use view object from http context :
index ({ view }) {
return view.render('hello-world')
}
Adonis documentation example
I had forgotten to import view class and fix it by this code:
const view = use('View');
class TicketController {
index(){
return view.render('tickets')
}
}

How do you access a lit-element render root after overriding createRenderRoot?

I've been playing around with lit-element, and I want to grab my custom element to run a getElementById. The only examples I can find use the shadow root (since that's the recommended way to use lit-element). How do you get access to your custom element to run a query on just your element?
import { LitElement, html }
from 'https://unpkg.com/lit-element/lit-element.js?module';
class RenderRootTest extends LitElement {
constructor() {
super();
}
render () {
const renderRoot = this.shadowRoot; //Won't work, because I'm overriding the shadowroot
return html`
<div>Rendered</div>
${renderRoot ?
html`<div>Render root found</div>` :
html``
}
`;
}
createRenderRoot() {
return this;
}
}
customElements.define('render-root-test', RenderRootTest);
I found the answer myself after enough tinkering. You can either use this.renderRoot or just this. However, note that certain methods such as .getElementById don't seem to exist. If anyone has any additional details on this topic, I would appreciate it.
ex.
import { LitElement, html }
from 'https://unpkg.com/lit-element/lit-element.js?module';
class RenderRootTest extends LitElement {
constructor() {
super();
}
render () {
const renderRoot = this.renderRoot;
return html`
<div>Rendered</div>
${renderRoot ?
html`<div>Render root found</div>` :
html``
}
`;
}
createRenderRoot() {
return this;
}
}
customElements.define('render-root-test', RenderRootTest);
Just reference this which is the instance of the custom element. The shadowRoot is created and returned by LitElement's createRenderRoot() so if you don't create one and instead of this.shadowRoot return this--which is the node itself--that is what the content is rendered into, there is no shadowRoot.

TypeScript Node.js this command undefined

I started writing Ts, but now I'm a novice.
export class CrudController<AddDto extends CoreAddDto>{
protected readonly addDtotype: new () => AddDto;
constructor(addDtotype: (new () => AddDto)) {
this.addDtotype = addDtotype;
}
public async add(ctx: any, next: any) {
/// this undefined !!! ///
const dto = new this.addDtotype();
Object.assign(dto, ctx.request.body);
}
}
class FooController extends
CrudController<FooDto> {
constructor {
super(FooDto);
}
}
Why didn't I understand this command undefined?
this works
public add = async (ctx: any, next: any) => { }
method works as a property, why ??
This isn't a bug, your linter is correct because your add method is unbound. Basically there are two types of function declarations in JavaScript, regular function statements and arrow functions, and the primary difference is that regular function statements provide their own definition for this within their scope, while arrow functions binds to the current this in the scope they are defined.
So when a method is a normal function:
add(ctx: any, next: any) {
const dto = new this.addDtotype();
Object.assign(dto, ctx.request.body);
}
this points to add, and not to your CrudController but when you change it to an arrow:
public add = async (ctx: any, next: any) => { }
this is not overridden, and so it still points to your component. Note that you do not need to do this for built-in methods like render or componentDidMount, only for methods you define.
So basically anytime you want to use this in reference to your component while inside a method, you should define it like method = () => {}. If you seriously hate arrow functions, you can also bind methods in the constructor like so:
constructor(props){
super(props)
this.add = this.add.bind(this)
this.method = this.method.bind(this)
}
TSLint has a great rule to help prevent making this mistake (it's an easy one to make) called "no-unbound-method"

How to use React DnD with styled component?

When wrapping my styled component in connectDragSource I get the following error:
Uncaught Error: Only native element nodes can now be passed to React
DnD connectors.You can either wrap PaneItemText__StyledItem into a
<div>, or turn it into a drag source or a drop target itself.
The first suggestion from this message is to wrap my styled component in a <div>, but this will mess with my layout and would prefer not to do this.
I'm not sure what the second option is suggesting - would anybody be able to clarify?
Below is an rough example what I am doing:
import React, { Component } from 'react';
import styled from 'styled-components';
import { DragSource } from 'react-dnd';
const StyledComponent = syled.div`
...
`;
class MyComponent extends Component {
...
render() {
const { connectDragSource } = this.props;
return connectDragSource(<StyledComponent />)
}
}
const itemSource = {
beginDrag(props) {
/* code here */
},
endDrag(props) {
/* code here */
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
export default DragSource('foo', itemSource, collect(MyComponent);
You should use Styled Component's innerRef to get the underlying DOM node, then you can call your connectDragSource to it.
In your case, it should be like this:
class MyComponent extends Component {
...
render() {
const { connectDragSource } = this.props;
return (
<StyledComponent
innerRef={instance => connectDragSource(instance)}
/>
)
}
}
You can also look at my implementation of Knight component for the official chess tutorial as a reference.
It is also accessible through CodeSandbox.
If you are using multiple connectors you can do the following:
<MyStyledComponent
innerRef={instance => {
connectDragSource(instance);
connectDropTarget(instance);
}}
/>
Source: https://github.com/react-dnd/react-dnd/issues/347#issuecomment-221703726

Resources