+
@@ -20,6 +20,7 @@
+
+
+
+
0">
{{field.properties.hints}}
diff --git a/frontend/app/features/content/shared/forms/field-editor.component.scss b/frontend/app/features/content/shared/forms/field-editor.component.scss
index 907653b31..34f89b657 100644
--- a/frontend/app/features/content/shared/forms/field-editor.component.scss
+++ b/frontend/app/features/content/shared/forms/field-editor.component.scss
@@ -1,4 +1,8 @@
.field {
+ & {
+ position: relative;
+ }
+
&-required {
color: $color-theme-error;
}
@@ -10,6 +14,14 @@
}
}
+.unset {
+ @include absolute(-.5rem, 0);
+
+ i {
+ font-size: 80%;
+ }
+}
+
.ui {
& {
margin-bottom: 1rem;
diff --git a/frontend/app/features/content/shared/forms/field-editor.component.ts b/frontend/app/features/content/shared/forms/field-editor.component.ts
index 3b7b294fa..5c750e855 100644
--- a/frontend/app/features/content/shared/forms/field-editor.component.ts
+++ b/frontend/app/features/content/shared/forms/field-editor.component.ts
@@ -8,6 +8,8 @@
import { Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
import { AbstractControl, FormArray, FormControl } from '@angular/forms';
import { AbstractContentForm, AppLanguageDto, EditContentForm, FieldDto, MathHelper, RootFieldDto, Types } from '@app/shared';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
@Component({
selector: 'sqx-field-editor',
@@ -30,12 +32,17 @@ export class FieldEditorComponent implements OnChanges {
@Input()
public languages: ReadonlyArray
;
+ @Input()
+ public canUnset: boolean;
+
@Input()
public displaySuffix: string;
@ViewChild('editor', { static: false })
public editor: ElementRef;
+ public isEmpty: Observable;
+
public get field() {
return this.formModel.field;
}
@@ -55,10 +62,14 @@ export class FieldEditorComponent implements OnChanges {
public uniqueId = MathHelper.guid();
public ngOnChanges(changes: SimpleChanges) {
- const previousControl = changes['control']?.previousValue;
+ if (changes['formModel']) {
+ const previousControl: AbstractContentForm = changes['formModel'].previousValue;
- if (previousControl && Types.isFunction(previousControl['_clearChangeFns'])) {
- previousControl['_clearChangeFns']();
+ if (previousControl && Types.isFunction(previousControl.form['_clearChangeFns'])) {
+ previousControl.form['_clearChangeFns']();
+ }
+
+ this.isEmpty = this.formModel.form.valueChanges.pipe(map(x => Types.isUndefined(x) || Types.isNull(x)));
}
}
@@ -75,4 +86,8 @@ export class FieldEditorComponent implements OnChanges {
}
}
}
+
+ public unset() {
+ this.formModel.form.setValue(undefined);
+ }
}
\ No newline at end of file
diff --git a/frontend/app/framework/angular/forms/string-form-control.spec.ts b/frontend/app/framework/angular/forms/string-form-control.spec.ts
deleted file mode 100644
index de38a3436..000000000
--- a/frontend/app/framework/angular/forms/string-form-control.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Squidex Headless CMS
- *
- * @license
- * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
- */
-
-import { StringFormControl } from './string-form-control';
-
-describe('StringFormControl', () => {
- it('should convert empty string to undefined', () => {
- const formControl = new StringFormControl();
-
- formControl.setValue('');
-
- expect(formControl.value).toBeUndefined();
- });
-
- it('should convert empty string to undefined when patching', () => {
- const formControl = new StringFormControl();
-
- formControl.patchValue('');
-
- expect(formControl.value).toBeUndefined();
- });
-});
\ No newline at end of file
diff --git a/frontend/app/framework/angular/forms/string-form-control.ts b/frontend/app/framework/angular/forms/string-form-control.ts
deleted file mode 100644
index 1a0f14d25..000000000
--- a/frontend/app/framework/angular/forms/string-form-control.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Squidex Headless CMS
- *
- * @license
- * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
- */
-
-// tslint:disable: readonly-array
-
-import { AbstractControlOptions, AsyncValidatorFn, FormControl, ValidatorFn } from '@angular/forms';
-
-export type ValueOptions = {
- onlySelf?: boolean;
- emitEvent?: boolean;
- emitModelToViewChange?: boolean;
- emitViewToModelChange?: boolean;
-};
-
-export class StringFormControl extends FormControl {
- constructor(formState?: any, validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null) {
- super(formState, validatorOrOpts, asyncValidator);
- }
-
- public setValue(value: any, options?: ValueOptions) {
- if (value === '') {
- value = undefined;
- }
-
- super.setValue(value, options);
- }
-
- public patchValue(value: any, options?: ValueOptions) {
- if (value === '') {
- value = undefined;
- }
-
- super.patchValue(value, options);
- }
-}
\ No newline at end of file
diff --git a/frontend/app/framework/angular/modals/tooltip.directive.ts b/frontend/app/framework/angular/modals/tooltip.directive.ts
index 4caf9319c..beef85be5 100644
--- a/frontend/app/framework/angular/modals/tooltip.directive.ts
+++ b/frontend/app/framework/angular/modals/tooltip.directive.ts
@@ -47,6 +47,13 @@ export class TooltipDirective {
}
}
+ @HostListener('click')
+ public onClick() {
+ if (this.titleText) {
+ this.dialogs.tooltip(new Tooltip(this.element.nativeElement, null, this.titlePosition));
+ }
+ }
+
private unsetAttribute() {
try {
this.renderer.setProperty(this.element.nativeElement, 'title', '');
diff --git a/frontend/app/framework/declarations.ts b/frontend/app/framework/declarations.ts
index a58921d19..935714657 100644
--- a/frontend/app/framework/declarations.ts
+++ b/frontend/app/framework/declarations.ts
@@ -31,7 +31,6 @@ export * from './angular/forms/form-hint.component';
export * from './angular/forms/forms-helper';
export * from './angular/forms/indeterminate-value.directive';
export * from './angular/forms/progress-bar.component';
-export * from './angular/forms/string-form-control';
export * from './angular/forms/transform-input.directive';
export * from './angular/forms/validators';
export * from './angular/highlight.pipe';
diff --git a/frontend/app/shared/state/contents.forms.ts b/frontend/app/shared/state/contents.forms.ts
index e37a0faa4..3f04076b7 100644
--- a/frontend/app/shared/state/contents.forms.ts
+++ b/frontend/app/shared/state/contents.forms.ts
@@ -7,8 +7,8 @@
// tslint:disable: readonly-array
-import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { Form, StringFormControl, Types, valueAll$ } from '@app/framework';
+import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import { Form, Types, valueAll$ } from '@app/framework';
import { BehaviorSubject } from 'rxjs';
import { AppLanguageDto } from './../services/app-languages.service';
import { LanguageDto } from './../services/languages.service';
@@ -48,7 +48,7 @@ export class PatchContentForm extends Form {
for (const field of this.editableFields) {
const validators = FieldsValidators.create(field, this.language.isOptional);
- this.form.setControl(field.name, new StringFormControl(undefined, { updateOn: FieldUpdateOn.get(field), validators }));
+ this.form.setControl(field.name, new FormControl(undefined, { updateOn: FieldUpdateOn.get(field), validators }));
}
}
@@ -328,7 +328,7 @@ export class FieldForm extends AbstractContentForm {
}
}
-export class FieldValueForm extends AbstractContentForm {
+export class FieldValueForm extends AbstractContentForm {
constructor(field: RootFieldDto, isOptional: boolean
) {
super(field, FieldValueForm.buildControl(field, isOptional), isOptional);
@@ -339,7 +339,7 @@ export class FieldValueForm extends AbstractContentForm {
+export class FieldArrayItemValueForm extends AbstractContentForm {
private isRequired = false;
constructor(field: NestedFieldDto, parent: RootFieldDto, rules: CompiledRule[], isOptional: boolean, source?: FieldArrayItemForm
@@ -517,7 +517,7 @@ export class FieldArrayItemValueForm extends AbstractContentForm