Skip to content

Commit

Permalink
Merge pull request #119 from erqk/v5
Browse files Browse the repository at this point in the history
V5
  • Loading branch information
erqk authored Apr 16, 2024
2 parents c6acf19 + 8af7a0d commit 71e15ab
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 124 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Change log

## 5.6.0

### Feature

- Control whether to add `required` class automatically.

### Chore

- Use shared template for form title.

### Fix

- FormGroup's description should have the same behavior with FormControl.

## 5.5.2

### Chore
Expand Down
2 changes: 1 addition & 1 deletion projects/ng-dynamic-json-form/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ng-dynamic-json-form",
"version": "5.5.2",
"version": "5.6.0",
"author": {
"name": "erqk",
"url": "https://github.com/erqk"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div
class="errors-container"
[ngClass]="{
hidden: !showErrors || layout?.hideErrorMessage === true
}"
[controlLayout]="{
type: 'error',
layout: layout
}"
>
<!-- Custom error message component -->
<ng-container #componentAnchor></ng-container>
<ng-container
*ngIf="layoutTemplates?.errorMessage"
[ngTemplateOutlet]="layoutTemplates!.errorMessage!"
[ngTemplateOutletContext]="{
control,
messages: errorMessages
}"
></ng-container>

<!-- Default error message component -->
<ng-container
*ngIf="!layoutComponents?.errorMessage && !layoutTemplates?.errorMessage"
>
<ng-container *ngFor="let error of errorMessages">
<div>{{ error }}</div>
</ng-container>
</ng-container>
</div>
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
DestroyRef,
EventEmitter,
HostBinding,
Input,
OnInit,
Output,
ViewChild,
ViewContainerRef,
inject,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { AbstractControl } from '@angular/forms';
import { tap } from 'rxjs';
import { ControlLayoutDirective } from '../../directives';
import { ValidatorConfig } from '../../models';
import { FormLayout } from '../../models/form-layout.interface';
import {
LayoutComponents,
LayoutTemplates,
} from '../../ng-dynamic-json-form.config';
import { FormValidationService } from '../../services/form-validation.service';

@Component({
selector: 'error-message',
template: ` <ng-container *ngFor="let error of errorMessages">
<div>{{ error }}</div>
</ng-container>`,
standalone: true,
imports: [CommonModule],
imports: [CommonModule, ControlLayoutDirective],
templateUrl: './error-message.component.html',
})
export class ErrorMessageComponent {
export class ErrorMessageComponent implements OnInit, AfterViewInit {
private _destroyRef = inject(DestroyRef);
private _internal_formValidationService = inject(FormValidationService);

@Input() control?: AbstractControl | null = null;
@Input() validators?: ValidatorConfig[];
@Input() layout?: FormLayout;
@Input() layoutComponents?: LayoutComponents;
@Input() layoutTemplates?: LayoutTemplates;
@Input() hideErrorMessage?: boolean;
@Output() errorMessagesGet = new EventEmitter<string[]>();

@ViewChild('componentAnchor', { read: ViewContainerRef })
private _componentAnchor!: ViewContainerRef;

@HostBinding('class.error-message') hostClass = true;

errorMessages: string[] = [];
Expand All @@ -46,4 +61,34 @@ export class ErrorMessageComponent {
)
.subscribe();
}

ngAfterViewInit(): void {
this._injectComponent();
}

get showErrors(): boolean {
const controlTouched = this.control?.touched ?? false;
const controlDirty = this.control?.dirty ?? false;
const hasErrors = !!this.control?.errors;

if (this.hideErrorMessage) {
return false;
}

return (controlDirty || controlTouched) && hasErrors;
}

private _injectComponent(): void {
if (!this.layoutComponents?.errorMessage || !this._componentAnchor) {
return;
}

this._componentAnchor.clear();
const componentRef = this._componentAnchor.createComponent(
this.layoutComponents.errorMessage
);

componentRef.instance.control = this.control;
componentRef.instance.validators = this.validators;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
<ng-container *ngIf="data">
<!-- Description (before) -->
<ng-container
*ngIf="data?.description && data?.layout?.descriptionPosition !== 'after'"
[ngTemplateOutlet]="descriptionTemplate"
[ngTemplateOutletContext]="{
description: data.description,
layout: data.layout
}"
></ng-container>

<div
class="input-container"
[ngClass]="{
Expand Down Expand Up @@ -50,62 +40,13 @@
</ng-container>
</div>

<!-- Description (after) -->
<ng-container
*ngIf="data?.description && data?.layout?.descriptionPosition === 'after'"
[ngTemplateOutlet]="descriptionTemplate"
[ngTemplateOutletContext]="{
description: data.description,
layout: data.layout
}"
></ng-container>

