feat(i18n): add better solution for formly translations #33

This commit is contained in:
Johannes Millan 2019-07-14 23:29:06 +02:00
parent 03cf3d4bc0
commit 820aa166e4
70 changed files with 82 additions and 1555 deletions

View file

@ -145,6 +145,7 @@
"@ngrx/store": "8.1.0",
"@ngrx/store-devtools": "^8.1.0",
"@ngx-formly/core": "^5.2.0",
"@ngx-formly/material": "^5.2.0",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"@schematics/angular": "^8.0.1",

View file

@ -13,7 +13,7 @@ import {UiModule} from './ui/ui.module';
import {reducers} from './root-store';
import {CoreModule} from './core/core.module';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyModule} from '@ngx-formly/core';
import {FORMLY_CONFIG, FormlyModule} from '@ngx-formly/core';
import {PagesModule} from './pages/pages.module';
import {MainHeaderModule} from './core-ui/main-header/main-header.module';
import {HttpClient, HttpClientModule} from '@angular/common/http';
@ -31,6 +31,7 @@ import {ProcrastinationModule} from './features/procrastination/procrastination.
import {TaskRepeatCfgModule} from './features/task-repeat-cfg/task-repeat-cfg.module';
import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {registerTranslateExtension} from './ui/formly-translate-extension/formly-translate-extension';
// NOTE: export required for aot to work
export function createTranslateLoader(http: HttpClient) {
@ -91,7 +92,13 @@ export function createTranslateLoader(http: HttpClient) {
bootstrap: [AppComponent],
providers: [
{provide: ErrorHandler, useClass: GlobalErrorHandler},
{provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig}
{provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig},
{
provide: FORMLY_CONFIG,
multi: true,
useFactory: registerTranslateExtension,
deps: [TranslateService],
},
],
})
export class AppModule {

View file

@ -1,6 +1,6 @@
import {ChangeDetectionStrategy, Component, ViewChild} from '@angular/core';
import {MatInput} from '@angular/material/input';
import {FieldType} from '../../../ui/formly-material-components/form-field';
import {FieldType} from '@ngx-formly/material';
@Component({
selector: 'keyboard-input',

View file

@ -1,6 +1,6 @@
import {ChangeDetectionStrategy, Component, ViewChild} from '@angular/core';
import {MatInput} from '@angular/material/input';
import {FieldType} from '../../formly-material-components/form-field';
import {FieldType} from '@ngx-formly/material';
@Component({
selector: 'input-duration-formly',

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,34 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {FormlyFieldCheckbox} from './checkbox.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldCheckbox],
imports: [
CommonModule,
ReactiveFormsModule,
TranslateModule,
MatCheckboxModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [
{
name: 'checkbox',
component: FormlyFieldCheckbox,
wrappers: ['form-field'],
},
],
}),
],
})
export class FormlyMatCheckboxModule {
}

View file

@ -1,55 +0,0 @@
import {AfterViewInit, Component, OnDestroy, ViewChild} from '@angular/core';
import {FieldType} from '../../form-field';
import {MatCheckbox} from '@angular/material/checkbox';
import {FocusMonitor} from '@angular/cdk/a11y';
@Component({
selector: 'formly-field-mat-checkbox',
template: `
<mat-checkbox
[formControl]="formControl"
[id]="id"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0"
[indeterminate]="to.indeterminate && formControl.value === null"
[color]="to.color"
[labelPosition]="to.align || to.labelPosition">
{{ to.label|translate }}
<span *ngIf="to.required && to.hideRequiredMarker !== true"
class="mat-form-field-required-marker">*</span>
</mat-checkbox>
`,
})
export class FormlyFieldCheckbox extends FieldType implements AfterViewInit, OnDestroy {
@ViewChild(MatCheckbox, {static: true}) checkbox!: MatCheckbox;
defaultOptions = {
templateOptions: {
hideFieldUnderline: true,
indeterminate: true,
floatLabel: 'always',
hideLabel: true,
align: 'start', // start or end
},
};
constructor(private focusMonitor: FocusMonitor) {
super();
}
onContainerClick(event: MouseEvent): void {
this.checkbox.focus();
super.onContainerClick(event);
}
ngAfterViewInit() {
super.ngAfterViewInit();
this.focusMonitor
.monitor(this.checkbox._inputElement.nativeElement)
.subscribe(focusOrigin => this.field.focus = !!focusOrigin);
}
ngOnDestroy() {
super.ngOnDestroy();
this.focusMonitor.stopMonitoring(this.checkbox._inputElement.nativeElement);
}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatCheckboxModule} from './checkbox.module';
export {FormlyFieldCheckbox} from './checkbox.type';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,33 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatInputModule} from '@angular/material/input';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {FormlyDatepickerTypeComponent} from './datepicker.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyDatepickerTypeComponent],
imports: [
CommonModule,
ReactiveFormsModule,
MatInputModule,
MatDatepickerModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [{
name: 'datepicker',
component: FormlyDatepickerTypeComponent,
wrappers: ['form-field'],
}],
}),
],
})
export class FormlyMatDatepickerModule {
}

View file

