Input

Displays a form input field or a component that looks like an input field.

PreviousNext
import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-default',
  imports: [ZardInputDirective],
  standalone: true,
  template: `
    <input z-input placeholder="Default" />
    <input z-input disabled placeholder="Disabled" />
  `,
})
export class ZardDemoInputDefaultComponent {}
 

Installation

1

Run the CLI

Use the CLI to add the component to your project.

npx @ngzard/ui@latest add input
1

Add the component files

Create the component directory structure and add the following files to your project.

input.directive.ts
input.directive.ts
import { computed, Directive, effect, ElementRef, inject, input, linkedSignal, model } from '@angular/core';
 
import type { ClassValue } from 'clsx';
 
import {
  inputVariants,
  type ZardInputSizeVariants,
  type ZardInputStatusVariants,
  type ZardInputTypeVariants,
} from './input.variants';
import { mergeClasses, transform } from '../../shared/utils/utils';
 
@Directive({
  selector: 'input[z-input], textarea[z-input]',
  host: {
    '[class]': 'classes()',
    '(input)': 'updateValue($event.target)',
  },
  exportAs: 'zInput',
})
export class ZardInputDirective {
  private readonly elementRef = inject(ElementRef);
 
  readonly class = input<ClassValue>('');
  readonly zBorderless = input(false, { transform });
  readonly zSize = input<ZardInputSizeVariants>('default');
  readonly zStatus = input<ZardInputStatusVariants>();
  readonly value = model<string>('');
 
  readonly size = linkedSignal<ZardInputSizeVariants>(() => this.zSize());
 
  protected readonly classes = computed(() =>
    mergeClasses(
      inputVariants({
        zType: this.getType(),
        zSize: this.size(),
        zStatus: this.zStatus(),
        zBorderless: this.zBorderless(),
      }),
      this.class(),
    ),
  );
 
  constructor() {
    effect(() => {
      const value = this.value();
 
      if (value !== undefined && value !== null) {
        this.elementRef.nativeElement.value = value;
      }
    });
  }
 
  disable(b: boolean): void {
    this.elementRef.nativeElement.disabled = b;
  }
 
  setDataSlot(name: string): void {
    if (this.elementRef?.nativeElement?.dataset) {
      this.elementRef.nativeElement.dataset.slot = name;
    }
  }
 
  protected updateValue(target: EventTarget | null): void {
    const el = target as HTMLInputElement | HTMLTextAreaElement | null;
    this.value.set(el?.value ?? '');
  }
 
  getType(): ZardInputTypeVariants {
    const isTextarea = this.elementRef.nativeElement.tagName.toLowerCase() === 'textarea';
    return isTextarea ? 'textarea' : 'default';
  }
}
 
input.variants.ts
input.variants.ts
import { cva, type VariantProps } from 'class-variance-authority';
 
export type zInputIcon = 'email' | 'password' | 'text';
 
export const inputVariants = cva('w-full', {
  variants: {
    zType: {
      default:
        'flex rounded-md border px-4 font-normal border-input bg-transparent file:border-0 file:text-foreground file:bg-transparent file:font-medium placeholder:text-muted-foreground outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
      textarea:
        'flex pb-2 min-h-20 h-auto rounded-md border border-input bg-background px-3 py-2 text-base placeholder:text-muted-foreground outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
    },
    zSize: {
      default: 'text-sm',
      sm: 'text-xs',
      lg: 'text-base',
    },
    zStatus: {
      error: 'border-destructive focus-visible:ring-destructive',
      warning: 'border-yellow-500 focus-visible:ring-yellow-500',
      success: 'border-green-500 focus-visible:ring-green-500',
    },
    zBorderless: {
      true: 'flex-1 bg-transparent border-0 outline-none focus-visible:ring-0 focus-visible:ring-offset-0 px-0 py-0',
    },
  },
  defaultVariants: {
    zType: 'default',
    zSize: 'default',
  },
  compoundVariants: [
    { zType: 'default', zSize: 'default', class: 'h-9 py-2 file:max-md:py-0' },
    { zType: 'default', zSize: 'sm', class: 'h-8 file:md:py-2 file:max-md:py-1.5' },
    { zType: 'default', zSize: 'lg', class: 'h-10 py-1 file:md:py-3 file:max-md:py-2.5' },
  ],
});
 
export type ZardInputTypeVariants = NonNullable<VariantProps<typeof inputVariants>['zType']>;
export type ZardInputSizeVariants = NonNullable<VariantProps<typeof inputVariants>['zSize']>;
export type ZardInputStatusVariants = NonNullable<VariantProps<typeof inputVariants>['zStatus']>;
 

Examples

default

import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-default',
  imports: [ZardInputDirective],
  standalone: true,
  template: `
    <input z-input placeholder="Default" />
    <input z-input disabled placeholder="Disabled" />
  `,
})
export class ZardDemoInputDefaultComponent {}
 

size

import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-size',
  imports: [ZardInputDirective],
  standalone: true,
  template: `
    <input z-input zSize="sm" placeholder="small size" />
    <input z-input zSize="default" placeholder="default size" />
    <input z-input zSize="lg" placeholder="large size" />
  `,
})
export class ZardDemoInputSizeComponent {}
 

status

import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-status',
  imports: [ZardInputDirective],
  standalone: true,
  template: `
    <input z-input zStatus="error" placeholder="Error" />
    <input z-input zStatus="warning" placeholder="Warning" />
    <input z-input zStatus="success" placeholder="Success" />
  `,
})
export class ZardDemoInputStatusComponent {}
 

borderless

import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-borderless',
  imports: [ZardInputDirective],
  standalone: true,
  template: `<input z-input zBorderless placeholder="Borderless" />`,
})
export class ZardDemoInputBorderlessComponent {}
 

text area

import { Component } from '@angular/core';
 
import { ZardInputDirective } from '../input.directive';
 
@Component({
  selector: 'z-demo-input-text-area',
  imports: [ZardInputDirective],
  standalone: true,
  template: `
    <textarea z-input rows="8" cols="12" placeholder="Default"></textarea>
    <textarea zBorderless z-input rows="8" cols="12" placeholder="Borderless"></textarea>
  `,
})
export class ZardDemoInputTextAreaComponent {}
 

API

[z-input] Directive

z-input is a Directive, it accepts all props which are supported by native input.

To get a customized input, just pass the following props to the directive.

Property Description Type Default
[zSize] input size default | sm | lg default
[zStatus] input status error | warning | success null
[zBorderless] input without border boolean false