Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 78 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,101 @@ Choose the version corresponding to your Angular version:

## Usage

Import `CodeInputModule` in your app module or page module:
Import `CodeInputComponent` directly in your standalone component or in your app module:

### For Standalone Components (Recommended):
```ts
import { CodeInputModule } from 'angular-code-input';
import { CodeInputComponent } from 'angular-code-input';

@Component({
selector: 'app-my-component',
imports: [CodeInputComponent],
// ...
})
export class MyComponent {
// ...
}
```

### For NgModule-based Applications:
```ts
import { CodeInputComponent } from 'angular-code-input';

@NgModule({
imports: [
// ...
CodeInputModule
CodeInputComponent
]
})
```

It is possible to configure the component across the app using the root config. In such case the import will look as follows:
### Global Configuration:

#### Modern Approach (Recommended):
Use the `provideCodeInputConfig` function for cleaner and more type-safe configuration:

```ts
import { CodeInputModule } from 'angular-code-input';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideCodeInputConfig } from 'angular-code-input';

// For standalone apps (main.ts)
bootstrapApplication(AppComponent, {
providers: [
provideCodeInputConfig({
codeLength: 6,
isCharsCode: true,
isCodeHidden: true
})
]
});
```

```ts
// For NgModule-based apps (app.module.ts)
import { provideCodeInputConfig } from 'angular-code-input';

@NgModule({
imports: [
// ...
CodeInputModule.forRoot({
providers: [
provideCodeInputConfig({
codeLength: 6,
isCharsCode: true,
code: 'abcdef'
}),
isCodeHidden: true
})
]
})
export class AppModule { }
```

#### Advanced Configuration with Factory:
For dynamic configuration based on environment or other services:

```ts
import { provideCodeInputConfigFactory } from 'angular-code-input';

// In your providers array
provideCodeInputConfigFactory(() => {
const isDev = !environment.production;
return {
codeLength: isDev ? 4 : 6,
isCharsCode: true,
initialFocusField: 0
};
})
```

#### Legacy Approach (Still Supported):
```ts
import { CodeInputComponentConfigToken } from 'angular-code-input';

// In your providers array
{
provide: CodeInputComponentConfigToken,
useValue: {
codeLength: 6,
isCharsCode: true,
code: 'abcdef'
}
}
```

Include the component on page template HTML:
Expand Down
88 changes: 78 additions & 10 deletions angular-code-input/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,101 @@ Choose the version corresponding to your Angular version:

## Usage

Import `CodeInputModule` in your app module or page module:
Import `CodeInputComponent` directly in your standalone component or in your app module:

### For Standalone Components (Recommended):
```ts
import { CodeInputModule } from 'angular-code-input';
import { CodeInputComponent } from 'angular-code-input';

@Component({
selector: 'app-my-component',
imports: [CodeInputComponent],
// ...
})
export class MyComponent {
// ...
}
```

### For NgModule-based Applications:
```ts
import { CodeInputComponent } from 'angular-code-input';

@NgModule({
imports: [
// ...
CodeInputModule
CodeInputComponent
]
})
```

It is possible to configure the component across the app using the root config. In such case the import will look as follows:
### Global Configuration:

#### Modern Approach (Recommended):
Use the `provideCodeInputConfig` function for cleaner and more type-safe configuration:

```ts
import { CodeInputModule } from 'angular-code-input';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideCodeInputConfig } from 'angular-code-input';

// For standalone apps (main.ts)
bootstrapApplication(AppComponent, {
providers: [
provideCodeInputConfig({
codeLength: 6,
isCharsCode: true,
isCodeHidden: true
})
]
});
```

```ts
// For NgModule-based apps (app.module.ts)
import { provideCodeInputConfig } from 'angular-code-input';

@NgModule({
imports: [
// ...
CodeInputModule.forRoot({
providers: [
provideCodeInputConfig({
codeLength: 6,
isCharsCode: true,
code: 'abcdef'
}),
isCodeHidden: true
})
]
})
export class AppModule { }
```

