File

src/components/sq-input-range/sq-input-range.component.ts

Description

Represents an input component for selecting a numeric value within a specified range.

This component allows users to input a numeric value within a defined range and provides visual feedback for the selected value.


Example :
<sq-input-range [name]='"input-range"' [(value)]='number' ></sq-input-range>

Implements

AfterContentInit OnChanges

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(validatorHelper: ValidatorHelper, element: ElementRef, translate: TranslateService)

Creates an instance of SqInputRangeComponent.

Parameters :
Name Type Optional Description
validatorHelper ValidatorHelper No
  • An instance of the ValidatorHelper service.
element ElementRef No
  • A reference to the element hosting the component.
translate TranslateService No
  • An optional instance of the TranslateService for internationalization.

Inputs

color
Type : string
Default value : 'var(--primary_color)'

The color of the input component.

customClass
Type : string
Default value : ''

A custom CSS class to apply to the input element.

errorSpan
Type : boolean
Default value : true

Whether to display an error message below the input.

externalError
Type : any

An external error message, if provided.

id
Type : string

The id attribute for the input element.

label
Type : string
Default value : ''

The label to display for the input.

labelColor
Type : string
Default value : ''

The color of the input label.

maxNumber
Type : number
Default value : 100

The maximum allowed value for the input.

minNumber
Type : number
Default value : 0

The minimum allowed value for the input.

name
Type : string
Default value : `random-name-${(1 + Date.now() + Math.random()).toString().replace('.', '')}`

The name attribute for the input element.

readonly
Type : boolean
Default value : false

Whether the input is read-only.

required
Type : boolean
Default value : false

Whether the input is required.

step
Type : number
Default value : 1

The step value for the input.

useFormErrors
Type : boolean
Default value : true

Whether to use form-level error messages.

value
Type : any
Default value : '0'

The initial value of the input.

Outputs

emitFocus
Type : EventEmitter<Event>

Event emitter for focus input changes.

inFocus
Type : EventEmitter<boolean>

Event emitter for focusing or blurring the input.

valid
Type : EventEmitter<boolean>

Event emitter for indicating the validity of the input.

valueChange
Type : EventEmitter<any>

Event emitter for changes in the input value.

Methods

change
change(event: any)

Handle input value changes.

Parameters :
Name Type Optional Description
event any No
  • The input change event.
Returns : void
changeValuePosition
changeValuePosition()

Changes the visual position of the input value based on the current value, minimum and maximum allowed values.

Returns : void
Async ngAfterContentInit
ngAfterContentInit()

Lifecycle hook that runs after content initialization. It sets a timeout to change the value position.

Returns : any
ngOnChanges
ngOnChanges(changes: SimpleChanges)

Lifecycle hook that runs when changes occur in the component's input properties. It recalculates the value position when value-related properties change.

Parameters :
Name Type Optional Description
changes SimpleChanges No
  • An object containing changes to input properties.
Returns : void
Async setError
setError(key: string)

Set an error message based on a translation key.

Parameters :
Name Type Optional Description
key string No
  • The translation key for the error message.
Returns : any
validate
validate(isBlur)

Validates the input value.

Parameters :
Name Optional Default value Description
isBlur No false
  • Indicates whether the validation is triggered by blurring the input.
Returns : void

Properties

error
Type : boolean | string
Default value : false

The error message associated with the input.

input
Type : ElementRef
Decorators :
@ViewChild('input')

A reference to the 'input' element in the component template.

labelTemplate
Type : TemplateRef<HTMLElement> | null
Default value : null
Decorators :
@ContentChild('labelTemplate')

Reference to a label template.

nativeElement
Type : ElementRef

A reference to the native element of the component.

Public translate
Type : TranslateService
Decorators :
@Optional()
- An optional instance of the `TranslateService` for internationalization.
Public validatorHelper
Type : ValidatorHelper
- An instance of the `ValidatorHelper` service.
valueFloating
Type : ElementRef
Decorators :
@ViewChild('valueFloating')

A reference to the 'valueFloating' element in the component template.

import {
  AfterContentInit,
  Component,
  ContentChild,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  Optional,
  Output,
  SimpleChanges,
  TemplateRef,
  ViewChild,
} from '@angular/core';
import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { ValidatorHelper } from '../../helpers/validator.helper';
import { sleep } from '../../helpers/sleep.helper';
import { UniversalSafePipe } from '../../pipes/universal-safe/universal-safe.pipe';

