Is there any better way to pass function to props with LitElement? - polymer

I'm using polymer LitElement and i tried to pass a function to props but did'nt work, this is the way i found to work, but its awfull... Any better suggestions?
import { LitElement, html, customElement, property } from 'lit-element';
#customElement('my-element')
export class MyElement extends LitElement {
onButtonClick = function name (){
console.log("Clicked!!")
}
render() {
return html`
<c-button text="Button Name" onClick="${this.onButtonClick}" />
`;
}
}
#customElement("c-button")
export class CButton extends LitElement{
#property() text;
#property() onClick;
handleClick(){
let fct = eval(`( ${this.onClick} )` )
fct();
}
render(){
return html`
<button #click="${this.handleClick}" > ${this.text} </button>
`
}
}

By default lit-html data bindings set an attribute which means it'll have to convert the value to a string.
Instead of onClick="${this.onButtonClick}" prefix it with . like this .onClick="${this.onButtonClick}". That will set a property in JavaScript instead and the method will be passed by reference.

#abraham's answer is good for the general case: you can set properties, including functions, with the . syntax.
However, if you're specifically dealing with events, then I would use event bindings (# syntax) and make sure the the event you're interested in is either bubbling and composed (as click is) so that it'll propagate out of the child component, or re-dispatched by the child component. Events are a good model, I'd use them for event-like things.

Related

React how to write inline style with custom properties [duplicate]

I want to define jsx like this:
<table style={{'--length': array.lenght}}>
<tbody>
<tr>{array}</tr>
</tbody>
</table>
and I use --length in CSS, I also have cells that have --count that shows count using CSS pseudo selector (using the counter hack).
but typescript throws error:
TS2326: Types of property 'style' are incompatible.
Type '{ '--length': number; }' is not assignable to type 'CSSProperties'.
Object literal may only specify known properties, and ''--length'' does not exist in type 'CSSProperties'.
is it possible to change type of style attribute to accept CSS variable (custom properties) or is there a way to force any on style object?
Like this:
function Component() {
const style = { "--my-css-var": 10 } as React.CSSProperties;
return <div style={style}>...</div>
}
Or without the extra style variable:
function Component() {
return <div style={{ "--my-css-var": 10 } as React.CSSProperties} />
}
If you go to the definition of CSSProperties, you'll see:
export interface CSSProperties extends CSS.Properties<string | number> {
/**
* The index signature was removed to enable closed typing for style
* using CSSType. You're able to use type assertion or module augmentation
* to add properties or an index signature of your own.
*
* For examples and more information, visit:
* https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
*/
}
That page gives examples of how to solve the type error by augmenting the definition of Properties in csstype or casting the property name to any.
You can add a type assertion to the variable. i.e. {['--css-variable' as any]: value }
<table style={{['--length' as any]: array.length}}>
<tbody>
<tr>{array}</tr>
</tbody>
</table>
Casting the style to any defeats the whole purpose of using TypeScript, so I recommend extending React.CSSProperties with your custom set of properties:
import React, {CSSProperties} from 'react';
export interface MyCustomCSS extends CSSProperties {
'--length': number;
}
By extending React.CSSProperties, you will keep TypeScript's property checking alive and you will be allowed to use your custom --length property.
Using MyCustomCSS would look like this:
const MyComponent: React.FC = (): JSX.Element => {
return (
<input
style={
{
'--length': 300,
} as MyCustomCSS
}
/>
);
};
you can simply put this module declaration merge using string templates at the top of the file or in any .d.ts file, then you will be able to use any CSS variable as long it starts '--' and that is string or number
import 'react';
declare module 'react' {
interface CSSProperties {
[key: `--${string}`]: string | number
}
}
for example
<div style={{ "--value": percentage }} />
import "react";
type CustomProp = { [key in `--${string}`]: string };
declare module "react" {
export interface CSSProperties extends CustomProp {}
}
put this in your global.d.ts file
try:
<table style={{['--length' as string]: array.lenght}}>
...
</table>
I would like to add a different approach by using document.body.style.setProperty, and maybe if your css variable will be affected by certain props you can put it in a useEffect like this:
useEffect(() => {
document.body.style.setProperty(
"--image-width-portrait",
`${windowSize.width - 20}px`
);
}, [windowSize])
Later inside your css file you can call it like this:
width: var(--image-width-portrait);
These are (well almost) all valid approaches to solve this, but there is another.
You could add the ref to your element and set the style where ever. I know that this would be quite possibly an improper use of useEffect but if you have something in useEffect that needs to happen on component mount then:
const tableRef = useRef<HTMLTableElement | null>(null)
useEffect(() => {
tableRef?.current?.style.setProperty('--length': array.lenght);
}, [])
...
<table ref={tableRef}>
<tbody>
<tr>{array}</tr>
</tbody>
</table>
This can also be used on any interaction and event