#### Advanced Configuration with Factory:
For dynamic configuration based on environment or other services:

```ts
import { provideCodeInputConfigFactory } from 'angular-code-input';

// In your providers array
provideCodeInputConfigFactory(() => {
const isDev = !environment.production;
return {
codeLength: isDev ? 4 : 6,
isCharsCode: true,
initialFocusField: 0
};
})
```

#### Legacy Approach (Still Supported):
```ts
import { CodeInputComponentConfigToken } from 'angular-code-input';

// In your providers array
{
provide: CodeInputComponentConfigToken,
useValue: {
codeLength: 6,
isCharsCode: true,
code: 'abcdef'
}
}
```

Include the component on page template HTML:
Expand Down
4 changes: 2 additions & 2 deletions angular-code-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"url": "https://github.com/AlexMiniApps/angular-code-input/issues"
},
"peerDependencies": {
"@angular/common": ">=18.0.0",
"@angular/core": ">=18.0.0"
"@angular/common": ">=20.1.0",
"@angular/core": ">=20.1.0"
}
}
26 changes: 25 additions & 1 deletion angular-code-input/src/lib/code-input.component.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {InjectionToken} from '@angular/core';
import {InjectionToken, Provider} from '@angular/core';

export const CodeInputComponentConfigToken = new InjectionToken<CodeInputComponentConfig>('CodeInputComponentConfig');

Expand Down Expand Up @@ -29,3 +29,27 @@ export const defaultComponentConfig: CodeInputComponentConfig = {
disabled: false,
autocapitalize: undefined
};

/**
* Modern provider function for CodeInput configuration
* @param config - Configuration object for CodeInput component
* @returns Provider array for use in bootstrapApplication or NgModule providers
*/
export function provideCodeInputConfig(config: CodeInputComponentConfig): Provider {
return {
provide: CodeInputComponentConfigToken,
useValue: { ...defaultComponentConfig, ...config }
};
}

/**
* Factory function for creating configuration with custom logic
* @param configFactory - Factory function that returns configuration
* @returns Provider for use in bootstrapApplication or NgModule providers
*/
export function provideCodeInputConfigFactory(configFactory: () => CodeInputComponentConfig): Provider {
return {
provide: CodeInputComponentConfigToken,
useFactory: configFactory
};
}
28 changes: 15 additions & 13 deletions angular-code-input/src/lib/code-input.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<span *ngFor="let holder of placeholders; index as i"
[class.code-hidden]="isCodeHidden">
<input #input
(click)="onClick($event)"
(paste)="onPaste($event, i)"
(input)="onInput($event, i)"
(keydown)="onKeydown($event, i)"
[type]="inputType"
[disabled]="disabled"
[attr.inputmode]="inputMode"
[attr.autocapitalize]="autocapitalize"
autocomplete="one-time-code"/>
</span>
@for (holder of placeholders; track holder; let i = $index) {
<span
[class.code-hidden]="isCodeHidden">
<input #input
(click)="onClick($event)"
(paste)="onPaste($event, i)"
(input)="onInput($event, i)"
(keydown)="onKeydown($event, i)"
[type]="inputType"
[disabled]="disabled"
[attr.inputmode]="inputMode"
[attr.autocapitalize]="autocapitalize"
autocomplete="one-time-code"/>
</span>
}
Comment on lines +1 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use a stable key in track to avoid unnecessary DOM churn

track holder re-evaluates by object identity.
If placeholders is rebuilt on every change-detection cycle (e.g. Array(n).fill('')), all inputs will be re-created. Prefer a stable scalar such as the loop index:

