Hone logo
Hone
Problems

Building a Reusable Angular Input Component

This challenge focuses on creating a flexible and reusable input component in Angular. A well-designed input component is a fundamental building block for any user interface, enabling consistent styling, validation, and user interaction across an application.

Problem Description

Your task is to create a custom Angular component that acts as a wrapper for native HTML input elements. This component should provide enhanced functionality beyond a basic <input> tag, including:

  • Labeling: Ability to associate a label with the input.
  • Placeholder: Support for placeholder text.
  • Type Control: Allow specifying the input type (e.g., 'text', 'password', 'email', 'number').
  • Value Binding: Seamless two-way data binding for the input's value.
  • Validation Feedback: Display validation messages when the input is invalid.
  • Disabled State: Option to disable the input.
  • Styling Flexibility: Allow for custom styling to integrate with various application themes.

Key Requirements:

  1. Component Structure: Create an Angular component, let's call it CustomInputComponent.
  2. Input Properties:
    • label: string - The text for the label.
    • placeholder: string - The placeholder text for the input.
    • inputType: string - The type of the HTML input element (e.g., 'text', 'password', 'email', 'number', 'date'). Defaults to 'text'.
    • control: AbstractControl - An instance of an Angular FormControl or FormGroup to manage validation and value. This is crucial for integration with Angular's reactive forms.
    • disabled: boolean - Whether the input is disabled. Defaults to false.
    • id: string - A unique identifier for the input and its associated label. This is important for accessibility.
  3. Output Behavior:
    • The component should render a <label> and an <input> element.
    • The <input> element should be correctly configured based on the provided input properties (inputType, placeholder, id, disabled).
    • Two-way data binding should be implemented using ngModel or by directly binding to the control property.
    • If the control is present and invalid (e.g., touched and invalid), display a generic error message or provide a mechanism to display specific error messages. For this challenge, a simple indication of "Invalid input" is sufficient if control.invalid and control.touched are true.
    • The id property should be used to link the label and input for accessibility (<label for="inputId"> and <input id="inputId">).
  4. Styling: The component should be styled such that it's visually distinct and can be easily themed. Provide a basic CSS implementation.

Edge Cases to Consider:

  • What happens if no id is provided? (The component should generate one or handle it gracefully for accessibility).
  • How are validation errors communicated to the user? (Focus on a basic visual indicator for this challenge).
  • Integration with both template-driven and reactive forms. (The control property strongly favors reactive forms, which is the modern standard).

Examples

Example 1: Basic Text Input

Assume a ReactiveFormsModule is imported and a FormGroup is set up.

Component Usage (in a parent component's template):

<form [formGroup]="myForm">
  <app-custom-input
    label="Your Name"
    placeholder="Enter your full name"
    [control]="myForm.get('name')"
    id="nameInput"
  ></app-custom-input>
  <!-- Other form elements -->
</form>

TypeScript (Parent Component):

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {
  myForm: FormGroup;

  ngOnInit() {
    this.myForm = new FormGroup({
      name: new FormControl('', [Validators.required, Validators.minLength(3)])
    });
  }
}

Expected Output (Visual - rendered HTML):

A label "Your Name" followed by an input field with placeholder "Enter your full name". If the input is touched and empty or less than 3 characters, it should visually indicate an error (e.g., red border, error message below).

Explanation: The CustomInputComponent receives the label, placeholder, and a form control. It renders these elements and links them appropriately. Validation is handled by the provided control.

Example 2: Password Input with Disabled State

Component Usage:

<form [formGroup]="myForm">
  <app-custom-input
    label="Password"
    placeholder="Create a strong password"
    inputType="password"
    [control]="myForm.get('password')"
    id="passwordInput"
    [disabled]="isFormDisabled"
  ></app-custom-input>
  <!-- Other form elements -->
</form>

TypeScript (Parent Component Snippet):

// ... inside ParentComponent
  isFormDisabled = false; // or some logic to set this
  // ...
  ngOnInit() {
    // ...
    this.myForm.addControl('password', new FormControl('', Validators.required));
    // ...
  }
// ...

Expected Output (Visual):

A label "Password" followed by a password input field (dots will appear when typing). If isFormDisabled is true, the input field should be visually disabled.

Explanation: The inputType is set to 'password', and the disabled input property controls the input's disabled state.

Example 3: Email Input with Validation Error Display

Component Usage:

<form [formGroup]="myForm">
  <app-custom-input
    label="Email Address"
    placeholder="your.email@example.com"
    inputType="email"
    [control]="myForm.get('email')"
    id="emailInput"
  ></app-custom-input>
  <!-- Displaying errors outside the component for clarity -->
  <div *ngIf="myForm.get('email')?.invalid && myForm.get('email')?.touched">
    <p style="color: red;">Please enter a valid email address.</p>
  </div>
</form>

TypeScript (Parent Component Snippet):

// ... inside ParentComponent
  ngOnInit() {
    // ...
    this.myForm.addControl('email', new FormControl('', [Validators.required, Validators.email]));
    // ...
  }
// ...

Expected Output (Visual):

A label "Email Address" followed by an email input. If the user types something that isn't a valid email or leaves it empty and then clicks away, the "Please enter a valid email address." message should appear below the input.

Explanation: The inputType is 'email', and Angular's built-in Validators.email is used. The parent component is responsible for displaying the error message based on the control's state. For the purpose of this challenge, your CustomInputComponent should at least visually indicate an invalid state (e.g., a red border).

Constraints

  • The CustomInputComponent must be built using Angular's component architecture.
  • You must use TypeScript for component logic.
  • The component should be designed to work with Angular's reactive forms module.
  • The id property should be handled correctly for accessibility and linking the label and input.
  • The styling should be contained within the component's CSS file, utilizing view encapsulation effectively.
  • The solution should be performant and avoid unnecessary re-renders.

Notes

  • Consider implementing the ControlValueAccessor interface if you want the component to be fully integrated and used with ngModel in template-driven forms as well, though the primary focus should be on reactive forms via the control input.
  • Think about how you can make the validation error display more integrated within the component in a real-world scenario (though for this challenge, a basic visual indicator of invalidity is sufficient).
  • Pay attention to accessibility. The for attribute on the <label> and id attribute on the <input> are crucial.
  • How will you handle the internal state of the input element while also allowing it to be controlled by an external FormControl?
Loading editor...
typescript