@ -1,56 +0,0 @@
import {AfterViewInit, Component, TemplateRef, ViewChild} from '@angular/core';
import {ɵdefineHiddenProp as defineHiddenProp} from '@ngx-formly/core';
import {FieldType} from '../../form-field';
import {MatInput} from '@angular/material/input';
import {MatDatepickerInput} from '@angular/material/datepicker';
@Component({
selector: 'formly-field-mat-datepicker',
template: `
<input matInput
[id]="id"
[errorStateMatcher]="errorStateMatcher"
[formControl]="formControl"
[matDatepicker]="picker"
[matDatepickerFilter]="to.datepickerOptions.filter"
[max]="to.datepickerOptions.max"
[min]="to.datepickerOptions.min"
[formlyAttributes]="field"
[placeholder]="to.placeholder"
[tabindex]="to.tabindex || 0"
[readonly]="to.readonly">
<ng-template #datepickerToggle>
<mat-datepicker-toggle [for]="picker"></mat-datepicker-toggle>
</ng-template>
<mat-datepicker #picker
[color]="to.color"
[touchUi]="to.datepickerOptions.touchUi"
[startView]="to.datepickerOptions.startView"
[startAt]="to.datepickerOptions.startAt">
</mat-datepicker>
`,
})
export class FormlyDatepickerTypeComponent extends FieldType implements AfterViewInit {
@ViewChild(MatInput, {static: true}) formFieldControl!: MatInput;
@ViewChild(MatDatepickerInput, {static: true}) datepickerInput!: MatDatepickerInput<any>;
@ViewChild('datepickerToggle', {static: true}) datepickerToggle!: TemplateRef<any>;
defaultOptions = {
templateOptions: {
datepickerOptions: {
startView: 'month',
datepickerTogglePosition: 'suffix',
},
},
};
ngAfterViewInit() {
super.ngAfterViewInit();
// temporary fix for https://github.com/angular/material2/issues/6728
(<any>this.datepickerInput)._formField = this.formField;
setTimeout(() => {
defineHiddenProp(this.field, '_mat' + this.to.datepickerOptions.datepickerTogglePosition, this.datepickerToggle);
});
}
}

View file

@ -1 +0,0 @@
export {FormlyMatDatepickerModule} from './datepicker.module';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,123 +0,0 @@
import {AfterViewInit, OnDestroy, OnInit, TemplateRef, Type, ViewChild} from '@angular/core';
import {FieldType as CoreFieldType, FormlyFieldConfig, ɵdefineHiddenProp as defineHiddenProp} from '@ngx-formly/core';
import {Subject} from 'rxjs';
import {MatFormField, MatFormFieldControl} from '@angular/material/form-field';
import {FormlyErrorStateMatcher} from './formly.error-state-matcher';
export abstract class FieldType<F extends FormlyFieldConfig = FormlyFieldConfig> extends CoreFieldType<F> implements OnInit, AfterViewInit, OnDestroy, MatFormFieldControl<any> {
@ViewChild('matPrefix', {static: true}) matPrefix!: TemplateRef<any>;
@ViewChild('matSuffix', {static: true}) matSuffix!: TemplateRef<any>;
errorStateMatcher = new FormlyErrorStateMatcher(this);
stateChanges = new Subject<void>();
private _control!: MatFormFieldControl<any>;
get formFieldControl() {
return this._control || this;
}
set formFieldControl(control: MatFormFieldControl<any>) {
this._control = control;
if (this.formField && control !== this.formField._control) {
this.formField._control = control;
}
}
_errorState = false;
get errorState() {
const showError = this.options!.showError!(this);
if (showError !== this._errorState) {
this._errorState = showError;
this.stateChanges.next();
}
return showError;
}
get controlType() {
if (this.to.type) {
return this.to.type;
}
if ((<any>this.field.type) instanceof Type) {
return this.field.type!.constructor.name;
}
return this.field.type!;
}
get focused() {
return !!this.field.focus && !this.disabled;
}
get disabled() {
return !!this.to.disabled;
}
get required() {
return !!this.to.required;
}
get placeholder() {
return this.to.placeholder || '';
}
get shouldPlaceholderFloat() {
return this.shouldLabelFloat;
}
get value() {
return this.formControl.value;
}
set value(value) {
this.formControl.patchValue(value);
}
get ngControl() {
return this.formControl as any;
}
get empty() {
return this.value === undefined || this.value === null || this.value === '';
}
get shouldLabelFloat() {
return this.focused || !this.empty;
}
get formField(): MatFormField {
return this.field ? (<any>this.field)['__formField__'] : null;
}
ngOnInit() {
if (this.formField) {
this.formField._control = this.formFieldControl;
}
}
ngAfterViewInit() {
if (this.matPrefix || this.matSuffix) {
setTimeout(() => {
defineHiddenProp(this.field, '_matprefix', this.matPrefix);
defineHiddenProp(this.field, '_matsuffix', this.matSuffix);
(<any>this.options)._markForCheck(this.field);
});
}
}
ngOnDestroy() {
if (this.formField) {
delete this.formField._control;
}
this.stateChanges.complete();
}
setDescribedByIds(ids: string[]): void {
}
onContainerClick(event: MouseEvent): void {
this.field.focus = true;
this.stateChanges.next();
}
}

View file

@ -1,25 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {MatFormFieldModule} from '@angular/material/form-field';
import {FormlyWrapperFormField} from './form-field.wrapper';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyWrapperFormField],
imports: [
CommonModule,
ReactiveFormsModule,
MatFormFieldModule,
TranslateModule,
FormlyModule.forChild({
wrappers: [{
name: 'form-field',
component: FormlyWrapperFormField,
}],
}),
],
})
export class FormlyMatFormFieldModule {
}