-@for (holder of placeholders; track holder; let i = $index) {
+@for (holder of placeholders; track i; let i = $index) {

This keeps focus/selection intact and improves performance.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@for (holder of placeholders; track holder; let i = $index) {
<span
[class.code-hidden]="isCodeHidden">
<input #input
(click)="onClick($event)"
(paste)="onPaste($event, i)"
(input)="onInput($event, i)"
(keydown)="onKeydown($event, i)"
[type]="inputType"
[disabled]="disabled"
[attr.inputmode]="inputMode"
[attr.autocapitalize]="autocapitalize"
autocomplete="one-time-code"/>
</span>
}
@for (holder of placeholders; track i; let i = $index) {
<span
[class.code-hidden]="isCodeHidden">
<input #input
(click)="onClick($event)"
(paste)="onPaste($event, i)"
(input)="onInput($event, i)"
(keydown)="onKeydown($event, i)"
[type]="inputType"
[disabled]="disabled"
[attr.inputmode]="inputMode"
[attr.autocapitalize]="autocapitalize"
autocomplete="one-time-code"/>
</span>
}
🤖 Prompt for AI Agents
In angular-code-input/src/lib/code-input.component.html lines 1 to 15, the
*ngFor trackBy uses 'holder' which tracks by object identity causing unnecessary
DOM re-creation if placeholders array is rebuilt. Change the trackBy expression
to use the loop index 'i' instead of 'holder' to provide a stable scalar key,
preventing input elements from being recreated and preserving focus and
selection.

24 changes: 14 additions & 10 deletions angular-code-input/src/lib/code-input.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import {
Component,
ElementRef,
EventEmitter,
Inject,
inject,
Input,
OnChanges,
OnDestroy,
OnInit,
Optional,
Output,
QueryList,
SimpleChanges,
ViewChildren
} from '@angular/core';
import { CommonModule } from '@angular/common';
import {
CodeInputComponentConfig,
CodeInputComponentConfigToken,
Expand All @@ -28,9 +28,10 @@ enum InputState {
}

@Component({
selector: 'code-input',
templateUrl: 'code-input.component.html',
styleUrls: ['./code-input.component.scss']
imports: [CommonModule],
selector: 'code-input',
templateUrl: 'code-input.component.html',
styleUrls: ['./code-input.component.scss']
})
export class CodeInputComponent implements AfterViewInit, OnInit, OnChanges, OnDestroy, AfterViewChecked, CodeInputComponentConfig {

Expand Down Expand Up @@ -65,16 +66,19 @@ export class CodeInputComponent implements AfterViewInit, OnInit, OnChanges, OnD
isInitialFocusFieldEnabled: false
};

constructor(@Optional() @Inject(CodeInputComponentConfigToken) config?: CodeInputComponentConfig) {
// Modern dependency injection using inject() function
private readonly config = inject(CodeInputComponentConfigToken, { optional: true });

constructor() {
Object.assign(this, defaultComponentConfig);

if (!config) {
if (!this.config) {
return;
}

// filtering for only valid config props
for (const prop in config) {
if (!config.hasOwnProperty(prop)) {
for (const prop in this.config) {
if (!this.config.hasOwnProperty(prop)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Modernize to Object.hasOwn() for better JavaScript practices

The static analysis correctly identifies that Object.hasOwn() is preferred over Object.hasOwnProperty() in modern JavaScript.

Apply this diff to use the modern approach:

-      if (!this.config.hasOwnProperty(prop)) {
+      if (!Object.hasOwn(this.config, prop)) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 81-81: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🤖 Prompt for AI Agents
In angular-code-input/src/lib/code-input.component.ts at line 81, replace the
use of this.config.hasOwnProperty(prop) with Object.hasOwn(this.config, prop) to
modernize the code and follow current JavaScript best practices.

continue;
}

Expand All @@ -83,7 +87,7 @@ export class CodeInputComponent implements AfterViewInit, OnInit, OnChanges, OnD
}

// @ts-ignore
this[prop] = config[prop];
this[prop] = this.config[prop];
}
}

Expand Down
26 changes: 0 additions & 26 deletions angular-code-input/src/lib/code-input.module.ts

This file was deleted.

1 change: 0 additions & 1 deletion angular-code-input/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@

export * from './lib/code-input.component';
export * from './lib/code-input.component.config';
export * from './lib/code-input.module';
Loading