I want to display some messages in a sort off in app notification.
It is supposed to behave like this:
- click on a button
- notification slides down + display text
- notification slides back up
If there are more than one simultaneous message displays the next message after 2 seconds and so on.
What I want is, when I click on the button and there is the notification animation still running to completely stop that animation and imidiately display the new one like this:
- click on a button
- notification slides down + display text
- click again
- timer/messages reset
- display the new messages + run the timer again
- notification slides back up
I have done this previously on another project but the same method is not working in this one as I get this errors:
- Cannot find namespace ‘NodeJS’ [ng] 11 endAnimationTimeout: NodeJS.Timeout | undefined;
- Cannot find namespace ‘NodeJS’.
[ng] 10 startAnimationTimeout: NodeJS.Timeout | undefined;
Note: this message component is called on another component
TS:
import { Component, OnInit } from '@angular/core';
import { ValidatorsService } from '../../../services/validators.service';
@Component({
selector: 'app-message',
templateUrl: './message.component.html',
styleUrls: ['./message.component.scss'],
})
export class MessageComponent implements OnInit {
startAnimationTimeout: NodeJS.Timeout | undefined;
endAnimationTimeout: NodeJS.Timeout | undefined;
constructor(public validatorsService: ValidatorsService) { }
ngOnInit() {
let card = document.querySelector('.message-card') as HTMLDivElement;
let timeBar = document.querySelector('.time-bar') as HTMLDivElement;
this.showMessage(card, timeBar);
}
messageAnimation(card: HTMLDivElement, timeBar: HTMLDivElement) {
if (this.validatorsService.messageIndex <= this.validatorsService.messages.length - 1) {
timeBar.classList.remove('slideToLeft')
}
clearTimeout(this.startAnimationTimeout);
this.startAnimationTimeout = setTimeout(() => {
timeBar.classList.add('slideToLeft');
this.endAnimationTimeout = setTimeout(() => {
if (this.validatorsService.messageIndex >= this.validatorsService.messages.length - 1) {
card.classList.replace('slideDown', 'slideUp');
setTimeout(() => {
card.classList.replace('slideUp', 'hide');
timeBar.classList.remove('slideToLeft')
}, 750);
}
}, 2000);
}, 500);
}
showMessage(card: HTMLDivElement, timeBar: HTMLDivElement) {
if (this.validatorsService.messages.length === 1) {
clearTimeout(this.startAnimationTimeout);
clearTimeout(this.endAnimationTimeout);
this.messageAnimation(card, timeBar);
return;
}
if (this.validatorsService.messageIndex >= this.validatorsService.messages.length) {
clearTimeout(this.startAnimationTimeout);
return;
}
this.messageAnimation(card, timeBar);
setTimeout(() => {
this.validatorsService.messageIndex++;
this.messageAnimation(card, timeBar);
this.showMessage(card, timeBar);
}, 2600);
return;
};
}
HTML:
<div #card class="message-card slideDown">
<p class="message"
[ngClass]="{'validMessage': validatorsService.messageType === 'success', 'warningMessage': validatorsService.messageType === 'warning'}">
<span *ngIf="validatorsService.messageType === 'error'">Error
{{validatorsService.messageIndex + 1}} of {{validatorsService.messages.length}}:</span>
{{validatorsService.messages[validatorsService.messageIndex]}}</p>
<div #timeBar class="time-bar"
[ngClass]="{'valid': validatorsService.messageType === 'success', 'warning': validatorsService.messageType === 'warning'}">
</div>
</div>
here is the validators service as requested + the functions used on the button click:
import { Injectable } from '@angular/core';
import { AbstractControl } from '@angular/forms';
@Injectable({
providedIn: 'root'
})
export class ValidatorsService {
messages: string[] = [];
messageIndex: number = 0;
messageType: string = 'error';
showMessage: boolean = false;
constructor() { }
// Form validations
noSpaceAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (control.value !== null && control.value.indexOf(' ') !== -1) {
return { error: `Your ${controlName} can't contain spaces.` };
}
return null;
}
noEmptyAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (control.value !== null && control.value === '') {
return { error: `Your ${controlName} can't be empty.` };
}
return null;
}
lengthRangeAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (controlName === 'email' && control.value !== null && control.value.length > 254) {
return { error: `Your ${controlName} must have less then 254 characters.` };
}
if (controlName === 'username' && control.value !== null && control.value.length > 20) {
return { error: `Your ${controlName} must have less then 20 characters.` };
}
if (controlName === 'password' && control.value !== null && control.value.length < 6) {
return { error: `Your ${controlName} must contain a minimum of 6 characters.` };
}
if (controlName === 'password' && control.value !== null && control.value.length > 24) {
return { error: `Your ${controlName} must have less then 24 characters.` };
}
return null;
}
noSpaceOnlyAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (control.value !== null && control.value !== '' && /^s*$/.test(control.value)) {
return { error: `Your ${controlName} can't contain just spaces.` };
}
return null;
}
noConsecutiveSpacesAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (control.value !== null && control.value !== '' && /s{2,}/.test(control.value)) {
return { error: `Your ${controlName} can't contain consecutive spaces.` };
}
return null;
}
noStartNorEndSpacesAllowed(control: AbstractControl) {
let parentControl: any = control.parent;
let controlName;
if (parentControl) {
controlName = Object.keys(parentControl.controls).find(key => parentControl.controls[key] === control);
}
if (control.value !== null && control.value !== '' && control.value.startsWith(" ")) {
return { error: `Your ${controlName} can't start with a space.` };
}
if (control.value !== null && control.value !== '' && control.value.endsWith(" ")) {
return { error: `Your ${controlName} can't end with a space.` };
}
return null;
}
acceptToSValidation(control: AbstractControl) {
if (control.value !== null && control.value === false) { return { error: 'You Must Agree to the Terms & Conditions.' }; }
return null;
}
}
formValidation() {
this.validatorsService.messages = [];
this.validatorsService.messageIndex = 0;
Object.keys(this.form.controls).forEach(key => {
let control: any = this.form.controls[key];
if (control.errors) {
Object.keys(control.errors).forEach(error => {
this.validatorsService.messages.push(control.errors[error]);
});
};
});
this.validatorsService.showMessage = true;
}
onSubmit() {
this.validatorsService.showMessage = false
Object.values(this.form.controls).forEach(control => {
control.markAsTouched();
});
if (this.form.valid === false) {
this.validatorsService.messageType = 'error';
this.formValidation();
return;
}
this.validatorsService.messageType = 'success';
console.log(this.form.value);
}
html to show the message on another component:
<app-message *ngIf="this.validatorsService.showMessage"></app-message>
2
Instead of NodeJS.Timeout, you should use number for the type of the timeout variable.
startAnimationTimeout: number | undefined;
endAnimationTimeout: number | undefined;
1