How to proper update UI when a property changes in Angular 5 - html

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.

Related

How to prevent passing className and style props to component in React

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
}

How to make a button to disable elements in a class

I am trying to create a filtering system for a portfolio site in wordpress (using the Divi Builder) and would love to be able to make a button/buttons to set elements with specific classes on a page to display: none when the button is clicked on.
If anyone has ideas on how this can be done that would be super appreciated.
Thanks
If you can use jQuery, the .show() and .hide() Functions work on jQuery objects.
For example $('.dataholder').hide().filter( (index,element) => $("body").innerHTML == "abc" ).show() will hide all nodes with class dataholder and then show those containing abc.
Otherwise you have to use something like
element = document.querySelector('#dataholder')
element.style.display = 'none'
Or
elements = document.querySelectorAll('.dataholder')
elements.forEach( element => element.style.display = 'none' )

How do you generate dynamic <style> tag content in Angular template at runtime?

I have an Angular component that generates mat-checkbox dynamically at runtime and I need to change the individual background of each checkbox differently with different color and I don't (won't) have the information before hand, only available at runtime.
I have the following ng-template for the checkboxes:
<ng-template #renderCheckbox let-id="id" let-attr="attr">
<mat-checkbox
[checked]="attr.show"
[color]="'custom-' + id"
(change)="onChange($event.checked, attr)">
{{attr.name}}
</mat-checkbox>
</ng-template>
where, attr in the template has the following interface type, these infomation are pulled from Highcharts' series and I didn't want to hardcode the color.
interface LinkedSeriesAttributes {
id: string;
name: string;
index: number;
color: string;
checked: boolean;
}
Since there is no way to create css classes before hand and there is no way to directly apply color to the mat-checkbox, I could only generate the <style>...</style> right at the beginning of my template.
In my component, I have code that will generate the style which would give me something like this:
.mat-checkbox.mat-custom-hello.mat-checkbox-checked .mat-checkbox-background::before {
color: #6E8BC3 !important;
}
.mat-checkbox.mat-custom-world.mat-checkbox-checked .mat-checkbox-background::before {
color: #9ED6F2 !important;
}
...
However, I tried various ways to dump it inside <style> without success. I tried:
<style>{{ dynamicCSSStyles }}</style>
Which, my IDE shows that's an error with the curly braces, although it compiled fine and ran without errors, I got nothing, can't even see the <style> tag.
I also tried to include <style> inside my dynamicCSSStyles variable, and angular just dumped the whole thing out as text...
What's the correct way to generate a <style> in Angular.
I've found a REALLY dirty way of "making this work" but it causes Angular to keep adding the <style> back into the DOM.
First, set encapsulation to ViewEncapsulation.None.
Second, create a function to generate the <style> tag the old fashion way with an id:
updateDynsmicStyleNode() {
const id = 'dynamic-css-styles';
const nativeElm = this.elmRef.nativeElement;
const existing = nativeElm.querySelector(`style#${id}`);
if (!existing) {
const styleTag = document.createElement('style');
styleTag.setAttribute('id', id);
styleTag.innerHTML = this.dynamicCSSStyles;
nativeElm.prepend(styleTag);
} else {
existing.innerHTML = this.dynamicCSSStyles;
}
}
Third, call our function in ngAfterViewChecked:
ngAfterViewChecked() {
this.updateDynsmicStyleNode();
}
I mean while this worked, it is really bad, since moving the mouse around the screen would cause Angular to just continuously reinsert the <style> tag.
Does anyone know some other way more legit to archive this? LOL
You can use ngClass or [class] attribute. Since you can have the styles ready from the component.ts file.
You can do something like this:
Way 1: If you already know what the dynamic ids might be, (like if it always will be 'hello' and 'world')
let dynamicClasses = {};
// Once you get some classes from your logic, you can add them to the object above
dynamicClasses['hello'] = 'custom-hello';
dynamicClasses['world'] = 'custom-world';
// Then in HTML
<mat-checkbox [ngClass]="dynamicClasses"></mat-checkbox>
Way 2: If you dont know what the classes also might be, like if its not always be hello or world, then create a method and call it where required, you might need to do something similar to #codenamezero said.

How to change dir property of the Angular CDK overlay at runtime?

I'm using Angular 10, on click the following function is executed to preform direction change:
private changeHtmlDirection(direction: 'rtl' | 'ltr') {
document.getElementsByTagName("html")[0].dir = direction;
}
It works well, only that the Angular CDK does not update.
I tried to find an API to change Angular CDK's direction at runtime, but couldn't find any.
I saw that there's a BidiModule but it uses only to get the current direction rather than set it.
Is there any solution?
According to the material documentation, you can't change 'dir' on the "html" tag so that affects bidi API. You can see the document at the following link:
bi-directionality document
But if you want to use material bi-directionality you can add the 'dir' directive to a container element in the root component like bellow:
<div [dir]="documentDirection"> </div>
and whenever the 'documentDirection' variable changes, the bidi "change emitter" will be emit.
like following code you can subscribe to it:
constructor(
private dir: Directionality ) {
this.isRtl = dir.value === 'rtl';
this.dir.change.subscribe(() => {
this.isRtl = !this.isRtl;
});
}

How to trigger a change event on a textarea when setting the value via ngModel Binding

I have a <textarea> within a template driven form of an Angular 7 project.
When editing an object, the form is prefilled with the current values. I want to automatically resize the <textarea> when the content has changed via the [(ngModel)]="property" binding by modifying the element-style.
area.style.overflow = 'hidden';
area.style.height = '0';
area.style.height = area.scrollHeight + 'px';
The code generally is working, but I cannot find a suitable event to trigger it.
Subscribing to the change event of the <textarea> is only working on keyboard input. Using (ngModelChange)="adjustTextAreaSize($event)" has the same behavior.
I tried to execute my resizing code at the end of the ngOnInit() function, but the actual html-control seems to not have any content yet at this point.
Does anyone have an idea which event could do the trick here?
Seemed a rather easy task in the beginning, but I'm breaking my had over this for over an hour now... can not be such a difficult task, can it?
Yes there is a very simple solution for this.
Wrap your textarea inside a form and try the code below:-
HTML
<form #form="ngForm">
<textarea>....</textarea>
</form>
TS
#ViewChild('form') ngForm: NgForm;
ngOnInit() {
this.subscription = this.ngForm.form.valueChanges.subscribe(resp =>
{
console.log(resp); // You get your event here
}
)
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
trigger a change event on a textarea when setting the value via ngModel Binding
This will cause infinite triggering if you do so.
If you don't want to monitor the input model change in a more reactive way, a quicker solution (but a bit hacky) will be simply wrap your code inside setTimeout in ngOnInit() or ngAfterViewInit() where you mentioned it was not working.
setTimeout(() => {
updateSize();
});