View file

@ -1,172 +0,0 @@
import {
AfterContentChecked,
AfterViewInit,
Component,
OnDestroy,
OnInit,
Renderer2,
TemplateRef,
ViewChild,
ViewContainerRef
} from '@angular/core';
import {FieldWrapper, FormlyFieldConfig, ɵdefineHiddenProp as defineHiddenProp} from '@ngx-formly/core';
import {MatFormField, MatFormFieldControl} from '@angular/material/form-field';
import {Subject} from 'rxjs';
import {FieldType} from './field.type';
interface MatFormlyFieldConfig extends FormlyFieldConfig {
_matprefix: TemplateRef<any>;
_matsuffix: TemplateRef<any>;
__formField__: FormlyWrapperFormField;
_componentFactory: any;
}
@Component({
selector: 'formly-wrapper-mat-form-field',
template: `
<!-- fix https://github.com/angular/material2/pull/7083 by setting width to 100% -->
<mat-form-field
[hideRequiredMarker]="true"
[floatLabel]="to.floatLabel"
[appearance]="to.appearance"
[color]="to.color"
[style.width]="'100%'">
<ng-container #fieldComponent></ng-container>
<mat-label *ngIf="to.label && to.hideLabel !== true">
{{ to.label|translate }}
<span *ngIf="to.required && to.hideRequiredMarker !== true"
class="mat-form-field-required-marker">*</span>
</mat-label>
<ng-container matPrefix>
<ng-container *ngTemplateOutlet="to.prefix ? to.prefix : formlyField._matprefix"></ng-container>
</ng-container>
<ng-container matSuffix>
<ng-container *ngTemplateOutlet="to.suffix ? to.suffix : formlyField._matsuffix"></ng-container>
</ng-container>
<!-- fix https://github.com/angular/material2/issues/7737 by setting id to null -->
<mat-error [id]="null">
<formly-validation-message [field]="field"></formly-validation-message>
</mat-error>
<!-- fix https://github.com/angular/material2/issues/7737 by setting id to null -->
<mat-hint *ngIf="to.description"
[id]="null">{{ to.description }}</mat-hint>
</mat-form-field>
`,
providers: [{provide: MatFormFieldControl, useExisting: FormlyWrapperFormField}],
})
export class FormlyWrapperFormField extends FieldWrapper<MatFormlyFieldConfig> implements OnInit, OnDestroy, MatFormFieldControl<any>, AfterViewInit, AfterContentChecked {
@ViewChild('fieldComponent', {read: ViewContainerRef, static: true}) fieldComponent!: ViewContainerRef;
@ViewChild(MatFormField, {static: true}) formField!: MatFormField;
stateChanges = new Subject<void>();
private initialGapCalculated = false;
constructor(private renderer: Renderer2) {
super();
}
_errorState = false;
get errorState() {
const showError = this.options!.showError!(this);
if (showError !== this._errorState) {
this._errorState = showError;
this.stateChanges.next();
}
return showError;
}
get controlType() {
return this.to.type;
}
get focused() {
return !!this.formlyField.focus && !this.disabled;
}
get disabled() {
return !!this.to.disabled;
}
get required() {
return !!this.to.required;
}
get placeholder() {
return this.to.placeholder || '';
}
get shouldPlaceholderFloat() {
return this.shouldLabelFloat;
}
get value() {
return this.formControl.value;
}
get ngControl() {
return this.formControl as any;
}
get empty() {
return !this.formControl.value;
}
get shouldLabelFloat() {
return this.focused || !this.empty;
}
get formlyField() {
return this.field as MatFormlyFieldConfig;
}
ngOnInit() {
this.formField._control = this;
defineHiddenProp(this.field, '__formField__', this.formField);
const fieldComponent = this.formlyField['_componentFactory'];
if (fieldComponent && !(fieldComponent.componentRef.instance instanceof FieldType)) {
console.warn(`Component '${fieldComponent.component.prototype.constructor.name}' must extend 'FieldType' from '@ngx-formly/material'.`);
}
// fix for https://github.com/angular/material2/issues/11437
if (this.formlyField.hide && this.formlyField.templateOptions!.appearance === 'outline') {
this.initialGapCalculated = true;
}
}
ngAfterContentChecked() {
if (!this.initialGapCalculated || this.formlyField.hide) {
return;
}
this.formField.updateOutlineGap();
this.initialGapCalculated = true;
}
ngAfterViewInit() {
// temporary fix for https://github.com/angular/material2/issues/7891
if (this.formField.underlineRef && this.to.hideFieldUnderline === true) {
this.renderer.removeClass(this.formField.underlineRef.nativeElement, 'mat-form-field-underline');
this.renderer.removeClass(this.formField.underlineRef.nativeElement.firstChild, 'mat-form-field-ripple');
}
}
ngOnDestroy() {
delete this.formlyField.__formField__;
this.stateChanges.complete();
}
setDescribedByIds(ids: string[]): void {
}
onContainerClick(event: MouseEvent): void {
this.formlyField.focus = true;
this.stateChanges.next();
}
}

View file

