Unwanted event trigger on mouseover Angular HTML - html

I have the below partial HTML in my Material Table Angular application.
//HTML
<button mat-icon-button class="mat-18" (click)="updateStatus(element.id)"
[disabled]="validateToWork(element.reachedTime)">
<mat-icon>edit</mat-icon>
</button>
//Typescript
validateToWork(reachedTime:any):boolean{
if(reachedTime&& new Date(this.datePipe.transform(reachedTime, DATE_WITH_TIME)) < new Date())
return true;
else
return false;
}
The above code works fine and the edit button is disabled. But what i observed is when i have a console.log(reachedTime) in the typescript like,
console.log(reachedTime)
if(reachedTime&& new Date(this.datePipe.transform(reachedTime, DATE_WITH_TIME)) < new Date())
I could see the event is triggered on every mouseover i do on the table. Its weird for me and not sure. How can i stop that?
Below is the console log i have
Any help ?

Why
One of the rule with angular, is that you should try to avoid using method inside the html.
doing so
<button mat-icon-button class="mat-18" (click)="updateStatus(element.id)"
[disabled]="validateToWork(element.reachedTime)">
<mat-icon>edit</mat-icon>
</button>
Will force [disabled] to be recalculated each time you make any interaction with the page, mouseover being one.
To test this out, just scroll or click somewhere, you should have the same result.
How to fix it
Add the logic inside a pipe
import { DatePipe } from '#angular/common'
import { Pipe, PipeTransform } from '#angular/core'
import { DATE_WITH_TIME } from 'from/some/where'
#Pipe({
name: 'validateToWork',
})
export class ValidateToWorkPipe implements PipeTransform {
constructor(private datePipe: DatePipe) {}
transform(reachedTime: any): boolean {
// <- do add a definition, try to never use any
if (reachedTime && new Date(this.datePipe.transform(reachedTime, DATE_WITH_TIME)) < new Date())
return true
else return false
}
}
And use it in your code like follow
<button mat-icon-button class="mat-18" (click)="updateStatus(element.id)"
[disabled]="element.reachedTime | validateToWork">
<mat-icon>edit</mat-icon>
</button>
Further more
Do not forget to declare it in your module under the declarations: [] or in the imports: [] if you did create a module for that specific pipe
If you declares it directly, you won't be able to use it somewhere else.
If this is an object, you will have to manually trigger the update

Related

how to refresh UI without reloading the page in Angular

I have a multiple charts in my page and I'm trying to make a delete call but some reason my chart UI is not updating immediately when I click the delete button. I always need to refresh the browser in order to see the changes.
I uploaded the full code for this two component her https://stackblitz.com/edit/angular-ivy-nnun96 so I would be really appreciated if I can get any suggestion on how to make the UI remove the Chart immediately when the user press Delete button.
Mc Chart List TS
deleteChart(){
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
});
}
Mc Chart List HTML
<button mat-menu-item (click) = "deleteChart()" *ngIf = "chart.hasAccess && chart.canEdit && !chart.isPublished">Delete Chart</button>
Parent HTML
<mc-chart-list [chart]="chart" [editMode]="true" [wsType]="workspace.type"></mc-chart-list>
Parent TS
ngOnInit(): void {
this.charts = this.workspace.charts;
}
It look like this right now
You can use ChangeDetectorRef to detect changes on the view.
import {ChangeDetectorRef} from '#angular/core';
constructor(private ref: ChangeDetectorRef)
deleteChart(){
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
this.ref.detectChanges();
});
}
Note: Remove changeDetection: ChangeDetectionStrategy.OnPush (if you are using it)

How to render component by adding the component using innerHTML?

