Hi I have a custom button component. This button component should accept all ButtonHTMLAttributes except for className and style to prevent devs from adding their own styles. I am using TypeScript with React. How can I achieve this? I tried using Omit but it's not working.
I think what you can do is you can overwrite the styles and classNames properties like:
function CustomButton = (props> => {
return (
<button {...props} className={""} style={{}}/>
)
}
I don't know how you annotated types. But you can try the rest parameter concept.
function Button({className, style, ...restProps}) {
// Use the restProps object
}
Related
I have 20+ elements, which all should use the same class (animate.css)
It is super annoying to change all classes if I want to edit the animation, so I saved the animation class in my service in a variable:
animClassSecond = "animate__animated animate__bounceInUp";
But I cant figure out how to add it to [ngClass], this does not work:
[ngClass]="{'select_elem':true, 'btn_2':true, 'dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, 'this.dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, this.dataService.animClassSecond:true}"
[ngClass]="{'select_elem':true, 'btn_2':true, dataService.animClassSecond:true}"
Its either a template error or it does not resolve to the variable. Any ideas?
P.S.: Adding a second [ngClass] attribute also does not work, because the first one is ignored.
is:
[ngClass]="dataService.animClassSecond"
But remember that you need declare the service public in the constructor
constructor(public dataService:DataService){}
NOTE you can use class and [ngClass] in the same tag:
class="select_elem btn_2" [ngClass]="dataService.animClassSecond"
This is probably not achievable in the template since the Angular template language is quite limited.
Just move the logic of ngClass object into your component.ts. There you can use all TypeScript's power
ngOnInit() {
this.ngClassObj = { [dataService.animClassSecond]: true };
}
or if you need it to be dynamic (use this one carefully because it might become a performance issue)
get ngClassObj() {
return { [dataService.animClassSecond]: true };
}
and then
[ngClass]="ngClassObj"
There is a very useful pattern in react called the renderProps pattern (https://reactjs.org/docs/render-props.html) but I'm not sure if this is possible with lit-elements, due to the way the shadow dom isolates the css (meaning any css defined on the renderProp won't be carried into the shadow dom of the component with the renderProp).
Has anyone found a way around this, or a different pattern that enables the same use case as the renderProps pattern ?
Thanks !
EDIT: Here is an example that might make it clearer. Let's imagine a hover-menu component whose job is to display a menu on hover. This menu might need to know the position of the element hovered. And we obviously want to be able to render whatever we want inside it.
So we would like to be able to do something like that (renderMenuContent is a renderProp).
<hover-menu
.renderMenuContent="${(boundingClientRect) =>
html`<div>my menu content which could be positioned using ${JSON.stringify(boundingClientRect)}</div>`
}"
></hover-menu>
Turns out there is indeed no such easy solution as in React, again due to the isolation of the shadow dom.
The best solution is to create a component and use it in the renderProp (this way it can manage its own css classes).
In our example:
<hover-menu
.renderMenuContent="${(boundingClientRect) =>
html`<my-menu-content .boundingClientRect="${boundingClientRect}"></my-menu-content>`
}"
></hover-menu>
class MyMenuContent extends LitElement {
static get properties() {
return { boundingClientRect: { type: Object } };
}
static get styles() {
return css`.my-container { color: red }`;
}
render() {
return html`<div class="my-container">
can be positioned using ${JSON.stringify(this.boundingClientRect)}
</div>`;
}
}
I am trying to update UI automatically, avoiding jQuery. I just want to update a css of a button after a click happened.
Should it be done in html with inline javascript code or typescript component?
Right now I am evaluating the latter, handling CSS in typescript as follows.
The component which changes its alreadyLiked property:
sendLike(recipientId: number) {
this.userService.sendLike(fromUserId, this.user.id).subscribe(data => {
this.alertifyService.success('You have liked ' + this.user.name);
// any way to update UI without this trash?
const btnLikeMe = <HTMLInputElement> document.getElementById('btnLikeMe');
btnLikeMe.classList.remove('btn-primary');
btnLikeMe.classList.add('btn-success');
btnLikeMe.classList.add('active');
btnLikeMe.disabled = true;
}, error => {
this.alertifyService.error(error);
});
}
After alreadyLiked is changed, I would like to update btnLikeMe css automatically in HTML:
<button id="btnLikeMe" class="btn" (click)="sendLike(user.id)"
[disabled]="alreadyLiked"
[ngClass]="alreadyLiked ? 'btn-success active' : 'btn-primary'"
title="{{alreadyLiked?'You already liked me. Thank you' : 'Like me now!'}}">
Like
</button>
It is working but it seems to be a bad approach.
I am using "#angular/core": "^5.2.0"
You don`t need JQuery for this. NgClass is the right way.
NgClass adds and removes CSS classes on an HTML element.
In some point if you want do to more complex things you can call functions...[ngClass]="getClass()"
Then you can debugging, but you can`t do this in HTML.
When you have to touch the DOM, you should use Renderer2.
My function isn't called when I click the <a... tag.
I have the following code in my component:
public htmlstr: string;
public idUser:number;
this.idUser = 1;
this.htmlstr = `<a (click)="delete(idUser)">${idUser}</a>`;
public delete(idUser){
alert("id " + idUser);
}
My html
<div [innerHTML]="htmlstr"></div>
but the function delete isn't called and does not show the alert.
The <div... is created dynamically
If anyone face same issue and above all answer not working then try my trick :
In HTML :
<button onclick="Window.myComponent.test()"> test </button>
In component :
class
constructor(){
Window["myComponent"] = this;
}
test(){
console.log("testing");
}
Your main issue here, on-top of the things pointed out by #Matt Clyde and #Marciej21592, is that you're trying to dynamically add HTML code that needs to be compiled before it can be used (you're trying to bind to a method and variable).
Some ways of doing this can be seen here.
From the code you have supplied, however, there are much easier ways to accomplish what you are after. For starters, I would have that code in the HTML to begin with and hide/show it as needed with ngIf.
i use this method and its work
public htmlstr: string;
public idUser:number;
this.idUser = 1;
this.htmlstr = `<a id='innerHtmlClick'>${idUser}</a>`
this.htmlstr.querySelector(`innerHtmlClick`).addEventListener('click', () => {
this.delete(idUser);
});
public delete(idUser){
alert("id " + idUser);
}
EventListener listen the event bye using id of innerHtml
I assume that it is not a bug but rather Angular's security measure against XSS attacks - for more information I would suggest taking a look here https://angular.io/guide/security#sanitization-example
I somewhat also fail to understand why you insist on passing the event via string literal instead of just simply using:
<div>
<a (click)="delete(idUser)">${this.idUser}</a>
</div>
Your component has inner Html.
Angular will not allow events inside inner Html portions for security reasons. You can use Child components. to make events from inside of inner Html portions. Create a child component and put your html inside the child component and pass the data by using any angular events between parent and child using Input, Output features in Angular
I don't often use [innerHTML], but it looks like the template string you're using <a (click)="delete(idUser)">${idUser}</a> is referencing ${idUser} when you might have meant ${this.idUser}?
Below code snippet worked for me:-
In component :
ngAfterViewChecked () {
if (this.elementRef.nativeElement.querySelector('ID or Class of the Html element')) {
this.elementRef.nativeElement.querySelector('ID or Class of the Html element').addEventListener('click', this.editToken.bind(this));
}
}
inside constructor parameter:-
constructor( private readonly elementRef: ElementRef) {}
import { ElementRef } from '#angular/core';---> at the top of the file
implement 'AfterViewChecked'
My final objective is don't have to write HTML like this:
<div id='counter'>
{{counter}}
</div>
<div>
<button
id="startButton"
on-click="{{start}}">
Start
</button>
<button
id="stopButton"
on-click="{{stop}}">
Stop
</button>
<button
id="resetButton"
on-click="{{reset}}">
Reset
</button>
</div>
I would like to know if it is possible to create a Polymer-element without using HTML. For example I tried this:
#CustomTag('tute-stopwatch')
class TuteStopWatch extends PolymerElement {
ButtonElement startButton,
stopButton,
resetButton;
#observable String counter = '00:00';
TuteStopWatch.created() : super.created() {
createShadowRoot()..children = [
new DivElement()..text = '{{counter}}',
new DivElement()..children = [
startButton = new ButtonElement()..text = 'Start'
..onClick.listen(start),
stopButton = new ButtonElement()..text = 'Stop'
..onClick.listen(stop),
resetButton = new ButtonElement()..text = 'Reset'
..onClick.listen(reset)
]
];
}
}
Previous code creates HTML and shadow root correctly, but it doesn't create the binding between the #observable counter and the text of the DivElement.
I know that this is caused because I am trying to create the shadow root after the element has been instantiated/created. So that I should create the template of the element in other place before the template has been bound with its observable.
You can write a manual data binding like this:
changes.listen((changes) {
for (var change in changes) {
if (change.name == #counter) {
myDivElement.text = change.newValue;
}
}
});
changes is a property of the Observable class, which PolymerElement mixes in. (This is difficult to see in the API reference, as it currently doesn't show a class' mixins or the mixed in properties and methods.)
Polymer seems to be mostly about enabling declarative html based bindings. It may be worth exploring using custom elements and shadow dom directly, as you're not really using polymer for anything in this example. To do this you need to change the class definition to:
class TuteStopWatch extends HtmlElement with Observable {
...
}
And register your element with document.register(). You also need to include the polymer.js polyfill for custom elements.