Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/public-api.ts"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
selector: 'abp-form-field-error',
template: `<div class="invalid-feedback d-block">
<ng-content></ng-content>
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbpFormFieldErrorComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
selector: 'abp-form-field-hint',
template: `<small class="form-text text-muted">
<ng-content></ng-content>
</small>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AbpFormFieldHintComponent {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';

@Component({
selector: 'abp-form-field-label',
template: `<ng-content></ng-content>`,
changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbpFormFieldLabelComponent {
for= input<string>('');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="form-group">
<label class="form-label" [for]="labelComponent()?.for()">
<ng-content select="abp-form-field-label" />
</label>
<ng-content select="abp-input" />
<ng-content select="abp-form-field-hint" />
<ng-content select="abp-validation-error" />
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
Component,
ChangeDetectionStrategy,
input,
HostBinding,
InjectionToken,
QueryList,
ContentChild,
contentChild,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { AbpFormFieldLabelComponent } from './abp-form-field-label.component';

export const ABP_FORM_FIELD = new InjectionToken<AbpFormFieldComponent>('AbpFormFieldComponent');

@Component({
selector: 'abp-form-field',
templateUrl: './abp-form-field.component.html',
imports: [CommonModule],
exportAs: 'abpFormField',
providers: [{ provide: ABP_FORM_FIELD, useExisting: AbpFormFieldComponent }],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AbpFormFieldComponent {

containerClass = input<string>('mb-3');
labelComponent = contentChild(AbpFormFieldLabelComponent);

@HostBinding('class')
get hostClasses(): string {
return `d-block mb-3 ${this.containerClass()}`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './abp-form-field.component';
export * from './abp-form-field-hint.component';
export * from './abp-form-field-label.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './abp-form-field';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib';
6 changes: 6 additions & 0 deletions npm/ng-packs/packages/components/abp-input/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/public-api.ts"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@if(abpFormField) {
<div [formGroup]="abpInputFormGroup">
<input
[type]="type()"
[id]="id()"
class="form-control"
[placeholder]="placeholder()"
formControlName="value"
/>
</div>
} @else {
<div class="form-group" [formGroup]="abpInputFormGroup">
@if (label()) {
<label [for]="id()" class="form-label">{{ label() | abpLocalization }}</label>
}
<input
[type]="type()"
[id]="id()"
class="form-control"
[placeholder]="placeholder()"
formControlName="value"
/>
@if (hint()) {
<small class="form-text text-muted">{{ hint() | abpLocalization }}</small>
}
@if (errors.length > 0) {
<div class="invalid-feedback d-block">
@for (error of errors; track error) {
<div>{{ error }}</div>
}
</div>
}
<div></div>
</div>
}
109 changes: 109 additions & 0 deletions npm/ng-packs/packages/components/abp-input/src/abp-input.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
forwardRef,
inject,
OnInit,
input,
Injector
} from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormBuilder,
FormControl,
FormControlName,
FormGroup,
FormGroupDirective,
NG_VALUE_ACCESSOR,
NgControl,
ReactiveFormsModule,
} from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LocalizationPipe } from '@abp/ng.core';
import { ABP_FORM_FIELD } from '@abp/ng.components/abp-form-field';

const ABP_INPUT_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AbpInputComponent),
multi: true,
};

@Component({
selector: 'abp-input',
templateUrl: './abp-input.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, LocalizationPipe],
exportAs: 'abpInput',
host: {
class: 'abp-input',
},
providers: [ABP_INPUT_CONTROL_VALUE_ACCESSOR],
})
export class AbpInputComponent implements OnInit, ControlValueAccessor {
label = input<string>();
type = input<'text' | 'number' | 'password'>('text');
id = input<string>('');
placeholder = input<string>('');
hint = input<string>('');
control: FormControl;
readonly formBuilder = inject(FormBuilder);
readonly changeDetectorRef = inject(ChangeDetectorRef);
readonly destroyRef = inject(DestroyRef);
readonly injector = inject(Injector);
readonly abpFormField = inject(ABP_FORM_FIELD, { optional: true });
abpInputFormGroup: FormGroup;

ngOnInit() {

Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

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

[nitpick] Extra blank line after method declaration should be removed for consistency.

Suggested change

Copilot uses AI. Check for mistakes.
const ngControl = this.injector.get(NgControl, null);
if (ngControl) {
this.control = this.injector.get(FormGroupDirective).getControl(ngControl as FormControlName);
}

this.abpInputFormGroup = this.formBuilder.group({
value: [''],
});

this.value.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => {
this.onChange(val);
});
}

writeValue(value: any): void {
this.value.setValue(value);
this.changeDetectorRef.markForCheck();
}

registerOnChange(fn: any): void {
this.onChange = fn;
}

registerOnTouched(fn: any): void {
this.onTouched = fn;
}

setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.value.disable();
} else {
this.value.enable();
}
}

get errors(): string[] {
if (this.control && this.control.errors) {
return []
}
return []
Comment on lines +98 to +100
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

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

The errors getter always returns an empty array regardless of whether there are actual errors. It should return the actual error messages when this.control.errors exists.

Suggested change
return []
}
return []
return Object.keys(this.control.errors);
}
return [];

Copilot uses AI. Check for mistakes.
}

get value(): AbstractControl<any> {
return this.abpInputFormGroup.get('value');
}

private onChange: (value: any) => void = () => {};
private onTouched: () => void = () => {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './abp-input.component';
2 changes: 1 addition & 1 deletion npm/ng-packs/packages/components/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"lib": ["dom", "es2020"],
"useDefineForClassFields": false
},
"exclude": ["src/test-setup.ts", "**/*.spec.ts", "jest.config.ts"],
"exclude": ["test-setup.ts", "**/*.spec.ts", "jest.config.ts"],
"include": ["**/*.ts"]
}
2 changes: 2 additions & 0 deletions npm/ng-packs/tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"@abp/ng.account.core/proxy": ["packages/account-core/proxy/src/public-api.ts"],
"@abp/ng.account/config": ["packages/account/config/src/public-api.ts"],
"@abp/ng.components": ["packages/components/src/public-api.ts"],
"@abp/ng.components/abp-form-field": ["packages/components/abp-form-field/src/public-api.ts"],
"@abp/ng.components/abp-input": ["packages/components/abp-input/src/public-api.ts"],
"@abp/ng.components/chart.js": ["packages/components/chart.js/src/public-api.ts"],
"@abp/ng.components/extensible": ["packages/components/extensible/src/public-api.ts"],
"@abp/ng.components/page": ["packages/components/page/src/public-api.ts"],
Expand Down