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
Related
I have a TypeScript utility class myUtils.ts as below:
export class MyUtils {
static doSomething(input: string) {
// do something
}
}
I want to call this method in my component's HTML. In order to do that I imported this class in my component
import { MyUtils } from './utils'
and using in the component's HTML like so:
<ng-template #dynamicTableCell let-entry>
{{ MyUtils.doSomething(entry) }}
</ng-template>
The HTML is complaining with Unresolved variable or type MyUtils. Interestingly enough, I am able to do the same MyUtils.doSomething(someEntry) without any error in the component's component.ts file. It is just the HTML where it is complaining.
Can someone please tell me how to fix this problem in the HTML? Thanks in advance.
You can't use it, since template expressions are restricted to referencing members of the componence instance, you need to create the method in your component, or use Angular pipe in your case.
component.ts
import { MyUtils } from './utils';
...
doSomething(entry) {
MyUtils.doSomething(entry);
}
// or
doSomething = MyUtils.doSomething;
You can have a look at Template expressions doc.
I've noticed that classes generated with makeStyles and the use of hooks follow this nomenclature:
while the classes generated with withStyles and the use of HOC follow this one:
Is there a way to use makeStyles (I like to use hooks) but keep the nomenclature of withStyles? I'd like this because it's easier to analyze the html in the browser and pinpoint the class that generated each element.
The second (optional) argument to makeStyles is an object of options to control the behavior of makeStyles. One of the options is a name which is then used as a prefix for the class names. withStyles passes Component.displayName as the name option. You can specify whatever name you want in order to control the class name prefix, for instance in my example below the class name ends up being Hello-root-1.
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(
{
root: {
backgroundColor: "green"
}
},
{ name: "Hello" }
);
export default function App() {
const classes = useStyles();
return (
<div>
<h1 className={classes.root}>Hello CodeSandbox</h1>
</div>
);
}
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.
Having an assets folder with :
/assets/images/image1.png
/assets/svg/svg1.svg
How to include it in an HTML template (not in a CSS file) in order to have angular/webpack to automatically enable cache-busting on it (transforming automaticallt assets/images/image1.jpg to assets/images/image1-4d5678xc0v987654v.jpg?
The goal is to handle cache and refresh it soon ASAP when an existing file gets updated.
With webpack, I used to do a :
<img src="<%=require("./assets/img/image1.jpg")%>" />
The only solution I found with angular is requiring all my images in the .ts file but it's quite a pain to do :
const image1src = require(`../assets/images/image1.jpg`);
class Component {
image1 = image1src; // contains image1-4d5678xc0v987654v.jpg
}
// and in template : <img [src]="image2" />
Is there something simplier ?
ps: I don't want to handle a query paremeter or custom name myself
pps: I don't want to inject these files through CSS (and I know it works when files are getting injected by css)
ppps: using a PWA is not an option in my case
Thanks
I created a pipe for this so I don't need to create variables inside a component as you did.
import {Pipe, PipeTransform} from '#angular/core';
#Pipe({
name: 'imgURL'
})
export class ImgURLPipe implements PipeTransform {
transform(value: string): string {
return require('../../../images/' + value); // specify here a correct src to your folder with images
}
}
To make the require works inside components or pipes, add this to your typings:
declare var require: {
<T>(path: string): T;
(paths: string[], callback: (...modules: any[]) => void): void;
ensure: (
paths: string[],
callback: (require: <T>(path: string) => T) => void
) => void;
};
Inside a template, it looks like this:
<img [src]="'myImage.png' | imgURL">
Remember to add a declaration of the pipe to your module.
are you using angular-cli?
Check this article, it explains how to manage the assets with angular-cli: https://kimsereyblog.blogspot.com/2017/09/manage-assets-and-static-files-with.html
Other solution would be to use css, eg:
background: url('../assets/fonts/roboto-v15-latin-regular.svg')
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.