npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/components-inputs-valid

v0.2.357-4

Published

> Component input tích hợp validation, label và binding dữ liệu — giải pháp all-in-one cho form Angular.

Readme

@libs-ui/components-inputs-valid

Component input tích hợp validation, label và binding dữ liệu — giải pháp all-in-one cho form Angular.

Giới thiệu

@libs-ui/components-inputs-valid là component cấp cao (high-level) kết hợp ba phần tử: nhãn (label), ô nhập liệu (input) và cơ chế kiểm tra tính hợp lệ (validation) vào một component duy nhất. Component tự động bind dữ liệu hai chiều vào đối tượng thông qua fieldNameBind, tự quản lý trạng thái lỗi và hiển thị thông báo — giúp giảm đáng kể boilerplate khi xây dựng form phức tạp. Hỗ trợ nhiều loại validation: bắt buộc nhập, độ dài, giá trị số, regex, và hàm async tùy chỉnh.

Tính năng

  • Two-way binding tự động: Bind trực tiếp với trường trong object qua fieldNameBinditem model
  • Label tích hợp: Hỗ trợ ILabel đầy đủ — required mark, popover, buttons, toggle, switch
  • Validation toàn diện: Required, min/max length, min/max value, regex pattern, hàm async tùy chỉnh
  • Debounced validation: Trì hoãn validate qua debounceTimeValidate để tối ưu hiệu năng
  • Unit bên trái/phải: Hiển thị dropdown đơn vị kết hợp với input (VD: USD, VND, kg, %)
  • Radio/Checkbox tích hợp: Thêm checkbox hoặc radio bên trái input để chọn/bỏ chọn dòng
  • FunctionsControl API: Truy cập checkIsValid(), setMessageError(), focus(), blur(), resetValue() qua ViewChild
  • Hỗ trợ bigint: Validate giá trị bigint với giới hạn an toàn tự động
  • Template slots: Hỗ trợ templateLeftBottomInputtemplateRightBottomInput cho nội dung tùy chỉnh

Khi nào sử dụng

  • Xây dựng form nhập liệu cần validation tập trung (form thêm mới, chỉnh sửa)
  • Cần hiển thị label + input + error message trong một block đồng nhất
  • Input cần đơn vị (tiền tệ, trọng lượng, phần trăm) bên trái hoặc phải
  • Cần gọi checkIsValid() programmatically trước khi submit form
  • Input trong bảng danh sách có checkbox/radio chọn dòng tích hợp

Cài đặt

npm install @libs-ui/components-inputs-valid

Import

import { LibsUiComponentsInputsValidComponent } from '@libs-ui/components-inputs-valid';
import {
  IValidRequired,
  IValidPattern,
  IValidLength,
  IInputValidUnitConfig,
  IInputValidFunctionControlEvent,
  TYPE_FUNCTION_INPUT_VALID,
} from '@libs-ui/components-inputs-valid';

Ví dụ sử dụng

Ví dụ 1 — Input bắt buộc với label và kiểm tra độ dài

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsValidComponent, IInputValidFunctionControlEvent } from '@libs-ui/components-inputs-valid';

@Component({
  standalone: true,
  imports: [LibsUiComponentsInputsValidComponent],
  templateUrl: './create-user.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CreateUserComponent {
  protected formData = signal<Record<string, any>>({
    username: '',
    email: '',
  });

  private usernameControl: IInputValidFunctionControlEvent | undefined;

  protected handlerUsernameControl(control: IInputValidFunctionControlEvent): void {
    this.usernameControl = control;
  }

  protected async handlerSubmit(event: Event): Promise<void> {
    event.stopPropagation();
    const isValid = await this.usernameControl?.checkIsValid();
    if (!isValid) return;
    // Gửi form...
  }
}
<!-- create-user.component.html -->
<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'username'"
  [labelConfig]="{
    labelLeft: 'Tên đăng nhập',
    required: true
  }"
  [validRequired]="{ isRequired: true, message: 'Vui lòng nhập tên đăng nhập' }"
  [validMinLength]="{ length: 6, message: 'Tối thiểu 6 ký tự' }"
  [maxLength]="50"
  [placeholder]="'Nhập tên đăng nhập'"
  (outFunctionsControl)="handlerUsernameControl($event)" />

Ví dụ 2 — Validate email bằng regex và hàm async tùy chỉnh

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsValidComponent, TYPE_FUNCTION_INPUT_VALID } from '@libs-ui/components-inputs-valid';