@ -1,12 +0,0 @@
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
import {FieldType} from '@ngx-formly/core';
export class FormlyErrorStateMatcher implements ErrorStateMatcher {
constructor(private field: FieldType) {
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.field && this.field.showError;
}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatFormFieldModule} from './form-field.module';
export {FieldType} from './field.type';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,33 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatInputModule} from '@angular/material/input';
import {FormlyFieldInput} from './input.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldInput],
imports: [
CommonModule,
ReactiveFormsModule,
MatInputModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [
{
name: 'input',
component: FormlyFieldInput,
wrappers: ['form-field'],
},
],
}),
],
})
export class FormlyMatInputModule {
}

View file

@ -1,85 +0,0 @@
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {FormGroup, ReactiveFormsModule} from '@angular/forms';
import {MatInputModule} from '@angular/material/input';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {FormlyFieldConfig, FormlyFormOptions, FormlyModule} from '@ngx-formly/core';
import {FormlyFieldInput} from './input.type';
@Component({
selector: 'formly-test',
template: `
<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>
`,
})
class FormlyTestComponent {
form = new FormGroup({});
fields: FormlyFieldConfig[];
model: any;
options: FormlyFormOptions;
}
describe('ui-material: Formly Input Component', () => {
let fixture: ComponentFixture<FormlyTestComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FormlyTestComponent, FormlyFieldInput],
imports: [
NoopAnimationsModule,
MatInputModule,
ReactiveFormsModule,
FormlyModule.forRoot({
types: [
{
name: 'input',
component: FormlyFieldInput,
},
],
}),
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FormlyTestComponent);
});
it('should properly set the readonly value on text inputs', () => {
const componentInstance = fixture.componentInstance;
componentInstance.fields = [
{
key: 'name',
type: 'input',
templateOptions: {
readonly: true,
},
},
];
fixture.detectChanges();
// assert
const inputField = fixture.debugElement.query(By.css('input'));
expect(inputField.nativeElement.getAttribute('readonly')).not.toBeNull();
});
it('should properly set the readonly value on number inputs', () => {
const componentInstance = fixture.componentInstance;
componentInstance.fields = [
{
key: 'name',
type: 'input',
templateOptions: {
type: 'number',
readonly: true,
},
},
];
fixture.detectChanges();
// assert
const inputField = fixture.debugElement.query(By.css('input'));
expect(inputField.nativeElement.getAttribute('readonly')).not.toBeNull();
});
});

View file

@ -1,37 +0,0 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {MatInput} from '@angular/material/input';
import {FieldType} from '../../form-field';
@Component({
selector: 'formly-field-mat-input',
template: `
<input *ngIf="type !== 'number'; else numberTmp"
matInput
[id]="id"
[readonly]="to.readonly"
[type]="type || 'text'"
[errorStateMatcher]="errorStateMatcher"
[formControl]="formControl"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0"
[placeholder]="to.placeholder|translate">
<ng-template #numberTmp>
<input matInput
[id]="id"
type="number"
[readonly]="to.readonly"
[errorStateMatcher]="errorStateMatcher"
[formControl]="formControl"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0"
[placeholder]="to.placeholder|translate">
</ng-template>
`,
})
export class FormlyFieldInput extends FieldType implements OnInit {
@ViewChild(MatInput, {static: true}) formFieldControl!: MatInput;
get type() {
return this.to.type || 'text';
}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatInputModule} from './input.module';
export {FormlyFieldInput} from './input.type';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,35 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyModule} from '@ngx-formly/core';
import {FormlySelectModule} from '@ngx-formly/core/select';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {FormlyFieldMultiCheckbox} from './multicheckbox.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldMultiCheckbox],
imports: [
CommonModule,
ReactiveFormsModule,
TranslateModule,
MatCheckboxModule,
FormlyMatFormFieldModule,
FormlySelectModule,
FormlyModule.forChild({
types: [
{
name: 'multicheckbox',
component: FormlyFieldMultiCheckbox,
wrappers: ['form-field'],
},
],
}),
],
})
export class FormlyMatMultiCheckboxModule {
}

View file

@ -1,41 +0,0 @@
import {Component} from '@angular/core';
import {FieldType} from '../../form-field';
@Component({
selector: 'formly-field-mat-multicheckbox',
template: `
<ng-container *ngFor="let option of to.options | formlySelectOptions:field | async; let i = index;">
<mat-checkbox
[id]="id + '_' + i"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0"
[color]="to.color"
[labelPosition]="to.labelPosition"
[checked]="formControl.value && (this.to.type === 'array' ? formControl.value.includes(option.value) : formControl.value[option.value])"
(change)="onChange(option.value, $event.checked)">
{{ option.label|translate }}
</mat-checkbox>
</ng-container>
`,
})
export class FormlyFieldMultiCheckbox extends FieldType {
defaultOptions = {
templateOptions: {
hideFieldUnderline: true,
floatLabel: 'always',
options: [],
},
};
onChange(value: any, checked: boolean) {
if (this.to.type === 'array') {
this.formControl.patchValue(checked
? [...(this.formControl.value || []), value]
: [...(this.formControl.value || [])].filter(o => o !== value),
);
} else {
this.formControl.patchValue({...this.formControl.value, [value]: checked});
}
this.formControl.markAsTouched();
}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatMultiCheckboxModule} from './multicheckbox.module';
export {FormlyFieldMultiCheckbox} from './multicheckbox.type';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,35 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {FormlySelectModule} from '@ngx-formly/core/select';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatInputModule} from '@angular/material/input';
import {FormlyFieldNativeSelect} from './native-select.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldNativeSelect],
imports: [
CommonModule,
ReactiveFormsModule,
MatInputModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlySelectModule,
FormlyModule.forChild({
types: [
{
name: 'native-select',
component: FormlyFieldNativeSelect,
wrappers: ['form-field'],
},
],
}),
],
})
export class FormlyMatNativeSelectModule {
}

