Issue
Is there any way in an effect that I can determine which Angular signal caused an effect to execute? I have two signals and one effect in a component. For one signal, I want to start a timer function within a service. The other signal is based on the changing conditions in the service timer function and can change frequently.
I have a workaround solution but it would be useful to know which signal caused the effect. The work around is to set an isTimerRunning boolean value in the service when the timer starts.
Here is my code:
import { Injectable, Signal, WritableSignal, signal } from '@angular/core';
import { Observable, Subscription, merge, fromEvent, timer, BehaviorSubject } from 'rxjs';
import { environment } from '../../environments/environment';
import { LocalStorageService } from './storage.service';
export interface IIdleTimeoutModel {
expired: boolean;
expiring: boolean;
}
@Injectable({
providedIn: 'root',
})
export class IdleTimeoutService {
constructor(private localStorageService: LocalStorageService) { }
idleTimeout: IIdleTimeoutModel = { expired: false, expiring: false }
private idleSignal: WritableSignal<IIdleTimeoutModel> = signal(this.idleTimeout);
readonly idleSignalModel: Signal<IIdleTimeoutModel> = this.idleSignal.asReadonly();
private idleEventTriggers: Observable<any> = new Observable();
private timer: Subscription = new Subscription();
private timeOutMilliSeconds: number = 1000;
private timeOutWarningMilliSeconds: number = 1000;
private isTimerRunning: boolean = false;
public startWatching(): void {
if (!this.isTimerRunning) {
this.timeOutMilliSeconds = environment.idleTimeInMilliseconds;
this.timeOutWarningMilliSeconds = environment.idleTimeWarningInMilliseconds;
this.idleEventTriggers = merge(
fromEvent(document, 'click'),
fromEvent(document, 'mousedown'),
fromEvent(document, 'keypress')
);
this.idleEventTriggers.subscribe((res) => {
this.resetTimer();
});
this.startTimer();
}
}
private startTimer() {
this.isTimerRunning = true; // <-- this is what I am currently doing to prevent the signal from staring the timer multiple times
let timeoutModel: IIdleTimeoutModel = {
expired: false,
expiring: false,
};
let initDatetime = new Date();
let timeoutSeconds = this.timeOutWarningMilliSeconds / 1000 + initDatetime.getSeconds();
let warningSeconds = this.timeOutMilliSeconds / 1000 + initDatetime.getSeconds();
let expiringDatetime = new Date().setSeconds(timeoutSeconds);
let expiredDatetime = new Date().setSeconds(warningSeconds);
this.localStorageService.set('expiringDatetime', expiringDatetime.toString());
this.localStorageService.set('expiredDatetime', expiredDatetime.toString());
// timer
this.timer = timer(1000, 5000).subscribe((response) => {
let nowDatetime = new Date();
// expiringDatetime
let checkExpiringDatetimeValue = this.localStorageService.get('expiringDatetime');
if (checkExpiringDatetimeValue) {
if (nowDatetime.getTime() >= parseInt(checkExpiringDatetimeValue)) {
this.idleSignal.set({ expiring: true, expired: false })
} else {
this.idleSignal.set({ expiring: false, expired: false })
}
} else {
this.resetTimer();
}
// expiredDatetime
let checkExpiredDatetimeValue = this.localStorageService.get('expiredDatetime');
if (checkExpiredDatetimeValue) {
if (nowDatetime.getTime() >= parseInt(checkExpiredDatetimeValue)) {
if (timeoutModel.expired === false) {
timeoutModel.expired = true;
this.idleSignal.set({ expiring: false, expired: true })
}
} else {
if (timeoutModel.expired === true) {
timeoutModel.expired = false;
this.idleSignal.set({ expiring: false, expired: true })
}
}
} else {
this.resetTimer();
}
});
}
public resetTimer() {
this.timer.unsubscribe();
this.idleSignal.set({ expiring: false, expired: false })
this.startTimer();
}
public stopTimer() {
this.isTimerRunning = false;
this.timer.unsubscribe();
}
}
Component (app component) :
// Variables declare in the component:
isLoggedIn = this.appService.isLoggedInSignal; // this is a signal in our app service that is set when a user log in
private idleTimeoutSignalModel = this.idleTimeoutService.idleSignalModel; // this is a signal from the IdleTimeoutService service above. this will change based on the timer function
In the constructor:
effect(() => {
if (this.isLoggedIn() && environment.idleTimeout) {
const idleModel = this.idleTimeoutSignalModel();
this.idleTimeoutService.startWatching(); // <-- this is the function I want to only run once based on isLoggedIn signal chnaging
if (idleModel.expiring) {
this.dialogService.openTimeOutDialog('Extend Session?', 'Due to inactivity, your login session will expire shortly. Do you want to continue?');
} else {
this.dialogService.closeDialogById('timeout-dialog');
}
if (idleModel.expired) {
try {
this.broadcastService.publish({
type: 'mnsso-logout',
payload: 'true',
});
} catch {
console.log('broadcast error');
}
this._authService.logout();
}
}
});
Solution
Effects are fired when their reactive node is marked as dirty.
At the time of writing there is no tracking of which signal makes another one dirty.
So there is no way to determine which signal triggers an effect.
Answered By - Matthieu Riegler
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.