Run a 'constructor' or function, after class fields initialized, in a sane way?

I'd like to use ES6 public class fields:
class Superclass {
constructor() {
// would like to write modular code that applies to all
// subclasses here, or similarly somewhere in Superclass
this.example++; // does NOT WORK (not intialized)
//e.g. doStuffWith(this.fieldTemplates)
}
}
class Subclass extends Superclass {
example = 0
static fieldTemplates = [
Foo,
function() {this.example++},
etc
]
}
Problem:
ES6 public fields are NOT initialized before the constructors, only before the current constructor. For example, when calling super(), any child field will not yet have been defined, like this.example will not yet exist. Static fields will have already been defined. So for example if one were to execute the code function(){this.example++} with .bind as appropriate, called from the superclass constructor, it would fail.
Workaround:
One workaround would be to put all initialization logic after all ES6 public classes have been properly initialized. For example:
class Subclass extends Superclass {
example = 0
lateConstructor = (function(){
this.example++; // works fine
}).bind(this)()
}
What's the solution?
However, this would involve rewriting every single class. I would like something like this by just defining it in the Superclass.constructor, something magic like Object.defineProperty(this, 'lateConstructor', {some magic}) (Object.defineProperty is allegedly internally how es6 static fields are defined, but I see no such explanation how to achieve this programatically in say the mozilla docs; after using Object.getOwnPropertyDescriptor to inspect my above immediately-.binded-and-evaluated cludge I'm inclined to believe there is no way to define a property descriptor as a thunk; the definition is probably executed after returning from super(), that is probably immediately evaluated and assigned to the class like let exampleValue = eval(...); Object.defineProperty(..{value:exampleValue})). Alternatively I could do something horrible like do setTimeout(this.lateConstructor,0) in the Superclass.constructor but that would break many things and not compose well.
I could perhaps try to just use a hierarchy of Objects everywhere instead, but is there some way to implement some global logic for all subclasses in the parent class? Besides making everything lazy with getters? Thanks for any insight.
References:
Run additional action after constructor -- (problems: this requires wrapping all subclasses)
Can I create a thunk to run after the constructor?
No, that is not possible.
How to run code after class fields are initialized, in a sane way?
Put the code in the constructor of the class that defines those fields.
Is there some way to implement some global logic for all subclasses in the parent class?
Yes: define a method. The subclass can call it from its constructor.
Just thought of a workaround (that is hierarchically composable). To answer my own question, in a somewhat unfulfilling way (people should feel free to post better solutions):
// The following illustrates a way to ensure all public class fields have been defined and initialized
// prior to running 'constructor' code. This is achieved by never calling new directly, but instead just
// running Someclass.make(...). All constructor code is instead written in an init(...) function.
class Superclass {
init(opts) { // 'constructor'
this.toRun(); // custom constructor logic example
}
static make() { // the magic that makes everything work
var R = new this();
R.init(...arguments);
return R;
}
}
class Subclass extends Superclass {
subclassValue = 0 // custom public class field example
init(toAdd, opts) { // 'constructor'
// custom constructor logic example
this.subclassValue += toAdd; // may use THIS before super.init
super.init(opts);
// may do stuff afterwards
}
toRun() { // custom public class method example
console.log('.subclassValue = ', this.subclassValue);
}
}
Demo:
> var obj = Subclass.make(1, {});
.subclassValue = 1
> console.log(obj);
Subclass {
subclassValue: 1
__proto__: Superclass
}

Angular 2 functions library

In my application I'll need to create a functions library to be accessed from different components. What is the best way to do this? Do I need to make a service with all the functions or a class or a module or a component?
In this library if I call a function from a component and this function need to access a variable in the caller, will it be possible to do?
If it's just a set of one off helper functions the easiest way is create an #Injectable service with your helper methods then inject that into any component that requires them. You can provide public vars as well so they're accessible in your html.
#Injectable()
export class Helpers {
helper1() { return "hello"; }
helper2(x) { return x + 1 }
}
#Component({
providers: [Helpers],
template: "<div>{{helper1()}}</div>" // will display 'hello'
)}
export class MyComponent {
public helper1: Function = Helpers.helper1;
constructor() {
console.log(Helpers.helper2(5)); // will output '6'
}
}
This works great for a simple set of random utility functions. If you need something more in depth please explain and we can provide alternate solutions.