View file

@ -1,39 +0,0 @@
import {Component, ViewChild} from '@angular/core';
import {FieldType} from '../../form-field';
import {MatInput} from '@angular/material/input';
@Component({
selector: 'formly-field-mat-native-select',
template: `
<select matNativeControl
[id]="id"
[readonly]="to.readonly"
[errorStateMatcher]="errorStateMatcher"
[formControl]="formControl"
[formlyAttributes]="field">
<option *ngIf="to.placeholder"
[ngValue]="null">{{ to.placeholder }}</option>
<ng-container *ngFor="let item of to.options | formlySelectOptions:field | async">
<optgroup *ngIf="item.group"
label="{{item.label|translate}}">
<option *ngFor="let child of item.group"
[ngValue]="child.value"
[disabled]="child.disabled">
{{ child.label|translate }}
</option>
</optgroup>
<option *ngIf="!item.group"
[ngValue]="item.value"
[disabled]="item.disabled">{{ item.label|translate }}</option>
</ng-container>
</select>
`,
})
export class FormlyFieldNativeSelect extends FieldType {
@ViewChild(MatInput, {static: true}) formFieldControl!: MatInput;
defaultOptions = {
templateOptions: {
options: [],
},
};
}

View file

@ -1,2 +0,0 @@
export {FormlyMatNativeSelectModule} from './native-select.module';
export {FormlyFieldNativeSelect} from './native-select.type';

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatRadioModule} from './radio.module';
export {FormlyFieldRadio} from './radio.type';

View file

@ -1,33 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlySelectModule} from '@ngx-formly/core/select';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatRadioModule} from '@angular/material/radio';
import {FormlyFieldRadio} from './radio.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldRadio],
imports: [
CommonModule,
ReactiveFormsModule,
MatRadioModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlySelectModule,
FormlyModule.forChild({
types: [{
name: 'radio',
component: FormlyFieldRadio,
wrappers: ['form-field'],
}],
}),
],
})
export class FormlyMatRadioModule {
}

View file

@ -1,38 +0,0 @@
import {Component, ViewChild} from '@angular/core';
import {FieldType} from '../../form-field';
import {MatRadioGroup} from '@angular/material/radio';
@Component({
selector: 'formly-field-mat-radio',
template: `
<mat-radio-group
[formControl]="formControl"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0">
<mat-radio-button *ngFor="let option of to.options | formlySelectOptions:field | async; let i = index;"
[id]="id + '_' + i"
[color]="to.color"
[labelPosition]="to.labelPosition"
[value]="option.value">
{{ option.label|translate }}
</mat-radio-button>
</mat-radio-group>
`,
})
export class FormlyFieldRadio extends FieldType {
@ViewChild(MatRadioGroup, {static: true}) radioGroup!: MatRadioGroup;
defaultOptions = {
templateOptions: {
hideFieldUnderline: true,
floatLabel: 'always',
options: [],
},
};
onContainerClick(event: MouseEvent): void {
if (this.radioGroup._radios.length && !this.radioGroup.selected) {
this.radioGroup._radios.first.focus();
}
super.onContainerClick(event);
}
}

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatSelectModule} from './select.module';
export {FormlyFieldSelect} from './select.type';

View file

@ -1,35 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyModule} from '@ngx-formly/core';
import {FormlySelectModule} from '@ngx-formly/core/select';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatSelectModule} from '@angular/material/select';
import {FormlyFieldSelect} from './select.type';
import {MatPseudoCheckboxModule} from '@angular/material/core';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldSelect],
imports: [
CommonModule,
ReactiveFormsModule,
MatSelectModule,
MatPseudoCheckboxModule,
TranslateModule.forChild(),
FormlyMatFormFieldModule,
FormlySelectModule,
FormlyModule.forChild({
types: [{
name: 'select',
component: FormlyFieldSelect,
wrappers: ['form-field'],
}],
}),
],
})
export class FormlyMatSelectModule {
}

View file