/**
 * Represents an input component for selecting a numeric value within a specified range.
 *
 * This component allows users to input a numeric value within a defined range and provides visual feedback for the selected value.
 *
 * @implements {AfterContentInit}
 * @implements {OnChanges}
 *
 * <br>
 * <label for='id-exemple-range'>
 *  Example Input Range
 * </label>
 * <input
 *   class='range mb-3'
 *   name="name-exemple-range"
 *   id="id-exemple-range"
 *   type="range"
 * ></input>
 *
 * @example
 * <sq-input-range [name]='"input-range"' [(value)]='number' ></sq-input-range>
 */
@Component({
  selector: 'sq-input-range',
  templateUrl: './sq-input-range.component.html',
  styleUrls: ['./sq-input-range.component.scss'],
  standalone: true,
  imports: [NgClass, NgStyle, NgTemplateOutlet, FormsModule, UniversalSafePipe],
})
export class SqInputRangeComponent implements AfterContentInit, OnChanges {
  /**
   * A custom CSS class to apply to the input element.
   */
  @Input() customClass = '';

  /**
   * The name attribute for the input element.
   *
   * @default 'random-name-[hash-random-code]'
   */
  @Input() name = `random-name-${(1 + Date.now() + Math.random()).toString().replace('.', '')}`;

  /**
   * The id attribute for the input element.
   */
  @Input() id?: string;

  /**
   * The initial value of the input.
   */
  @Input() value: any = '0';

  /**
   * Whether the input is read-only.
   */
  @Input() readonly = false;

  /**
   * Whether the input is required.
   */
  @Input() required = false;

  /**
   * The label to display for the input.
   */
  @Input() label = '';

  /**
   * The color of the input component.
   */
  @Input() color = 'var(--primary_color)';

  /**
   * The color of the input label.
   */
  @Input() labelColor = '';

  /**
   * Whether to display an error message below the input.
   */
  @Input() errorSpan = true;

  /**
   * An external error message, if provided.
   */
  @Input() externalError?: any;

  /**
   * The step value for the input.
   */
  @Input() step = 1;

  /**
   * The minimum allowed value for the input.
   */
  @Input() minNumber = 0;

  /**
   * The maximum allowed value for the input.
   */
  @Input() maxNumber = 100;

  /**
   * Whether to use form-level error messages.
   */
  @Input() useFormErrors = true;

  /**
   * Event emitter for changes in the input value.
   */
  @Output() valueChange: EventEmitter<any> = new EventEmitter();

  /**
   * Event emitter for focusing or blurring the input.
   */
  @Output() inFocus: EventEmitter<boolean> = new EventEmitter();

  /**
   * Event emitter for indicating the validity of the input.
   */
  @Output() valid: EventEmitter<boolean> = new EventEmitter();

  /**
   * Event emitter for focus input changes.
   */
  @Output() emitFocus: EventEmitter<Event> = new EventEmitter<Event>();

  /**
   * A reference to the 'valueFloating' element in the component template.
   */
  @ViewChild('valueFloating') valueFloating!: ElementRef;

  /**
   * A reference to the 'input' element in the component template.
   */
  @ViewChild('input') input!: ElementRef;

  /**
   * Reference to a label template.
   */
  @ContentChild('labelTemplate')
  labelTemplate: TemplateRef<HTMLElement> | null = null;

  /**
   * The error message associated with the input.
   */
  error: boolean | string = false;

  /**
   * A reference to the native element of the component.
   */
  nativeElement: ElementRef;

  /**
   * Creates an instance of `SqInputRangeComponent`.
   *
   * @param validatorHelper - An instance of the `ValidatorHelper` service.
   * @param element - A reference to the element hosting the component.
   * @param translate - An optional instance of the `TranslateService` for internationalization.
   */
  constructor(
    public validatorHelper: ValidatorHelper,
    element: ElementRef,
    @Optional() public translate: TranslateService
  ) {
    this.nativeElement = element.nativeElement;
  }