@Component({
  standalone: true,
  imports: [LibsUiComponentsInputsValidComponent],
  templateUrl: './register.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RegisterComponent {
  protected formData = signal<Record<string, any>>({ email: '' });

  protected readonly emailPatterns = [
    {
      pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
      message: 'Email không đúng định dạng',
    },
  ];

  protected readonly checkEmailExists: TYPE_FUNCTION_INPUT_VALID = async (value) => {
    if (!value) return { message: undefined, interpolateParams: undefined };
    // Giả lập gọi API kiểm tra email
    const exists = await fakeCheckEmailApi(value as string);
    return exists
      ? { message: 'Email này đã được sử dụng', interpolateParams: {} }
      : { message: undefined, interpolateParams: undefined };
  };

  protected handlerValueChange(value: any): void {
    // Xử lý sau khi value thay đổi
  }
}
<!-- register.component.html -->
<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'email'"
  [labelConfig]="{ labelLeft: 'Email', required: true }"
  [validRequired]="{ isRequired: true, message: 'Vui lòng nhập email' }"
  [validPattern]="emailPatterns"
  [functionValid]="checkEmailExists"
  [debounceTimeValidate]="500"
  [placeholder]="'[email protected]'"
  (outValueChange)="handlerValueChange($event)" />

Ví dụ 3 — Input số với đơn vị và giới hạn giá trị

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsValidComponent, IInputValidUnitConfig } from '@libs-ui/components-inputs-valid';

@Component({
  standalone: true,
  imports: [LibsUiComponentsInputsValidComponent],
  templateUrl: './price-input.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PriceInputComponent {
  protected formData = signal<Record<string, any>>({ price: null, currency: 'vnd' });

  protected readonly currencies = [
    { id: 'vnd', label: 'VND' },
    { id: 'usd', label: 'USD' },
  ];

  protected readonly unitConfig: IInputValidUnitConfig = {
    fieldKey: 'id',
    fieldLabel: 'label',
  };

  protected handlerValueChange(value: any): void {
    // Xử lý sau khi giá thay đổi
  }
}
<!-- price-input.component.html -->
<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'price'"
  [labelConfig]="{ labelLeft: 'Giá bán', required: true }"
  [dataType]="'number'"
  [minValueNumber]="0"
  [maxValueNumber]="999999999"
  [validRequired]="{ isRequired: true, message: 'Vui lòng nhập giá' }"
  [validMinValue]="{ message: 'Giá phải lớn hơn 0', interpolateParams: {} }"
  [validMaxValue]="{ message: 'Giá không vượt quá 999,999,999', interpolateParams: {} }"
  [unitsRight]="currencies"
  [configUnitRight]="unitConfig"
  [keySelectedUnitRight]="formData()['currency']"
  (outValueChange)="handlerValueChange($event)" />

Ví dụ 4 — Textarea với validate và đếm ký tự

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsValidComponent } from '@libs-ui/components-inputs-valid';

@Component({
  standalone: true,
  imports: [LibsUiComponentsInputsValidComponent],
  templateUrl: './note-form.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NoteFormComponent {
  protected formData = signal<Record<string, any>>({ note: '' });

  protected handlerHeightChange(event: { isChange: boolean }): void {
    // Xử lý khi textarea thay đổi chiều cao
  }
}
<!-- note-form.component.html -->
<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'note'"
  [labelConfig]="{ labelLeft: 'Ghi chú', limitLength: 500, onlyShowCount: true }"
  [tagInput]="'textarea'"
  [maxLength]="500"
  [showCount]="true"
  [minHeightTextArea]="80"
  [maxHeightTextArea]="200"
  [placeholder]="'Nhập ghi chú...'"
  (outHeightAreaChange)="handlerHeightChange($event)" />

Ví dụ 5 — Validate form nhiều trường với checkIsValid trước khi submit

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsValidComponent, IInputValidFunctionControlEvent } from '@libs-ui/components-inputs-valid';