@ -1,236 +0,0 @@
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MatSelectModule} from '@angular/material/select';
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {createGenericTestComponent} from '../../../../../../ngx-formly/src/core/src/lib/test-utils';
import {By} from '@angular/platform-browser';
import {Component, ViewChild} from '@angular/core';
import {FormGroup, ReactiveFormsModule} from '@angular/forms';
import {FormlyForm, FormlyModule} from '@ngx-formly/core';
import {FormlySelectModule} from '@ngx-formly/core/select';
import {FormlyFieldSelect} from './select.type';
import {of as observableOf} from 'rxjs';
import {MatPseudoCheckboxModule} from '@angular/material/core';
import {timeout} from 'rxjs/operators';
const createTestComponent = (html: string) =>
createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
let testComponentInputs;
describe('ui-material: Formly Field Select Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent, FormlyFieldSelect],
imports: [
NoopAnimationsModule,
MatSelectModule,
MatPseudoCheckboxModule,
ReactiveFormsModule,
FormlySelectModule,
FormlyModule.forRoot({
types: [
{
name: 'select',
component: FormlyFieldSelect,
},
],
}),
],
});
});
describe('options', () => {
beforeEach(() => {
testComponentInputs = {
form: new FormGroup({}),
options: {},
model: {},
};
});
it('should correctly bind to a static array of data', () => {
testComponentInputs.fields = [{
key: 'sportId',
type: 'select',
templateOptions: {
options: [
{id: '1', name: 'Soccer'},
{id: '2', name: 'Basketball'},
{id: {test: 'A'}, name: 'Not Soccer or Basketball'},
],
valueProp: 'id',
labelProp: 'name',
},
}];
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>'),
trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3);
});
it('should correctly bind to an Observable', async(() => {
const sports$ = observableOf([
{id: '1', name: 'Soccer'},
{id: '2', name: 'Basketball'},
{id: {test: 'A'}, name: 'Not Soccer or Basketball'},
]);
testComponentInputs.fields = [{
key: 'sportId',
type: 'select',
templateOptions: {
options: sports$,
valueProp: 'id',
labelProp: 'name',
},
}];
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>'),
trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3);
}));
});
describe('multi select', () => {
beforeEach(() => {
testComponentInputs = {
form: new FormGroup({}),
options: {},
model: {},
};
testComponentInputs.fields = [{
key: 'sportId',
type: 'select',
templateOptions: {
multiple: true,
selectAllOption: 'Select All',
options: [
{id: '1', name: 'Soccer'},
{id: '2', name: 'Basketball'},
{id: '3', name: 'Martial Arts'},
],
valueProp: 'id',
labelProp: 'name',
},
}];
});
it('should have a "Select All" option if configured', () => {
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>');
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(1 + 3);
});
it('should select all options if clicking the "Select All" option', () => {
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>');
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
const selectAllOption = fixture.debugElement.queryAll(By.css('mat-option'))[0].nativeElement;
selectAllOption.click();
fixture.detectChanges();
expect(testComponentInputs.form.get('sportId').value.length).toEqual(3);
// clicking again should deselect all
selectAllOption.click();
fixture.detectChanges();
expect(testComponentInputs.form.get('sportId').value.length).toEqual(0);
});
it('should use the selectAllOption prop as label for the option entry', () => {
testComponentInputs.fields = [{
key: 'sportId',
type: 'select',
templateOptions: {
multiple: true,
selectAllOption: 'Click me!!',
options: [
{id: '1', name: 'Soccer'},
{id: '2', name: 'Basketball'},
{id: '3', name: 'Martial Arts'},
],
valueProp: 'id',
labelProp: 'name',
},
}];
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>');
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
const selectAllOption = fixture.debugElement.queryAll(By.css('mat-option'))[0].nativeElement;
expect(selectAllOption.innerHTML).toContain('Click me!!');
});
it('should correctly bind a multi select to an observable', fakeAsync(() => {
// bind a value which triggers the error in case
testComponentInputs.model = {
sportId: [1],
};
testComponentInputs.fields = [{
key: 'sportId',
type: 'select',
templateOptions: {
multiple: true,
selectAllOption: 'Click me!!',
options: observableOf([
{id: '1', name: 'Soccer'},
{id: '2', name: 'Basketball'},
{id: '3', name: 'Martial Arts'},
]).pipe(
timeout(50),
),
valueProp: 'id',
labelProp: 'name',
},
}];
const fixture = createTestComponent('<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options"></formly-form>');
tick(51);
fixture.detectChanges();
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
trigger.click();
fixture.detectChanges();
const selectAllOption = fixture.debugElement.queryAll(By.css('mat-option'))[0].nativeElement;
expect(selectAllOption.innerHTML).toContain('Click me!!');
}));
});
});
@Component({selector: 'formly-form-test', template: '', entryComponents: []})
class TestComponent {
@ViewChild(FormlyForm) formlyForm: FormlyForm;
fields = testComponentInputs.fields;
form: FormGroup = testComponentInputs.form;
model = testComponentInputs.model || {};
options = testComponentInputs.options;
}

View file

@ -1,89 +0,0 @@
import {Component} from '@angular/core';
import {MatSelectChange} from '@angular/material/select';
import {FieldType} from '../../form-field';
@Component({
selector: 'formly-field-mat-select',
template: `
<ng-template #selectAll
let-selectOptions="selectOptions">
<mat-option (click)="toggleSelectAll(selectOptions)">
<mat-pseudo-checkbox class="mat-option-pseudo-checkbox"
[state]="getState(selectOptions)">
</mat-pseudo-checkbox>
{{ to.selectAllOption }}
</mat-option>
</ng-template>
<mat-select [id]="id"
[formControl]="formControl"
[formlyAttributes]="field"
[placeholder]="to.placeholder"
[tabindex]="to.tabindex || 0"
[compareWith]="to.compareWith || compareWith"
[multiple]="to.multiple"
(selectionChange)="change($event)"
[errorStateMatcher]="errorStateMatcher"
[aria-labelledby]="formField?._labelId"
[disableOptionCentering]="to.disableOptionCentering"
>
<ng-container *ngIf="to.options | formlySelectOptions:field | async as selectOptions">
<ng-container *ngIf="to.multiple && to.selectAllOption"
[ngTemplateOutlet]="selectAll"
[ngTemplateOutletContext]="{ selectOptions: selectOptions }">
</ng-container>
<ng-container *ngFor="let item of selectOptions">
<mat-optgroup *ngIf="item.group"
[label]="item.label|translate">
<mat-option *ngFor="let child of item.group"
[value]="child.value"
[disabled]="child.disabled">
{{ child.label|translate }}
</mat-option>
</mat-optgroup>
<mat-option *ngIf="!item.group"
[value]="item.value"
[disabled]="item.disabled">{{ item.label|translate }}</mat-option>
</ng-container>
</ng-container>
</mat-select>
`,
})
export class FormlyFieldSelect extends FieldType {
defaultOptions = {
templateOptions: {options: []},
};
get value() {
return this.formControl.value || [];
}
getState(options: any[]) {
if (this.value.length > 0) {
return this.value.length !== options.length
? 'indeterminate'
: 'checked';
}
return '';
}
toggleSelectAll(options: any[]) {
this.formControl.setValue(
this.value.length !== options.length
? options.map(x => x.value)
: [],
);
}
change($event: MatSelectChange) {
if (this.to.change) {
this.to.change(this.field, $event);
}
}
compareWith(o1: any, o2: any) {
return o1 === o2;
}
}

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1 +0,0 @@
export {FormlyMatSliderModule} from './slider.module';