  /**
   * Lifecycle hook that runs after content initialization.
   * It sets a timeout to change the value position.
   */
  async ngAfterContentInit() {
    await sleep();
    this.changeValuePosition();
  }

  /**
   * Lifecycle hook that runs when changes occur in the component's input properties.
   * It recalculates the value position when value-related properties change.
   *
   * @param changes - An object containing changes to input properties.
   */
  ngOnChanges(changes: SimpleChanges) {
    if (changes.hasOwnProperty('value') || changes.hasOwnProperty('minNumber') || changes.hasOwnProperty('maxNumber')) {
      this.changeValuePosition();
    }
  }

  /**
   * Validates the input value.
   *
   * @param isBlur - Indicates whether the validation is triggered by blurring the input.
   */
  validate(isBlur = false) {
    if (this.externalError) {
      this.error = false;
    } else if (!!this.required && !this.value && this.value !== '0') {
      this.setError('forms.required');
      this.valid.emit(false);
    } else {
      this.valid.emit(true);
      this.error = '';
    }
    if (isBlur) {
      this.inFocus.emit(false);
    }
  }

  /**
   * Changes the visual position of the input value based on the current value,
   * minimum and maximum allowed values.
   */
  changeValuePosition() {
    const val = parseFloat(this.value);
    const min = this.minNumber ? this.minNumber : 0;
    const max = this.maxNumber ? this.maxNumber : 100;
    const newVal = Number(((val - min) * 100) / (max - min));
    if (this.valueFloating) {
      this.valueFloating.nativeElement.style.left = `calc(${newVal}% + (${10 - newVal * 0.36}px))`;
    }
  }

  /**
   * Handle input value changes.
   * @param event - The input change event.
   */
  change(event: any): void {
    this.inFocus.emit(true);
    this.changeValuePosition();
    this.value = event;
    this.valueChange.emit(event);
    this.validate();
  }

  /**
   * Set an error message based on a translation key.
   * @param key - The translation key for the error message.
   */
  async setError(key: string) {
    if (this.useFormErrors && this.translate) {
      this.error = await this.translate.instant(key);
    }
  }
}
<div
  class="wrapper-all-inside-input {{ customClass }}"
  [ngClass]="{
    'no-label': !label.length,
  }"
>
  @if (label?.length || labelTemplate) {
    <label
      class="display-flex"
      [ngClass]="{
        readonly: readonly,
      }"
      [for]="id"
    >
      @if (label?.length && !labelTemplate) {
        <div [ngStyle]="{ color: labelColor }" [innerHtml]="label | universalSafe"></div>
      }
    </label>
  }
  @if (labelTemplate) {
    <div>
      <ng-container *ngTemplateOutlet="labelTemplate"></ng-container>
    </div>
  }
  <div
    class="p-0 wrapper-input wrapper-input-squid"
    [ngClass]="{
      error: (externalError && externalError !== '') || (error && error !== ''),
    }"
  >
    <output class="value-floating" #valueFloating>
      {{ value }}
    </output>
    <input
      [id]="id"
      type="range"
      class="range"
      [name]="name"
      [step]="step"
      [required]="required"
      [disabled]="readonly"
      [min]="minNumber || ''"
      [max]="maxNumber || ''"
      (blur)="validate(true)"
      [ngModel]="value"
      (ngModelChange)="change($event)"
      (focus)="emitFocus.emit()"
      ngDefaultControl
      #input
    />
  </div>
  @if (errorSpan) {
    <div class="box-validation box-invalid show">
      <i
        [ngClass]="{
          'visibility-hidden-force': !error && !externalError,
        }"
        class="fa-solid fa-triangle-exclamation"
      ></i>
      {{ externalError ? externalError : '' }}
      {{ error && !externalError ? error : '' }}
    </div>
  }
</div>

./sq-input-range.component.scss

.wrapper-all-inside-input {
  position: relative;
  &.no-label {
    margin-top: 2rem;
  }
  label {
    margin: 0 auto 2.5rem;
  }
  input[type='range'] {
    border-radius: 12px;
  }
  .value-floating {
    position: absolute;
    bottom: 26px;
    font-size: 0.86rem;
    line-height: 1;
    left: 10px;
  }
  .box-validation {
    margin-top: 0.5rem;
  }
  .wrapper-input.wrapper-input-squid {
    position: relative;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""