<div
class="errors-container"
[ngClass]="{
hidden: !showErrors
}"
[controlLayout]="{
type: 'error',
layout: data.layout
}"
>
<!-- Custom error message component -->
<ng-container #errorComponentAnchor></ng-container>
<ng-container
*ngIf="layoutTemplates?.errorMessage"
[ngTemplateOutlet]="layoutTemplates!.errorMessage!"
[ngTemplateOutletContext]="{
control,
messages: errorMessages
}"
></ng-container>

<!-- Default error message component -->
<ng-container
*ngIf="!layoutComponents?.errorMessage && !layoutTemplates?.errorMessage"
>
<error-message
[control]="control"
[validators]="data.validators"
(errorMessagesGet)="onErrorMessagesGet($event)"
></error-message>
</ng-container>
</div>
<error-message
[control]="control"
[validators]="data.validators"
[layout]="data.layout"
[layoutComponents]="layoutComponents"
[layoutTemplates]="layoutTemplates"
[hideErrorMessage]="hideErrorMessage"
(errorMessagesGet)="onErrorMessagesGet($event)"
></error-message>
</ng-container>

<!-- Description template -->
<ng-template
#descriptionTemplate
let-description="description"
let-layout="layout"
>
<span
class="form-description"
[controlLayout]="{
type: 'description',
layout
}"
>{{ description }}</span
>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
ValidationErrors,
Validator,
} from '@angular/forms';
import { EMPTY, Observable, catchError, finalize, of, tap } from 'rxjs';
import { EMPTY, Observable, finalize, tap } from 'rxjs';
import { UI_BASIC_COMPONENTS } from '../../../ui-basic/ui-basic-components.constant';
import { UiBasicInputComponent } from '../../../ui-basic/ui-basic-input/ui-basic-input.component';
import { ControlLayoutDirective } from '../../directives';
Expand Down Expand Up @@ -140,7 +140,6 @@ export class FormControlComponent implements ControlValueAccessor, Validator {

ngAfterViewInit(): void {
this._injectInputComponent();
this._injectErrorMessageComponent();
this._viewInitialized = true;
this._cd.markForCheck();
this._cd.detectChanges();
Expand Down Expand Up @@ -208,20 +207,6 @@ export class FormControlComponent implements ControlValueAccessor, Validator {
}
}

private _injectErrorMessageComponent(): void {
if (!this.layoutComponents?.errorMessage) return;

const componentRef = this._injectComponent(
this.errorComponentAnchor,
this.layoutComponents.errorMessage
);

if (!componentRef) return;

componentRef.instance.control = this.control;
componentRef.instance.validators = this.data?.validators;
}

private _fetchOptions(): void {
if (!this.data || !this.data.options) return;
const { sourceList, trigger, autoSelectFirst, data } = this.data.options;
Expand Down
4 changes: 4 additions & 0 deletions projects/ng-dynamic-json-form/src/lib/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@
"contentCollapsible": {
"enum": ["collapse", "expand"],
"description": "Enable expand/collapse of content. The default state will be determined by value provided"
},
"autoAddRequiredClass": {
"type": "boolean",
"description": "Add `required` class automatically to control if there's validator named `required`. Default is true."
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ export interface FormLayout {

/**Enable expand/collapse of content. The default state will be determined by value provided */
contentCollapsible?: 'collapse' | 'expand';

/**Add `required` class automatically to control if there's validator named `required`.
* Default is true.
*/
autoAddRequiredClass?: boolean;
}
Loading

0 comments on commit 71e15ab

Please sign in to comment.