View file

@ -1,29 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {MatSliderModule} from '@angular/material/slider';
import {FormlyMatFormFieldModule} from '../../form-field';
import {FormlySliderTypeComponent} from './slider.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlySliderTypeComponent],
imports: [
CommonModule,
ReactiveFormsModule,
MatSliderModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [{
name: 'slider',
component: FormlySliderTypeComponent,
wrappers: ['form-field'],
}],
}),
],
})
export class FormlyMatSliderModule {
}

View file

@ -1,31 +0,0 @@
import {Component, ViewChild} from '@angular/core';
import {FieldType} from '../../form-field';
import {MatSlider} from '@angular/material/slider';
@Component({
selector: 'formly-field-mat-slider',
template: `
<mat-slider
[id]="id"
[style.width]="'100%'"
[formControl]="formControl"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0"
[color]="to.color">
</mat-slider>
`,
})
export class FormlySliderTypeComponent extends FieldType {
@ViewChild(MatSlider, {static: true}) slider!: MatSlider;
defaultOptions = {
templateOptions: {
hideFieldUnderline: true,
floatLabel: 'always',
},
};
onContainerClick(event: MouseEvent): void {
this.slider.focus();
super.onContainerClick(event);
}
}

View file

@ -1,27 +0,0 @@
import {NgModule} from '@angular/core';
import {FormlyMatFormFieldModule} from '../../form-field';
import {FormlyMatInputModule} from '../../input';
import {FormlyMatTextAreaModule} from '../../textarea';
import {FormlyMatRadioModule} from '../../radio';
import {FormlyMatCheckboxModule} from '../../checkbox';
import {FormlyMatMultiCheckboxModule} from '../../multicheckbox';
import {FormlyMatSelectModule} from '../../select';
import {TranslateModule} from '@ngx-translate/core';
import {FormlyMatToggleModule} from '../../toggle';
@NgModule({
imports: [
FormlyMatFormFieldModule,
FormlyMatInputModule,
FormlyMatTextAreaModule,
FormlyMatRadioModule,
FormlyMatCheckboxModule,
FormlyMatMultiCheckboxModule,
FormlyMatSelectModule,
FormlyMatToggleModule,
TranslateModule,
],
})
export class FormlyMaterialModule {
}

View file

@ -1,3 +0,0 @@
export {FieldType} from './../../form-field';
export {FormlyMaterialModule} from './ui-material.module';

View file

@ -1,5 +0,0 @@
/*
* Public API Surface of material
*/
export * from './lib/ui-material';

View file

@ -1,19 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import {getTestBed} from '@angular/core/testing';
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting,} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context = require.context('../', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1,2 +0,0 @@
export {FormlyMatTextAreaModule} from './textarea.module';
export {FormlyFieldTextArea} from './textarea.type';

View file

@ -1,33 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {FormlyMatFormFieldModule} from '../../form-field';
import {MatInputModule} from '@angular/material/input';
import {FormlyFieldTextArea} from './textarea.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyFieldTextArea],
imports: [
CommonModule,
ReactiveFormsModule,
MatInputModule,
TranslateModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [
{
name: 'textarea',
component: FormlyFieldTextArea,
wrappers: ['form-field'],
},
],
}),
],
})
export class FormlyMatTextAreaModule {
}

View file

@ -1,30 +0,0 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {MatInput} from '@angular/material/input';
import {FieldType} from '../../form-field';
@Component({
selector: 'formly-field-mat-textarea',
template: `
<textarea matInput
[id]="id"
[readonly]="to.readonly"
[formControl]="formControl"
[errorStateMatcher]="errorStateMatcher"
[cols]="to.cols"
[rows]="to.rows"
[formlyAttributes]="field"
[placeholder]="to.placeholder|translate"
[tabindex]="to.tabindex || 0"
[readonly]="to.readonly">
</textarea>
`,
})
export class FormlyFieldTextArea extends FieldType implements OnInit {
@ViewChild(MatInput, {static: true}) formFieldControl!: MatInput;
defaultOptions = {
templateOptions: {
cols: 1,
rows: 1,
},
};
}

View file

@ -1 +0,0 @@
export * from './src/public_api';

View file

@ -1,3 +0,0 @@
{
"ngPackage": {}
}

View file

@ -1 +0,0 @@
export {FormlyMatToggleModule} from './toggle.module';

View file

