Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .changeset/fancy-lies-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@sl-design-system/angular': minor
---

- Add explicit `markForChanges()` to all `ControlValueAccessor` directives
- Add support for the `<sl-number-field>` component:
- Added `NumberFieldComponent` for exposing the web component API within Angular
- Added `NumberFieldDirective` for Angular form integration

The above work the same as the existing bindings (for `<sl-text-field>` for example).
1 change: 1 addition & 0 deletions packages/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@sl-design-system/form": "^1.2.4",
"@sl-design-system/icon": "^1.2.1",
"@sl-design-system/locales": "^0.0.13",
"@sl-design-system/number-field": "^0.1.4",
"@sl-design-system/radio-group": "^1.1.4",
"@sl-design-system/select": "^2.0.4",
"@sl-design-system/switch": "^1.1.4",
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/checkbox-group.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type CheckboxGroup } from '@sl-design-system/checkbox';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class CheckboxGroupDirective extends FormControlElementDirective<CheckboxGroup> {
constructor(@Inject(ElementRef) elementRef: ElementRef<CheckboxGroup>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<CheckboxGroup>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/checkbox.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type Checkbox } from '@sl-design-system/checkbox';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class CheckboxDirective extends FormControlElementDirective<Checkbox> {
constructor(@Inject(ElementRef) elementRef: ElementRef<Checkbox>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<Checkbox>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ElementRef, Injectable, type OnDestroy, type OnInit } from '@angular/core';
import { type ChangeDetectorRef, type ElementRef, Injectable, type OnDestroy, type OnInit } from '@angular/core';
import { type AbstractControl, type ControlValueAccessor, type ValidationErrors, type Validator } from '@angular/forms';
import { type FormControl } from '@sl-design-system/form';

Expand All @@ -9,6 +9,7 @@ export abstract class FormControlElementDirective<T extends HTMLElement & FormCo
#onChange = (event: Event): void => {
this.onChange((event as CustomEvent<T['formValue']>).detail);
this.onTouched();
this.changeDetection.markForCheck();
};

protected onChange: (value: T['formValue']) => void = () => {};
Expand All @@ -19,7 +20,10 @@ export abstract class FormControlElementDirective<T extends HTMLElement & FormCo
return this.elementRef.nativeElement;
}

constructor(protected elementRef: ElementRef<T>) {}
constructor(
protected elementRef: ElementRef<T>,
protected changeDetection: ChangeDetectorRef
) {}

ngOnInit(): void {
this.element.addEventListener('sl-blur', this.onTouched);
Expand Down
57 changes: 57 additions & 0 deletions packages/angular/src/forms/number-field.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type NumberField } from '@sl-design-system/number-field';
import { FormControlElementDirective } from './form-control-element.directive';

@Directive({
selector: 'sl-number-field',
standalone: true,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberFieldDirective),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => NumberFieldDirective),
multi: true
}
]
})
export class NumberFieldDirective extends FormControlElementDirective<NumberField> {
constructor(
@Inject(ElementRef) elementRef: ElementRef<NumberField>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
if (this.element.valid) {
return null;
} else if (
!this.element.valid &&
typeof this.element.min === 'number' &&
typeof this.element.valueAsNumber === 'number' &&
this.element.min > this.element.valueAsNumber
) {
return {
rangeUnderflow: { min: this.element.min, actual: this.element.valueAsNumber }
};
} else if (
!this.element.valid &&
typeof this.element.max === 'number' &&
typeof this.element.valueAsNumber === 'number' &&
this.element.max < this.element.valueAsNumber
) {
return {
rangeOverflow: { max: this.element.max, actual: this.element.valueAsNumber }
};
} else if (this.element.validity.valueMissing) {
return { required: true };
}

return null;
}
}
1 change: 1 addition & 0 deletions packages/angular/src/forms/public-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './checkbox-group.directive';
export * from './checkbox.directive';
export * from './form-control-element.directive';
export * from './number-field.directive';
export * from './radio-group.directive';
export * from './select.directive';
export * from './switch.directive';
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/radio-group.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type RadioGroup } from '@sl-design-system/radio-group';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class RadioGroupDirective extends FormControlElementDirective<RadioGroup> {
constructor(@Inject(ElementRef) elementRef: ElementRef<RadioGroup>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<RadioGroup>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/select.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type Select } from '@sl-design-system/select';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class SelectDirective extends FormControlElementDirective<Select> {
constructor(@Inject(ElementRef) elementRef: ElementRef<Select>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<Select>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/switch.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import { type Switch } from '@sl-design-system/switch';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,7 +20,10 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class SwitchDirective extends FormControlElementDirective<Switch> {
constructor(@Inject(ElementRef) elementRef: ElementRef<Switch>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<Switch>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}
}
9 changes: 6 additions & 3 deletions packages/angular/src/forms/text-area.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type TextArea } from '@sl-design-system/text-area';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class TextAreaDirective extends FormControlElementDirective<TextArea> {
constructor(@Inject(ElementRef) elementRef: ElementRef<TextArea>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<TextArea>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
9 changes: 6 additions & 3 deletions packages/angular/src/forms/text-field.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { ChangeDetectorRef, Directive, ElementRef, Inject, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, type ValidationErrors } from '@angular/forms';
import { type TextField } from '@sl-design-system/text-field';
import { FormControlElementDirective } from './form-control-element.directive';
Expand All @@ -20,8 +20,11 @@ import { FormControlElementDirective } from './form-control-element.directive';
]
})
export class TextFieldDirective extends FormControlElementDirective<TextField> {
constructor(@Inject(ElementRef) elementRef: ElementRef<TextField>) {
super(elementRef);
constructor(
@Inject(ElementRef) elementRef: ElementRef<TextField>,
@Inject(ChangeDetectorRef) changeDetection: ChangeDetectorRef
) {
super(elementRef, changeDetection);
}

override validate(): ValidationErrors | null {
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@sl-design-system/dialog": "*",
"@sl-design-system/form": "*",
"@sl-design-system/icon": "*",
"@sl-design-system/number-field": "*",
"@sl-design-system/radio-group": "*",
"@sl-design-system/select": "*",
"@sl-design-system/switch": "*",
Expand Down
Loading