@Component({
  standalone: true,
  imports: [LibsUiComponentsInputsValidComponent],
  templateUrl: './contact-form.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContactFormComponent {
  protected formData = signal<Record<string, any>>({
    fullName: '',
    phone: '',
  });

  private nameControl: IInputValidFunctionControlEvent | undefined;
  private phoneControl: IInputValidFunctionControlEvent | undefined;

  protected handlerNameControl(control: IInputValidFunctionControlEvent): void {
    this.nameControl = control;
  }

  protected handlerPhoneControl(control: IInputValidFunctionControlEvent): void {
    this.phoneControl = control;
  }

  protected async handlerSubmit(event: Event): Promise<void> {
    event.stopPropagation();
    const [isNameValid, isPhoneValid] = await Promise.all([
      this.nameControl?.checkIsValid(),
      this.phoneControl?.checkIsValid(),
    ]);
    if (!isNameValid || !isPhoneValid) return;
    // Submit form...
  }

  protected readonly phonePatterns = [
    {
      pattern: /^(0|\+84)[0-9]{9}$/,
      message: 'Số điện thoại không đúng định dạng (VD: 0912345678)',
    },
  ];
}
<!-- contact-form.component.html -->
<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'fullName'"
  [labelConfig]="{ labelLeft: 'Họ và tên', required: true }"
  [validRequired]="{ isRequired: true, message: 'Vui lòng nhập họ và tên' }"
  [validMinLength]="{ length: 2, message: 'Tối thiểu 2 ký tự' }"
  (outFunctionsControl)="handlerNameControl($event)" />

<libs_ui-components-inputs-valid
  [(item)]="formData"
  [fieldNameBind]="'phone'"
  [labelConfig]="{ labelLeft: 'Số điện thoại', required: true }"
  [validRequired]="{ isRequired: true, message: 'Vui lòng nhập số điện thoại' }"
  [validPattern]="phonePatterns"
  (outFunctionsControl)="handlerPhoneControl($event)" />

<button (click)="handlerSubmit($event)">Gửi</button>

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | item (model, required) | Record<string, any> | — | Đối tượng chứa dữ liệu. Two-way binding qua [(item)] | [(item)]="formData" | | fieldNameBind (required) | string | — | Tên trường trong item cần bind giá trị | [fieldNameBind]="'email'" | | labelConfig | ILabel | undefined | Cấu hình label phía trên input (text, required mark, popover, buttons) | [labelConfig]="{ labelLeft: 'Email', required: true }" | | acceptNegativeValue | boolean | undefined | Cho phép nhập số âm | [acceptNegativeValue]="true" | | autoAddZeroLessThan10InTypeInt | boolean | undefined | Tự động thêm số 0 đứng trước nếu giá trị < 10 (kiểu int) | [autoAddZeroLessThan10InTypeInt]="true" | | autoRemoveEmoji | boolean | undefined | Tự động xóa emoji khi người dùng nhập | [autoRemoveEmoji]="true" | | backgroundNone | boolean | undefined | Loại bỏ màu nền mặc định của input | [backgroundNone]="true" | | borderError | boolean | undefined | Luôn hiển thị border đỏ bất kể trạng thái lỗi | [borderError]="true" | | classContainerBottomInput | string | undefined | Class CSS cho vùng container phía dưới input | [classContainerBottomInput]="'mt-2'" | | classContainerInput | string | undefined | Class CSS cho container bao quanh thẻ input | [classContainerInput]="'relative'" | | classInclude | string | undefined | Class CSS tổng thể cho toàn bộ component | [classInclude]="'mb-4'" | | classIncludeInput | string | undefined | Class CSS cho thẻ input/textarea | [classIncludeInput]="'text-right'" | | classMessageErrorInclude | string | undefined | Class CSS bổ sung cho block thông báo lỗi | [classMessageErrorInclude]="'mt-1'" | | configUnitLeft | IInputValidUnitConfig | { fieldKey: 'id', fieldLabel: 'label' } | Cấu hình mapping trường key/label cho đơn vị trái | [configUnitLeft]="{ fieldKey: 'id', fieldLabel: 'name' }" | | configUnitRight | IInputValidUnitConfig | { fieldKey: 'id', fieldLabel: 'label' } | Cấu hình mapping trường key/label cho đơn vị phải | [configUnitRight]="{ fieldKey: 'id', fieldLabel: 'name' }" | | dataType | TYPE_DATA_TYPE_INPUT | 'string' | Kiểu dữ liệu: 'string', 'number', 'int', 'bigint', ... | [dataType]="'number'" | | debounceTimeValidate | number | 0 | Thời gian chờ (ms) trước khi kích hoạt validate sau khi nhập | [debounceTimeValidate]="300" | | defaultHeight | number | undefined | Chiều cao mặc định của input (px) | [defaultHeight]="40" | | disable | boolean | undefined | Vô hiệu hóa toàn bộ input | [disable]="isDisabled()" | | disableComponentSelectItem | boolean | undefined | Vô hiệu hóa checkbox/radio tích hợp bên trái | [disableComponentSelectItem]="true" | | emitEmptyInDataTypeNumber | boolean | undefined | Emit giá trị rỗng thay vì null khi xóa input kiểu number | [emitEmptyInDataTypeNumber]="true" | | fixedFloat | number | undefined | Số chữ số thập phân cố định cho kiểu number | [fixedFloat]="2" | | focusTimeOut | number | undefined | Độ trễ (ms) trước khi focus vào input | [focusTimeOut]="100" | | functionValid | TYPE_FUNCTION_INPUT_VALID | undefined | Hàm validate tùy chỉnh, hỗ trợ async, trả về IMessageTranslate | [functionValid]="checkEmailExists" | | iconLeftClass | string | undefined | Class CSS cho icon bên trái bên trong input | [iconLeftClass]="'icon-search'" | | iconRightClass | string | undefined | Class CSS cho icon bên phải bên trong input | [iconRightClass]="'icon-calendar'" | | ignoreBlockInputMaxValue | boolean | undefined | Cho phép nhập quá maxValueNumber (chỉ hiển thị lỗi, không chặn) | [ignoreBlockInputMaxValue]="true" | | ignoreContentLeft | boolean | undefined | Ẩn toàn bộ vùng bên trái (units, checkbox/radio) | [ignoreContentLeft]="true" | | ignoreContentRight | boolean | undefined | Ẩn toàn bộ vùng bên phải (units) | [ignoreContentRight]="true" | | ignoreShowError | boolean | undefined | @deprecated — Dùng ignoreShowMessageError thay thế | — | | ignoreShowMessageError | boolean | undefined | Ẩn text thông báo lỗi nhưng vẫn hiển thị border đỏ khi có lỗi | [ignoreShowMessageError]="true" | | ignoreStopPropagationEvent | boolean | undefined | Bỏ qua stopPropagation trong các event handler | [ignoreStopPropagationEvent]="true" | | ignoreUnitRightClassReadOnly | boolean | undefined | Không áp dụng class readonly cho đơn vị bên phải | [ignoreUnitRightClassReadOnly]="true" | | ignoreWidthInput100 | boolean | undefined | Không đặt width: 100% cho thẻ input bên trong | [ignoreWidthInput100]="true" | | isBaselineStyle | boolean | undefined | Căn align-items: baseline — dùng khi cần căn input thẳng hàng với label/text cùng dòng | [isBaselineStyle]="true" | | keepPlaceholderOnly | boolean | undefined | Chỉ hiển thị placeholder, không hiển thị giá trị thực | [keepPlaceholderOnly]="true" | | keySelectedUnitLeft | any | undefined | Key của đơn vị bên trái đang được active | [keySelectedUnitLeft]="formData()['unitLeft']" | | keySelectedUnitRight | any | undefined | Key của đơn vị bên phải đang được active | [keySelectedUnitRight]="formData()['currency']" | | maxHeightTextArea | number | 250 | Chiều cao tối đa (px) cho textarea khi auto-resize | [maxHeightTextArea]="300" | | maxLength | number | undefined | Độ dài ký tự tối đa, hiển thị lỗi khi vượt quá | [maxLength]="255" | | maxLengthNumberCount | number | undefined | Giới hạn số lượng chữ số hiển thị trong bộ đếm | [maxLengthNumberCount]="10" | | maxValueNumber | number | undefined | Giá trị số tối đa được phép nhập | [maxValueNumber]="100" | | minHeightTextArea | number | undefined | Chiều cao tối thiểu (px) cho textarea | [minHeightTextArea]="80" | | minValueNumber | number | undefined | Giá trị số tối thiểu được phép nhập | [minValueNumber]="0" | | modeInput | TYPE_MODE_INPUT | undefined | Chế độ nhập: 'password', 'text', ... | [modeInput]="'password'" | | noBorder | boolean | undefined | Ẩn viền của input | [noBorder]="true" | | onlyAcceptNegativeValue | boolean | undefined | Chỉ chấp nhận giá trị âm | [onlyAcceptNegativeValue]="true" | | paddingRightCustomSpecific | number | undefined | Custom padding-right khi showCount không tự tính được (workaround cho #52185) | [paddingRightCustomSpecific]="60" | | placeholder | string | undefined | Văn bản placeholder của input | [placeholder]="'Nhập nội dung...'" | | popoverContentIconLeft | string | undefined | Nội dung tooltip/popover khi hover icon trái | [popoverContentIconLeft]="'Tìm kiếm'" | | popoverContentIconRight | string | undefined | Nội dung tooltip/popover khi hover icon phải | [popoverContentIconRight]="'Chọn ngày'" | | positionMessageErrorStartInput | boolean | undefined | Hiển thị thông báo lỗi thẳng hàng với input thay vì từ lề trái | [positionMessageErrorStartInput]="true" | | readonly | boolean | undefined | Chế độ chỉ đọc — không cho phép chỉnh sửa | [readonly]="isReadOnly()" | | resetAutoCompletePassword | boolean | undefined | Reset trạng thái autocomplete cho input password | [resetAutoCompletePassword]="true" | | resize | TYPE_INPUT_RESIZE_MODE | undefined | Chế độ resize cho textarea: 'none', 'vertical', ... | [resize]="'none'" | | showCount | boolean | undefined | Hiển thị bộ đếm ký tự hiện tại / maxLength | [showCount]="true" | | tagInput | TYPE_TAG_INPUT | undefined | Loại thẻ: 'input' (mặc định) hoặc 'textarea' | [tagInput]="'textarea'" | | templateLeftBottomInput | TemplateRef | undefined | Template tùy chỉnh hiển thị ở góc dưới trái input | [templateLeftBottomInput]="myTpl" | | templateRightBottomInput | TemplateRef | undefined | Template tùy chỉnh hiển thị ở góc dưới phải input | [templateRightBottomInput]="myTpl" | | textAreaEnterNotNewLine | boolean | undefined | Phím Enter không xuống dòng trong textarea | [textAreaEnterNotNewLine]="true" | | typeComponentSelectItem | 'radio' \| 'checkbox' | undefined | Thêm radio hoặc checkbox tích hợp bên trái input | [typeComponentSelectItem]="'checkbox'" | | typeInput | TYPE_INPUT | undefined | Kiểu HTML input: 'text', 'number', ... | [typeInput]="'text'" | | unitsLeft | any[] | undefined | Danh sách đơn vị hiển thị bên trái input | [unitsLeft]="currencies" | | unitsRight | any[] | undefined | Danh sách đơn vị hiển thị bên phải input | [unitsRight]="currencies" | | useColorModeExist | boolean | undefined | Dùng màu sắc theo theme hiện tại của hệ thống | [useColorModeExist]="true" | | validMaxLength | IMessageTranslate | undefined | Thông báo lỗi khi vượt quá maxLength | [validMaxLength]="{ message: 'Tối đa 255 ký tự' }" | | validMaxValue | IMessageTranslate | undefined | Thông báo lỗi khi vượt quá maxValueNumber | [validMaxValue]="{ message: 'Giá trị quá lớn' }" | | validMinLength | IValidLength | undefined | Kiểm tra độ dài tối thiểu của chuỗi | [validMinLength]="{ length: 6, message: 'Tối thiểu 6 ký tự' }" | | validMinValue | IMessageTranslate | undefined | Thông báo lỗi khi giá trị nhỏ hơn minValueNumber | [validMinValue]="{ message: 'Giá trị quá nhỏ' }" | | validPattern | IValidPattern[] | undefined | Danh sách regex validate, hiển thị lỗi khi không khớp | [validPattern]="emailPatterns" | | validRequired | IValidRequired | undefined | Cấu hình trường bắt buộc nhập | [validRequired]="{ isRequired: true, message: 'Bắt buộc nhập' }" | | valueComponentSelectItem | boolean | undefined | Trạng thái checked của checkbox/radio tích hợp | [valueComponentSelectItem]="isSelected()" | | valuePatternShowError | boolean | false | Khi true: báo lỗi khi pattern match; khi false: báo lỗi khi pattern không match | [valuePatternShowError]="false" | | valueUpDownNumber | number | undefined | Bước nhảy khi nhấn nút tăng/giảm số | [valueUpDownNumber]="10" | | zIndexPopoverContent | number | undefined | Z-index của popover icon trái/phải | [zIndexPopoverContent]="1050" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outChangeValueByButtonUpDown) | void | Phát ra khi giá trị thay đổi bởi nút tăng/giảm | handlerChangeByUpDown(): void { event?.stopPropagation(); } | (outChangeValueByButtonUpDown)="handlerChangeByUpDown()" | | (outClickButtonLabel) | IButton | Phát ra khi người dùng click vào button trong label | handlerClickButtonLabel(button: IButton): void { button; } | (outClickButtonLabel)="handlerClickButtonLabel($event)" | | (outEnterInputEvent) | IEvent | Phát ra khi nhấn phím Enter trong ô nhập liệu | handlerEnterInput(e: IEvent): void { e; } | (outEnterInputEvent)="handlerEnterInput($event)" | | (outFocusAndBlur) | IFocusAndBlurEvent | Phát ra khi input được focus hoặc blur | handlerFocusAndBlur(e: IFocusAndBlurEvent): void { e; } | (outFocusAndBlur)="handlerFocusAndBlur($event)" | | (outFunctionsControl) | IInputValidFunctionControlEvent | Cung cấp các phương thức điều khiển programmatic | handlerFunctionsControl(ctrl: IInputValidFunctionControlEvent): void { this.ctrl = ctrl; } | (outFunctionsControl)="handlerFunctionsControl($event)" | | (outHeightAreaChange) | { isChange: boolean } | Phát ra khi chiều cao textarea thay đổi (auto-resize) | handlerHeightChange(e: { isChange: boolean }): void { e; } | (outHeightAreaChange)="handlerHeightChange($event)" | | (outIconLeft) | string | Phát ra tên sự kiện khi click icon bên trái | handlerIconLeft(eventName: string): void { eventName; } | (outIconLeft)="handlerIconLeft($event)" | | (outIconRight) | string | Phát ra tên sự kiện khi click icon bên phải | handlerIconRight(eventName: string): void { eventName; } | (outIconRight)="handlerIconRight($event)" | | (outLabelRightClick) | boolean | Phát ra khi click vào label bên phải | handlerLabelRightClick(value: boolean): void { value; } | (outLabelRightClick)="handlerLabelRightClick($event)" | | (outSelect) | boolean | Phát ra khi thay đổi trạng thái checkbox/radio tích hợp | handlerSelect(checked: boolean): void { checked; } | (outSelect)="handlerSelect($event)" | | (outSwitchEventLabel) | ISwitchEvent | Phát ra khi thay đổi trạng thái switch trong label | handlerSwitchLabel(e: ISwitchEvent): void { e; } | (outSwitchEventLabel)="handlerSwitchLabel($event)" | | (outValueChange) | any | Phát ra giá trị mới sau khi validate (có debounce nếu cấu hình) | handlerValueChange(value: any): void { value; } | (outValueChange)="handlerValueChange($event)" |

FunctionsControl API

Nhận về thông qua (outFunctionsControl) hoặc viewChild. Sử dụng để điều khiển programmatic.

import { IInputValidFunctionControlEvent } from '@libs-ui/components-inputs-valid';

// Nhận control
private emailControl: IInputValidFunctionControlEvent | undefined;

handlerEmailControl(control: IInputValidFunctionControlEvent): void {
  this.emailControl = control;
}

// Gọi trước khi submit
async handlerSubmit(event: Event): Promise<void> {
  event.stopPropagation();
  const isValid = await this.emailControl?.checkIsValid();
  if (!isValid) return;
}

// Set lỗi từ server
async handlerApiError(message: string): Promise<void> {
  await this.emailControl?.setMessageError(message);
}

| Method | Signature | Mô tả | |---|---|---| | checkIsValid() | () => Promise<boolean> | Chạy toàn bộ validate và trả về true nếu hợp lệ, false nếu có lỗi | | setMessageError() | (message: string) => Promise<void> | Đặt thông báo lỗi thủ công (VD: lỗi từ API server) | | currentStateIsValid() | () => Promise<boolean> | Trả về trạng thái lỗi hiện tại mà không chạy lại validate | | focus() | () => Promise<void> | Focus vào ô input | | blur() | () => Promise<void> | Blur khỏi ô input | | resetValue() | () => Promise<void> | Xóa giá trị hiện tại trong input | | insertContent() | (data: string \| number) => Promise<void> | Chèn nội dung vào vị trí con trỏ hiện tại | | selectAllContent() | () => Promise<void> | Chọn toàn bộ nội dung trong input | | getElementValue() | () => any | Lấy giá trị hiện tại từ DOM element | | checkAndDisableUpDownButton() | (value: any) => Promise<void> | Kiểm tra và disable nút tăng/giảm theo giá trị |

Types & Interfaces

import {
  IValidRequired,
  IValidPattern,
  IValidLength,
  IInputValidUnitConfig,
  IInputValidFunctionControlEvent,
  IInputUnit,
  IValidConfirmPassword,
  TYPE_FUNCTION_INPUT_VALID,
} from '@libs-ui/components-inputs-valid';

IValidRequired

Cấu hình validate trường bắt buộc.

interface IValidRequired {
  isRequired: boolean;
  message?: string;                 // Thông báo lỗi (i18n key hoặc text thuần)
  interpolateParams?: object;       // Tham số nội suy cho i18n
}

IValidPattern

Cấu hình validate theo regex.

interface IValidPattern {
  pattern: RegExp | string;         // Biểu thức chính quy
  message?: string;                 // Thông báo lỗi khi không khớp
  interpolateParams?: object;
  valuePatternShowError?: boolean;  // Override global valuePatternShowError cho rule này
}

IValidLength

Cấu hình validate độ dài tối thiểu.

interface IValidLength {
  length: number;                   // Số ký tự tối thiểu
  message?: string;
  interpolateParams?: object;
}

IInputValidUnitConfig

Cấu hình mapping field cho danh sách đơn vị.

interface IInputValidUnitConfig {
  fieldKey: string;                 // Tên field dùng làm key (VD: 'id')
  fieldLabel: string;               // Tên field dùng làm label hiển thị (VD: 'label')
  classInclude?: string;            // Class CSS bổ sung cho block đơn vị
}

IInputValidFunctionControlEvent

API điều khiển programmatic, kế thừa từ IInputFunctionControlEvent.

interface IInputValidFunctionControlEvent extends IInputFunctionControlEvent {
  checkIsValid: () => Promise<boolean>;
  setMessageError: (message: string) => Promise<void>;
  currentStateIsValid?: () => Promise<boolean>;
}

TYPE_FUNCTION_INPUT_VALID

Kiểu cho hàm validate async tùy chỉnh.

type TYPE_FUNCTION_INPUT_VALID = (value: any) => Promise<IMessageTranslate>;

// IMessageTranslate từ @libs-ui/interfaces-types:
// { message: string | undefined; interpolateParams?: object; }

Ví dụ hàm validate async:

import { TYPE_FUNCTION_INPUT_VALID } from '@libs-ui/components-inputs-valid';

protected readonly checkUsernameAvailable: TYPE_FUNCTION_INPUT_VALID = async (value) => {
  if (!value) return { message: undefined, interpolateParams: undefined };
  const isTaken = await this.userService.checkUsername(value as string).toPromise();
  return isTaken
    ? { message: 'Tên đăng nhập đã tồn tại', interpolateParams: {} }
    : { message: undefined, interpolateParams: undefined };
};

Lưu ý quan trọng

⚠️ item là required model: Bắt buộc truyền [(item)] với two-way binding. Không dùng [item] một chiều vì component ghi trực tiếp vào object thông qua set().

⚠️ fieldNameBind là required: Bắt buộc phải khớp với tên trường trong object item. Sai tên sẽ không bind được giá trị.

⚠️ ignoreShowError đã deprecated: Dùng ignoreShowMessageError thay thế. ignoreShowError sẽ bị xóa trong phiên bản tương lai.

⚠️ checkIsValid() bỏ qua validate khi readonly/disable: Nếu readonly hoặc disabletrue, checkIsValid() luôn trả về true mà không chạy validate.

⚠️ Thứ tự validate: Validate chạy theo thứ tự: required → minLength → pattern → minValue → maxValue → bigint range → maxLength → functionValid. Dừng lại ở lỗi đầu tiên.

⚠️ valuePatternShowError logic ngược: Mặc định false — báo lỗi khi regex KHÔNG match. Đặt true để báo lỗi khi regex CÓ match (VD: detect ký tự không được phép).

⚠️ debounceTimeValidate chỉ áp dụng cho outValueChange: Validate vẫn chạy ngay khi gọi checkIsValid() programmatically, bất kể debounce.

⚠️ Selector đúng là libs_ui-components-inputs-valid: Lưu ý dùng dấu gạch dưới _ không phải gạch ngang - sau libs_ui.

Demo

npx nx serve core-ui

Truy cập: http://localhost:4500 và tìm phần demo của inputs-valid trong menu Components.