@ -1,29 +0,0 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {ReactiveFormsModule} from '@angular/forms';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {FormlyMatFormFieldModule} from '../../form-field';
import {FormlyToggleTypeComponent} from './toggle.type';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
declarations: [FormlyToggleTypeComponent],
imports: [
CommonModule,
TranslateModule,
ReactiveFormsModule,
MatSlideToggleModule,
FormlyMatFormFieldModule,
FormlyModule.forChild({
types: [{
name: 'toggle',
component: FormlyToggleTypeComponent,
wrappers: ['form-field'],
}],
}),
],
})
export class FormlyMatToggleModule {
}

View file

@ -1,31 +0,0 @@
import {Component, ViewChild} from '@angular/core';
import {FieldType} from '../../form-field';
import {MatSlideToggle} from '@angular/material/slide-toggle';
@Component({
selector: 'formly-field-mat-toggle',
template: `
<mat-slide-toggle
[id]="id"
[formControl]="formControl"
[formlyAttributes]="field"
[tabindex]="to.tabindex || 0">
{{ to.label|translate }}
</mat-slide-toggle>
`,
})
export class FormlyToggleTypeComponent extends FieldType {
@ViewChild(MatSlideToggle, {static: true}) slideToggle!: MatSlideToggle;
defaultOptions = {
templateOptions: {
hideFieldUnderline: true,
floatLabel: 'always',
hideLabel: true,
},
};
onContainerClick(event: MouseEvent): void {
this.slideToggle.focus();
super.onContainerClick(event);
}
}

View file

@ -0,0 +1,44 @@
import {FormlyFieldConfig} from '@ngx-formly/core';
import {TranslateService} from '@ngx-translate/core';
import {of} from 'rxjs';
export class TranslateExtension {
constructor(
private translate: TranslateService,
) {
}
prePopulate(field: FormlyFieldConfig) {
const to = field.templateOptions || {};
field.expressionProperties = {
...(field.expressionProperties || {}),
...(to.label
? {'templateOptions.label': this.translate.stream(to.label)}
: {}),
...(to.placeholder
? {'templateOptions.placeholder': this.translate.stream(to.placeholder)}
: {}),
...(to.options && Array.isArray(to.options)
? {
// TODO better solution working with live changes
'templateOptions.options': of(to.options.map((opt) => {
return {
...opt,
label: this.translate.instant(opt.label),
};
})),
}
: {}),
};
}
}
export function registerTranslateExtension(translate: TranslateService) {
return {
extensions: [{
name: 'translate',
extension: new TranslateExtension(translate)
}],
};
}

View file

@ -1,4 +1,4 @@
import {NgModule} from '@angular/core';
import {ErrorHandler, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {InputDurationDirective} from './duration/input-duration.directive';
import {DurationFromStringPipe} from './duration/duration-from-string.pipe';
@ -31,7 +31,7 @@ import {MatToolbarModule} from '@angular/material/toolbar';
import {MatTooltipModule} from '@angular/material/tooltip';
import {MarkdownModule, MarkdownService} from 'ngx-markdown';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {FormlyModule} from '@ngx-formly/core';
import {FORMLY_CONFIG, FormlyModule} from '@ngx-formly/core';
import {ThemeSelectComponent} from './theme-select/theme-select.component';
import {MsToStringPipe} from './duration/ms-to-string.pipe';
import {StringToMsPipe} from './duration/string-to-ms.pipe';
@ -60,8 +60,12 @@ import {ChipListInputComponent} from './chip-list-input/chip-list-input.componen
import {ValidationModule} from './validation/validation.module';
import {OwlDateTimeModule, OwlNativeDateTimeModule} from 'ng-pick-datetime';
import {FullPageSpinnerComponent} from './full-page-spinner/full-page-spinner.component';
import {TranslateModule} from '@ngx-translate/core';
import {FormlyMaterialModule} from './formly-material-components/src/lib/ui-material';
import {TranslateModule, TranslateService} from '@ngx-translate/core';
import {FormlyMaterialModule} from '@ngx-formly/material';
import {GlobalErrorHandler} from '../core/error-handler/global-error-handler.class';
import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {MyHammerConfig} from '../../hammer-config.class';
import {registerTranslateExtension} from './formly-translate-extension/formly-translate-extension';
@NgModule({
@ -222,7 +226,17 @@ import {FormlyMaterialModule} from './formly-material-components/src/lib/ui-mate
MarkdownModule,
ValidationModule,
TranslateModule,
]
],
providers: [
{provide: ErrorHandler, useClass: GlobalErrorHandler},
{provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig},
{
provide: FORMLY_CONFIG,
multi: true,
useFactory: registerTranslateExtension,
deps: [TranslateService],
},
],
})
export class UiModule {
constructor(private _markdownService: MarkdownService) {

View file

@ -458,6 +458,13 @@
dependencies:
tslib "^1.7.1"
"@ngx-formly/material@^5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@ngx-formly/material/-/material-5.2.0.tgz#dfbe8109f1af0f63221169bffabcf4e6bce90f18"
integrity sha512-RQajdWJz5fGmyRWzdcr3z3rGJF3X1xgqY9305Bw/vJyckTRfuT7yD4bZNna01HkeADafCVipnC2EogtdETNkoQ==
dependencies:
tslib "^1.9.0"
"@ngx-translate/core@^11.0.1":
version "11.0.1"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-11.0.1.tgz#cecefad41f06368f5859dac48fec8fcc4485615f"