I have a component A which only contain a div with an id and a buttons that renders a component inside the div using innterHTML document.getElementById('my-router-outlet').innerHTML = '<app-component-b-page></app-component-b-page>';. But this is not rendering I wonder why?.
I'm trying to avoid using ngIf to be a selector for which component should be rendered for performance reason. Also if I clear the innerHTML does the resources of that component will be cleared?
Okay so a few things here
innerHTML = '<app-component-b-page></app-component-b-page>' is never going to work, angular wont recognise the angular component tag from a innerHTML call
using *ngIf wont affect the performance of the page, so doing the following
<app-component-b-page *ngIf="value === true"></app-component-b-page>
is probably you best option here
If you really don't want to use *ngIf you can use #ViewChild and ComponentFactoryResolver
In your HTML
<!-- this is where your component will be rendered -->
<div #entry></div>
In your component
import { Component, OnInit, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '#angular/core'
import { YourComponent } from ... // import the component you want to inject
// ...
export class ...
#ViewChild('entry', {read: ViewContainerRef, static: true }) entry: ViewContainerRef;
constructor(
private _resolver: ComponentFactoryResolver
) {}
showComponent() {
const factory = this._resolver.resolveComponentFactory(YourComponent);
// this will insert your component onto the page
const component = this.entry.createComponent(factory);
}
// and if you want to dynamically remove the created component you can do this
removeComponent() {
this.entry.clear();
}
You are adding the element to the dom directly and it's not rendered by Angular.
You should go for the *ngIf.

Angular 5 share variable between two components

I have a shared variable I want to declare in both component and I want the components to have shared values. I was wondering if this can be done.
A.html
...
<button (click)="sendInfo(a,b,c)" > </button>
...
B.html
...
<div *ngIf="showData" > {{loadData()}} </div>
...
A.component.ts
...
showData = false;
sendInfo(a: string, b:string, c:string) {
//calling webservice
showData = true;
}
...
B.component.ts
...
showData = false;
//when button from A.html clicked
showData = true;
So in B.component.ts I want to set the showData to true if the button from A.html is clicked.
How does B.component.ts know when A.component.ts has been changed?
How do I set this shared variable between two component?
If you want to share a variable between two components use a service and observable using ReplaySubject:
export class Service {
sharedVariable$ = new ReplaySubject(1);
updateValue(value) {
this.sharedVariable$.next(value);
}
}
In components inject the service:
class Component {
constructor(public service: Service) {}
}
And use in html:
<span>{{service.sharedVariable$ | async}}</span>
<button (click)="service.updateValue(55)">set 55</button>
This is much better than to share variable using component bindings and future proof.

Angular 5 - Auto-reload the HTML page of the specific component at some fixed intervals

The manual solutions for Auto Reloading the HTML page of a specific component:
Either by navigating to the HTML page on click.
Or calling the ngOnInit() of that component on click.
I am doing it manually using a click event from the HTML code as follows:
HTML Code: app.component.html
<button (click) = reloadPage()>
TS Code: app.component.ts
reloadPage() {
// Solution 1:
this.router.navigate('localhost:4200/new');
// Solution 2:
this.ngOnInit();
}
But I need to achieve this automatically. I hope I am clear. The page should auto-reload after some specific interval and call the ngOnInit() on each interval.
Add correct call to setInterval anywhere in your call:
setInterval(() => reloadPage(), 150000); and inside the method reloadPage put the same logic you have for the button.
An example:
Just put the reloadPage function call inside the constructor:
export class SomeComponent {
constructor() {
setInterval(() => this.reloadPage(), 150000);
}
reloadPage() {
// anything your button doeas
}
}
also note, that correct call of setInterval would be:
setInterval(() => this.reloadPage(), 150000);
Note: My answer just fixes the code you presented. But it seems there is some bigger logical misunderstanding of "reloading page" in angular and using ngOnInit

In angular2, how to call a function inside an element without mouse event?

Below is part of code in parent component, I already get the enable value from eventEmitter in its child component, which is enable=true.
<img src="{{currentImg}}" alt="Play Image not found" (click)="onMouseClick()">
<pause-button (isPauseBtnClicked)="enable = $event"></pause-button>
status is: {{enable}}
Then how can I assign a value for currentImg="someImg.png" after it listened the eventEmitter(enable=true)? Should I write a function? if so, how can I call that function in img tag without any mouse event?
I konw with mouse click event, things becomes easier, currentImg can be assign a value inside function.
onMouseClick() {
this.currentImg = this.clickedImg;
}
Look I don't know what you want to achieve. But writing this answer by thinking that you want to go with EventEmitter way without calling any mouseevent.
Note: Your expectation might be different. But It might help you out. If doesn't, kindly use it as a reference. I might have understood something completely different but purpose is not to guide you in wrong way
<img src="{{currentImg}}" alt="Play Image not found" (click)="onMouseClick()">
<pause-button (isPauseBtnClicked)="fire($event)"></pause-button><br>
status is: {{enable}}<br> // haven't played with status
{{currentImg}}
boot.ts
fire(arg) {
console.log('test start');
//this.animate.subscribe((value) => { this.name = value; });
this.currentImg=arg;
console.log(arg);
}
Working Plunker
PasueButton.ts
#Component({
selector: 'pause-button ',
template: `
________________________________________________________
<br>
I'm child
<br>
<img src="img path" (click)="clickMe()"/>
<= click the img
<br>
_____________________________________________________
`
,
})
export class PasueButton implements OnInit {
#Output() isPauseBtnClicked: EventEmitter = new EventEmitter();
constructor() {
console.log('Constructor called');
}
ngOnInit() {
console.log('onit strat');
}
clickMe()
{
this.isPauseBtnClicked.next('child Img path is copied');
}
}