Invariant Violation on changing route

I'm using a file upload component, here's an extract of the relevant part:
class FileUpload extends React.Component {
static propTypes = {
edit_mode: React.PropTypes.bool
};
constructor(props) {
super(props);
this.delete_asset = this.delete_asset.bind(this);
}
delete_asset(id, event) {
}
render() {
return <div>
{this.props.edit_mode ?
<button onClick={() => this.delete_asset()}>Delete</button>
: <span />
}
</div>;
}
}
This component is wired up within a react-router setup, and on a route change, it's one of several nested componentes of the route target.
Whenever I change the route, I get the following exception, even if edit_mode does not change on the route change:
invariant.js:39 Uncaught Invariant Violation: findComponentRoot(..., >0.1.1.1.$0.1.1.0): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID ``.
The exception is not thrown then I omit the onClick handler.
Any ideas?

Extending dart:html classes in dart

I am new to Dart and I wonder if I can, for example, extend the DivElement class to create custom elements in one line of code. I am looking for something like this;
class RedBox extends DivElement {
RedBox(text) {
this.text = text;
this.style.background = "red";
}
}
RedBox myBox = new RedBox("Hello World");
document.body.append(myBox);
Of course I will have much more complex elements with custom functions. But just in general, is this possible?
When I try to run this, i get:
implicit call to super constructor 'DivElement()'
You can extend HTML elements, but there are a few requirements. The one you're running into now is that you need a RedBox.created constructor, and it can only redirect to its super class. created must be the only generative constructor, though you can add factory constructors.
Another requirement is that the element is registered with document.registerElement.
Try adding this:
class RedBox extends HtmlElement {
RedBox.created() : super.created() {
style.background = "red";
}
factory RedBox(text) => new Element.tag('my-redbox')..text = text;
}
document.registerElement('my-redbox', RedBox);
Some note on extending HtmlElement.
Example:
https://api.dartlang.org/1.14.1/dart-html/HtmlDocument/registerElement.html
Pitfalls:
Extending non HtmlElement(e.g. PreElement) throws:
HierarchyRequestError: Custom element XXX is a native PreElement
should be derived from HtmlElement or SvgElement.
Using extendsTag option with registerElement suppresses the above error but causes "new Element.tag('xxx')" to return an instance of HtmElement.
document.registerElement('xxx', XXX, extendsTag:'pre');
Solution(Assuming extending PreElement):
Use 'document.registerElement('xxx', XXX, extendsTag:'pre');' and 'new Element.tag('pre','xxx');'
void main{
document.registerElement('xxx',
XXX,extendsTag: 'pre');
querySelectior('body').append(new XXX()..text = 'hello');
}
class XXX extends PreElement{
XXX.created():super.created(){}
factory XXX(){
return new Element.tag('pre','xxx');
}
}
Dart does not currently support library initialization. You must call document.registerElement in the main.
Tested with 1.14.0