updated with the latest changes

This commit is contained in:
Giuseppe Digilio
2018-06-11 11:13:05 +02:00
parent 10c739c903
commit 5ec01031d9
23 changed files with 375 additions and 242 deletions

View File

@@ -127,8 +127,8 @@
[showWeekdays]="getAdditional('showWeekdays', true)"
[showWeekNumbers]="getAdditional('showWeekNumbers', false)"
[startDate]="model.focusedDate"
(dateSelect)="onValueChange($event)"
(blur)="onBlur($event)"
(change)="onValueChange($event)"
(focus)="onFocus($event)">
<div class="input-group-append">

View File

@@ -12,7 +12,6 @@ import {
import { FormGroup } from '@angular/forms';
import {
DynamicDatePickerModel,
DynamicFormArrayGroupModel,
DynamicFormControlComponent,
DynamicFormControlEvent,
DynamicFormControlModel,
@@ -172,5 +171,4 @@ export class DsDynamicFormControlComponent extends DynamicFormControlComponent i
this.onValueChange(event);
}
}
}

View File

@@ -1,6 +1,6 @@
<div class="d-flex">
<div class="mr-3">
<ds-number-picker
tabindex="1"
[disabled]="model.disabled"
[min]="minYear"
[max]="maxYear"
@@ -8,14 +8,14 @@
[size]="4"
[(ngModel)]="initialYear"
[value]="year"
[invalid]="invalid"
[invalid]="showErrorMessages"
[placeholder]='yearPlaceholder'
(change)="onChange($event)"
(focus)="onFocus($event)"
></ds-number-picker>
</div>
<div class="mr-3">
<ds-number-picker
tabindex="2"
[min]="minMonth"
[max]="maxMonth"
[name]="'month'"
@@ -25,11 +25,11 @@
[placeholder]="monthPlaceholder"
[disabled]="!year || model.disabled"
(change)="onChange($event)"
(focus)="onFocus($event)"
></ds-number-picker>
</div>
<div class="mr-3">
<ds-number-picker
tabindex="3"
[min]="minDay"
[max]="maxDay"
[name]="'day'"
@@ -39,8 +39,8 @@
[placeholder]="dayPlaceholder"
[disabled]="!month || model.disabled"
(change)="onChange($event)"
(focus)="onFocus($event)"
></ds-number-picker>
</div>
</div>

View File

@@ -1,6 +1,7 @@
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { DynamicDsDatePickerModel } from './date-picker.model';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
export const DS_DATE_PICKER_SEPARATOR = '-';
@@ -20,12 +21,10 @@ export class DsDatePickerComponent implements OnInit {
// @Input()
// maxDate;
@Output()
selected = new EventEmitter<number>();
@Output()
remove = new EventEmitter<number>();
@Output()
change = new EventEmitter<any>();
@Output() selected = new EventEmitter<number>();
@Output() remove = new EventEmitter<number>();
@Output() change = new EventEmitter<any>();
@Output() focus = new EventEmitter<any>();
initialYear: number;
initialMonth: number;
@@ -48,9 +47,8 @@ export class DsDatePickerComponent implements OnInit {
disabledMonth = true;
disabledDay = true;
invalid = false;
ngOnInit() {// TODO Manage fields when not setted
ngOnInit() {
const now = new Date();
this.initialYear = now.getFullYear();
this.initialMonth = now.getMonth() + 1;
@@ -76,15 +74,6 @@ export class DsDatePickerComponent implements OnInit {
this.maxYear = this.initialYear + 100;
// Invalid state for year
this.group.get(this.model.id).statusChanges.subscribe((state) => {
if (state === 'INVALID' || this.model.malformedDate) {
this.invalid = true;
} else {
this.invalid = false;
this.model.malformedDate = false;
}
});
}
onChange(event) {
@@ -157,8 +146,13 @@ export class DsDatePickerComponent implements OnInit {
: this.day.toString();
value += DS_DATE_PICKER_SEPARATOR + dd;
}
this.model.valueUpdates.next(value);
this.change.emit(event);
this.change.emit(value);
}
onFocus(event) {
this.focus.emit(event);
}
getLastDay(date: Date) {

View File

@@ -18,10 +18,11 @@
<div class="pt-2" [ngClass]="{'border-top': !invalid, 'border border-danger': invalid}">
<div *ngIf="!(formCollapsed | async)" class="pl-2 row" @shrinkInOut>
<ds-form #formRef="formComponent"
class="col-sm-9 pl-0"
class="col-sm-12 col-md-8 col-lg-9 col-xl-10 pl-0"
[formId]="formId"
[formModel]="formModel"
[displaySubmit]="false"></ds-form>
[displaySubmit]="false"
(dfChange)="onChange($event)"></ds-form>
<div *ngIf="!(formCollapsed | async)" class="col p-0 m-0 d-flex justify-content-center align-items-center">
@@ -54,7 +55,11 @@
<div class="d-flex">
<div *ngIf="!chips.hasItems()">
<input type="text" readonly class="border-0 form-control-plaintext tag-input mt-1 mb-1 pl-2 text-muted" value="{{'form.no-value' | translate}}">
<input type="text"
class="border-0 form-control-plaintext tag-input mt-1 mb-1 pl-2 text-muted"
readonly
tabindex="-1"
value="{{'form.no-value' | translate}}">
</div>
<ds-chips
*ngIf="chips.hasItems()"

View File

@@ -11,7 +11,12 @@ import {
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel } from '@ng-dynamic-forms/core';
import {
DynamicFormControlEvent,
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicInputModel
} from '@ng-dynamic-forms/core';
import { isEqual } from 'lodash';
import { DynamicGroupModel, PLACEHOLDER_PARENT_METADATA } from './dynamic-group.model';
@@ -28,6 +33,9 @@ import { GlobalConfig } from '../../../../../../../config/global-config.interfac
import { GLOBAL_CONFIG } from '../../../../../../../config';
import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { isObjectEmpty } from '../../../../../object.util';
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model';
@Component({
selector: 'ds-dynamic-group',
@@ -67,9 +75,13 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
if (isNotEmpty(this.model.value)) {
this.formCollapsed = Observable.of(true);
}
this.model.valueUpdates.subscribe((value: any[]) => {
this.formCollapsed = (isNotEmpty(value) && !(value.length === 1 && isObjectEmpty(value[0]))) ? Observable.of(true) : Observable.of(false);
});
this.formId = this.formService.getUniqueId(this.model.id);
this.formModel = this.formBuilderService.modelFromConfiguration(config, this.model.scopeUUID, {});
this.chips = new Chips(this.model.value, 'value', this.model.mandatoryField);
this.chips = new Chips(this.model.value, 'value', this.model.mandatoryField, this.EnvConfig.submission.metadata.icons);
this.subs.push(
this.chips.chipsItems
.subscribe((subItems: any[]) => {
@@ -118,20 +130,32 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
return res;
}
onChange(event: DynamicFormControlEvent) {
return
}
onChipSelected(event) {
this.expandForm();
this.selectedChipItem = this.chips.getChipByIndex(event);
this.formModel.forEach((row) => {
const modelRow = row as DynamicFormGroupModel;
modelRow.group.forEach((model: DynamicInputModel) => {
const value = this.selectedChipItem.item[model.name] === PLACEHOLDER_PARENT_METADATA ? null : this.selectedChipItem.item[model.name];
if (model instanceof DynamicLookupModel) {
(model as DynamicLookupModel).valueUpdates.next(value);
} else if (model instanceof DynamicInputModel) {
model.valueUpdates.next(value);
const value = (this.selectedChipItem.item[model.name] === PLACEHOLDER_PARENT_METADATA
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
? null
: this.selectedChipItem.item[model.name];
if (value instanceof FormFieldMetadataValueObject || value instanceof AuthorityValueModel) {
model.valueUpdates.next(value.display);
} else {
(model as any).value = value;
model.valueUpdates.next(value);
}
// if (model instanceof DynamicLookupModel) {
// (model as DynamicLookupModel).valueUpdates.next(value);
// } else if (model instanceof DynamicInputModel) {
// model.valueUpdates.next(value);
// } else {
// (model as any).value = value;
// }
});
});

View File

@@ -6,12 +6,11 @@ import { AuthorityService } from '../../../../../../core/integration/authority.s
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model';
import { ConfigData } from '../../../../../../core/config/config-data';
import { ConfigAuthorityModel } from '../../../../../../core/shared/config/config-authority.model';
import { FormBuilderService } from '../../../form-builder.service';
import { DynamicCheckboxModel } from '@ng-dynamic-forms/core';
import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model';
import { DynamicListRadioGroupModel } from './dynamic-list-radio-group.model';
import { IntegrationData } from '../../../../../../core/integration/integration-data';
export interface ListItem {
id: string,
@@ -26,7 +25,6 @@ export interface ListItem {
templateUrl: './dynamic-list.component.html'
})
// TODO Fare questo componente da zero
export class DsDynamicListComponent implements OnInit {
@Input() bindId = true;
@Input() group: FormGroup;
@@ -91,13 +89,13 @@ export class DsDynamicListComponent implements OnInit {
protected setOptionsFromAuthority() {
if (this.model.authorityOptions.name && this.model.authorityOptions.name.length > 0) {
const listGroup = this.group.controls[this.model.id] as FormGroup;
this.authorityService.getEntriesByName(this.searchOptions).subscribe((authorities: ConfigData) => {
this.authorityService.getEntriesByName(this.searchOptions).subscribe((authorities: IntegrationData) => {
let groupCounter = 0;
let itemsPerGroup = 0;
let tempList: ListItem[] = [];
this.authorityList = authorities.payload as ConfigAuthorityModel[];
this.authorityList = authorities.payload as AuthorityValueModel[];
// Make a list of available options (checkbox/radio) and split in groups of 'model.groupLength'
(authorities.payload as ConfigAuthorityModel[]).forEach((option, key) => {
(authorities.payload as AuthorityValueModel[]).forEach((option, key) => {
const value = option.id || option.value;
const checked: boolean = isNotEmpty(findKey(
this.model.value,

View File

@@ -1,7 +1,6 @@
<div #sdRef="ngbDropdown"
ngbDropdown
<div ngbDropdown #sdRef="ngbDropdown"
(click)="$event.stopPropagation();"
(openChange)="openChange($event)">
(openChange)="openChange($event);">
<!--Simple lookup, only 1 field -->
<div class="form-row" *ngIf="!isLookupName">
@@ -19,23 +18,22 @@
[placeholder]="model.placeholder"
[readonly]="model.readOnly"
(change)="$event.preventDefault()"
(blur)="onBlurEvent($event); sdRef.close();"
(focus)="onFocusEvent($event); sdRef.close();"
(click)="$event.stopPropagation(); sdRef.close();"
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();"
(input)="onInput($event)">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-4 col-md-2 col-lg-1 text-center">
<button
<button ngbDropdownAnchor
*ngIf="!isInputDisabled()" class="btn btn-secondary"
type="button"
[disabled]="model.readOnly || isSearchDisabled()"
(click)="search($event); sdRef.open();">{{'form.search' | translate}}
(click)="sdRef.open(); search(); $event.stopPropagation();">{{'form.search' | translate}}
</button>
<button
*ngIf="isInputDisabled()" class="btn btn-secondary"
<button *ngIf="isInputDisabled()" class="btn btn-secondary"
type="button"
[disabled]="model.readOnly"
(click)="remove($event)">{{'form.remove' | translate}}
@@ -59,9 +57,9 @@
[placeholder]="model.placeholder | translate"
[readonly]="model.readOnly"
(change)="$event.preventDefault()"
(blur)="onBlurEvent($event); sdRef.close();"
(focus)="onFocusEvent($event); sdRef.close();"
(click)="$event.stopPropagation(); sdRef.close();"
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();"
(input)="onInput($event)">
</div>
@@ -78,22 +76,21 @@
[placeholder]="model.placeholder2 | translate"
[readonly]="model.readOnly"
(change)="$event.preventDefault()"
(blur)="onBlurEvent($event); sdRef.close();"
(focus)="onFocusEvent($event); sdRef.close();"
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
(click)="$event.stopPropagation(); sdRef.close();"
(input)="onInput($event)">
</div>
</div>
</div>
<div class="col-xs-12 col-md-3 col-lg-2 text-center">
<button
<button ngbDropdownAnchor
*ngIf="!isInputDisabled()" class="btn btn-secondary"
type="button"
[disabled]="isSearchDisabled()"
(click)="search($event); sdRef.open();">{{'form.search' | translate}}
(click)="sdRef.open(); search(); $event.stopPropagation();">{{'form.search' | translate}}
</button>
<button
*ngIf="isInputDisabled()" class="btn btn-secondary"
<button *ngIf="isInputDisabled()" class="btn btn-secondary"
type="button"
(click)="remove($event)">{{'form.remove' | translate}}
</button>
@@ -115,11 +112,11 @@
<button class="dropdown-item disabled"
*ngIf="optionsList && optionsList.length == 0"
(click)="clearFields(); sdRef.close();">{{'form.no-results' | translate}}
(click)="$event.stopPropagation(); clearFields(); sdRef.close();">{{'form.no-results' | translate}}
</button>
<button class="dropdown-item collection-item"
*ngFor="let listEntry of optionsList"
(click)="onSelect(listEntry); sdRef.close();"
(click)="$event.stopPropagation(); onSelect(listEntry); sdRef.close();"
title="{{ listEntry.display }}">
{{listEntry.value}}
</button>

View File

@@ -1,5 +1,9 @@
@import "../../../../../../../styles/variables";
.dropdown-toggle::after {
display:none
}
/* enable absolute positioning */
.spinner-addon {
position: relative;

View File

@@ -30,11 +30,11 @@
(scrolled)="onScroll()"
[scrollWindow]="false">
<button class="dropdown-item disabled" *ngIf="optionsList && optionsList.length == 0">No results found</button>
<button class="dropdown-item disabled" *ngIf="optionsList && optionsList.length == 0">{{'form.no-results' | translate}}</button>
<button class="dropdown-item collection-item" *ngFor="let listEntry of optionsList" (click)="onSelect(listEntry)" title="{{ listEntry.display }}">
{{inputFormatter(listEntry)}}
</button>
<div class="scrollable-dropdown-loading text-center" *ngIf="loading"><p>Loading...</p></div>
<div class="scrollable-dropdown-loading text-center" *ngIf="loading"><p>{{'form.loading' | translate}}</p></div>
</div>
</div>

View File

@@ -30,6 +30,7 @@ export class DsDynamicTypeaheadComponent implements OnInit {
searchFailed = false;
hideSearchingWhenUnsubscribed = new Observable(() => () => this.changeSearchingStatus(false));
currentValue: any;
inputValue: any;
formatter = (x: { display: string }) => {
return (typeof x === 'object') ? x.display : x
@@ -88,9 +89,8 @@ export class DsDynamicTypeaheadComponent implements OnInit {
onInput(event) {
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
const valueObj = new FormFieldMetadataValueObject(event.target.value);
this.currentValue = valueObj;
this.model.valueUpdates.next(valueObj as any);
this.change.emit(valueObj);
this.inputValue = valueObj;
this.model.valueUpdates.next(this.inputValue);
}
if (event.data) {
// this.group.markAsDirty();
@@ -98,6 +98,10 @@ export class DsDynamicTypeaheadComponent implements OnInit {
}
onBlurEvent(event: Event) {
if (!this.model.authorityOptions.closed && isNotEmpty(this.inputValue)) {
this.change.emit(this.inputValue);
this.inputValue = null;
}
this.blur.emit(event);
}
@@ -114,6 +118,7 @@ export class DsDynamicTypeaheadComponent implements OnInit {
}
onSelectItem(event: NgbTypeaheadSelectItemEvent) {
this.inputValue = null;
this.currentValue = event.item;
this.model.valueUpdates.next(event.item);
this.change.emit(event.item);

View File

@@ -10,20 +10,26 @@ import {
DynamicPathable,
JSONUtils,
} from '@ng-dynamic-forms/core';
import { mergeWith } from 'lodash';
import { mergeWith, isObject } from 'lodash';
import { isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util';
import { DynamicComboboxModel } from './ds-dynamic-form-ui/models/ds-dynamic-combobox.model';
import { SubmissionFormsModel } from '../../../core/shared/config/config-submission-forms.model';
import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model';
import { DynamicListCheckboxGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model';
import { DynamicGroupModel } from './ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model';
import { DynamicTagModel } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model';
import {
DYNAMIC_FORM_CONTROL_TYPE_RELATION,
DynamicGroupModel
} from './ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model';
import { DYNAMIC_FORM_CONTROL_TYPE_TAG, DynamicTagModel } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model';
import { DynamicListRadioGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model';
import { RowParser } from './parsers/row-parser';
import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model';
import { DynamicRowGroupModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-group-model';
import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model';
import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model';
import { AuthorityValueModel } from '../../../core/integration/models/authority-value.model';
@Injectable()
export class FormBuilderService extends DynamicFormService {
@@ -95,11 +101,25 @@ export class FormBuilderService extends DynamicFormService {
}
};
const iterateControlModels = (findGroupModel: DynamicFormControlModel[]): void => {
const normalizeValue = (controlModel, controlValue, controlModelIndex) => {
const controlLanguage = (controlModel as DsDynamicInputModel).hasLanguages ? (controlModel as DsDynamicInputModel).language : null;
if (((isObject(controlValue) && controlValue.id) || controlValue instanceof AuthorityValueModel)) {
return new FormFieldMetadataValueObject(controlValue.value, controlLanguage, controlValue.id, controlValue.display, controlModelIndex);
} else if (!(controlValue instanceof FormFieldMetadataValueObject)) {
return new FormFieldMetadataValueObject(controlValue, controlLanguage, null, null, controlModelIndex);
} else {
const place = controlModelIndex || controlValue.place;
return Object.assign(new FormFieldMetadataValueObject(), controlValue, {place});
}
};
const iterateControlModels = (findGroupModel: DynamicFormControlModel[], controlModelIndex: number = 0): void => {
let iterateResult = Object.create({});
// Iterate over all group's controls
for (const controlModel of findGroupModel) {
/* tslint:disable-next-line:no-shadowed-variable */
// for (const {controlModel, controlModelIndex} of findGroupModel.map((controlModel, controlModelIndex) => ({ controlModel, controlModelIndex }))) {
if (controlModel instanceof DynamicRowGroupModel && !this.isCustomGroup(controlModel)) {
iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group), customizer);
@@ -113,7 +133,7 @@ export class FormBuilderService extends DynamicFormService {
if (controlModel instanceof DynamicRowArrayModel) {
for (const arrayItemModel of controlModel.groups) {
iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group), customizer);
iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index), customizer);
}
continue;
}
@@ -121,7 +141,7 @@ export class FormBuilderService extends DynamicFormService {
if (controlModel instanceof DynamicFormArrayModel) {
iterateResult[controlModel.name] = [];
for (const arrayItemModel of controlModel.groups) {
iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group));
iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index));
}
continue;
}
@@ -135,12 +155,37 @@ export class FormBuilderService extends DynamicFormService {
controlId = controlModel.name;
}
const controlValue = isNotUndefined((controlModel as any).value) ? (controlModel as any).value : null;
if (controlId && iterateResult.hasOwnProperty(controlId) && isNotNull(iterateResult[controlId])) {
iterateResult[controlId].push(controlValue);
if (controlModel instanceof DynamicGroupModel) {
const values = (controlModel as any).value;
values.forEach((groupValue, groupIndex) => {
const newGroupValue = Object.create({});
Object.keys(groupValue)
.forEach((key) => {
const normValue = normalizeValue(controlModel, groupValue[key], groupIndex);
if (iterateResult.hasOwnProperty(key)) {
iterateResult[key].push(normValue);
} else {
iterateResult[controlId] = isNotEmpty(controlValue) ? (Array.isArray(controlValue) ? controlValue : [controlValue]) : null;
iterateResult[key] = [normValue];
}
// newGroupValue[key] = normalizeValue(controlModel, groupValue[key], groupIndex);
});
// controlArrayValue.push(newGroupValue);
})
} else if (isNotUndefined((controlModel as any).value) && isNotEmpty((controlModel as any).value)) {
const controlArrayValue = [];
// Normalize control value as an array of FormFieldMetadataValueObject
const values = Array.isArray((controlModel as any).value) ? (controlModel as any).value : [(controlModel as any).value];
values.forEach((controlValue) => {
controlArrayValue.push(normalizeValue(controlModel, controlValue, controlModelIndex))
});
if (controlId && iterateResult.hasOwnProperty(controlId) && isNotNull(iterateResult[controlId])) {
iterateResult[controlId] = iterateResult[controlId].concat(controlArrayValue);
} else {
iterateResult[controlId] = isNotEmpty(controlArrayValue) ? controlArrayValue : null;
}
}
}
return iterateResult;
@@ -182,16 +227,29 @@ export class FormBuilderService extends DynamicFormService {
|| model.parent instanceof DynamicGroupModel);
}
isComboboxGroup(model: DynamicFormControlModel) {
return model && model instanceof DynamicComboboxModel;
}
isCustomGroup(model: DynamicFormControlModel) {
return model &&
(model instanceof DynamicConcatModel
|| model instanceof DynamicComboboxModel
|| model instanceof DynamicListCheckboxGroupModel
|| this.isListGroup(model));
}
isListGroup(model: DynamicFormControlModel) {
return model &&
(model instanceof DynamicListCheckboxGroupModel
|| model instanceof DynamicListRadioGroupModel);
}
isModelInAuthorityGroup(model: DynamicFormControlModel) {
return (model instanceof DynamicListCheckboxGroupModel || model instanceof DynamicTagModel);
return model && (this.isListGroup(model) || model.type === DYNAMIC_FORM_CONTROL_TYPE_TAG);
}
isRelationGroup(model: DynamicFormControlModel) {
return model && model.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION;
}
getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0) {

View File

@@ -2,7 +2,7 @@ import { isNotEmpty } from '../../../empty.util';
export class FormFieldMetadataValueObject {
metadata?: string;
value: string;
value: any;
display: string;
language: any;
authority: string;
@@ -11,14 +11,14 @@ export class FormFieldMetadataValueObject {
closed: boolean;
label: string;
constructor(value: string,
constructor(value: any = null,
language: any = null,
authority: string = null,
display: string = null,
place: number = 0,
confidence: number = -1,
place: number = -1,
metadata: string = null) {
this.value = value;
this.value = isNotNull(value) ? ((typeof value === 'string') ? value.trim() : value) : null;
this.language = language;
this.authority = authority;
this.display = display || value;
@@ -39,4 +39,8 @@ export class FormFieldMetadataValueObject {
hasAuthority(): boolean {
return isNotEmpty(this.authority);
}
hasValue(): boolean {
return isNotEmpty(this.value);
}
}

View File

@@ -4,20 +4,19 @@ import { FormFieldModel } from '../models/form-field.model';
import { DynamicDsDatePickerModel } from '../ds-dynamic-form-ui/models/date-picker/date-picker.model';
import { isNotEmpty } from '../../../empty.util';
import { DS_DATE_PICKER_SEPARATOR } from '../ds-dynamic-form-ui/models/date-picker/date-picker.component';
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
export class DateFieldParser extends FieldParser {
public modelFactory(): any {
public modelFactory(fieldValue: FormFieldMetadataValueObject): any {
const inputDateModelConfig: DynamicDatePickerModelConfig = this.initModel();
inputDateModelConfig.toggleIcon = 'fa fa-calendar';
const dateModel = new DynamicDsDatePickerModel(inputDateModelConfig);
this.setValues(inputDateModelConfig as any, fieldValue);
// Init Data and validity check
if (isNotEmpty(this.getInitFieldValue())) {
if (isNotEmpty(inputDateModelConfig.value)) {
let malformedData = false;
const value = this.getInitFieldValue().toString();
const value = inputDateModelConfig.value.toString();
if (value.length >= 4) {
const valuesArray = value.split(DS_DATE_PICKER_SEPARATOR);
if (valuesArray.length < 4) {
@@ -29,12 +28,8 @@ export class DateFieldParser extends FieldParser {
}
}
if (!malformedData) {
dateModel.valueUpdates.next(this.getInitFieldValue());
} else {
if (malformedData) {
// TODO Set error message
dateModel.malformedDate = true;
// TODO
// const errorMessage = 'The stored date is not compliant';
// dateModel.validators = Object.assign({}, dateModel.validators, {malformedDate: null});
// dateModel.errorMessages = Object.assign({}, dateModel.errorMessages, {malformedDate: errorMessage});
@@ -44,6 +39,7 @@ export class DateFieldParser extends FieldParser {
}
}
const dateModel = new DynamicDsDatePickerModel(inputDateModelConfig);
return dateModel;
}
}

View File

@@ -118,9 +118,9 @@ export abstract class FieldParser {
const values: FormFieldMetadataValueObject[] = [];
fieldIds.forEach((id) => {
if (this.initFormValues.hasOwnProperty(id)) {
const valueObj: FormFieldMetadataValueObject = Object.create({});
const valueObj: FormFieldMetadataValueObject = Object.assign(new FormFieldMetadataValueObject(), this.initFormValues[id][innerIndex]);
valueObj.metadata = id;
valueObj.value = this.initFormValues[id][innerIndex];
// valueObj.value = this.initFormValues[id][innerIndex];
values.push(valueObj);
}
});
@@ -243,16 +243,21 @@ export abstract class FieldParser {
if (typeof fieldValue === 'object') {
modelConfig.language = fieldValue.language;
if (hasValue(fieldValue.language)) {
// Instance of FormFieldLanguageValueObject
modelConfig.value = fieldValue.value;
} else if (hasValue(fieldValue.metadata)) {
// Is a combobox field's value
modelConfig.value = fieldValue.value;
} else {
// Instance of FormFieldMetadataValueObject
if (forceValueAsObj) {
modelConfig.value = fieldValue;
} else {
modelConfig.value = fieldValue.value;
}
// if (hasValue(fieldValue.language)) {
// // Instance of FormFieldLanguageValueObject
// modelConfig.value = fieldValue.value;
// } else if (hasValue(fieldValue.metadata)) {
// // Is a combobox field's value
// modelConfig.value = fieldValue.value;
// } else {
// // Instance of FormFieldMetadataValueObject
// modelConfig.value = fieldValue;
// }
} else {
if (forceValueAsObj) {
// If value isn't an instance of FormFieldMetadataValueObject instantiate it

View File

@@ -68,7 +68,6 @@ export class OneboxFieldParser extends FieldParser {
if (isNotEmpty(fieldValue)) {
selectModelConfig.value = fieldValue.metadata;
}
selectModelConfig.disabled = true;
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + COMBOBOX_VALUE_SUFFIX, true, true);

View File

@@ -114,6 +114,29 @@ export class FormAddError implements Action {
}
}
export class FormRemoveErrorAction implements Action {
type = FormActionTypes.FORM_REMOVE_ERROR;
payload: {
formId: string,
fieldId: string
};
constructor(formId: string, fieldId: string) {
this.payload = {formId, fieldId};
}
}
export class FormClearErrorsAction implements Action {
type = FormActionTypes.FORM_CLEAR_ERRORS;
payload: {
formId: string
};
constructor(formId: string) {
this.payload = {formId};
}
}
/* tslint:enable:max-classes-per-file */
/**
@@ -125,3 +148,5 @@ export type FormAction = FormInitAction
| FormRemoveAction
| FormStatusChangeAction
| FormAddError
| FormClearErrorsAction
| FormRemoveErrorAction

View File

@@ -10,7 +10,13 @@ import {
import { Store } from '@ngrx/store';
import { AppState } from '../../app.reducer';
import { FormChangeAction, FormInitAction, FormRemoveAction, FormStatusChangeAction } from './form.actions';
import {
FormChangeAction,
FormInitAction,
FormRemoveAction,
FormRemoveErrorAction,
FormStatusChangeAction
} from './form.actions';
import { FormBuilderService } from './builder/form-builder.service';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
@@ -144,7 +150,7 @@ export class FormComponent implements OnDestroy, OnInit {
.filter((formState: FormEntry) => !!formState && !isEmpty(formState.errors))
.map((formState) => formState.errors)
.distinctUntilChanged()
.delay(100) // this terrible delay is here to prevent the detection change error
// .delay(100) // this terrible delay is here to prevent the detection change error
.subscribe((errors: FormError[]) => {
const {formGroup, formModel} = this;
@@ -172,10 +178,10 @@ export class FormComponent implements OnDestroy, OnInit {
* Method provided by Angular. Invoked when the instance is destroyed
*/
ngOnDestroy() {
this.store.dispatch(new FormRemoveAction(this.formId));
this.subs
.filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe());
this.store.dispatch(new FormRemoveAction(this.formId));
}
/**
@@ -206,7 +212,6 @@ export class FormComponent implements OnDestroy, OnInit {
}
onChange(event) {
console.log(event, this.formGroup);
const action: FormChangeAction = new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel));
this.store.dispatch(action);
@@ -215,7 +220,10 @@ export class FormComponent implements OnDestroy, OnInit {
this.change.emit(event);
const control: FormControl = event.control;
control.setErrors(null);
// control.setErrors(null);
if (control.valid) {
this.store.dispatch(new FormRemoveErrorAction(this.formId, event.model.id));
}
}
/**

View File

@@ -1,9 +1,15 @@
import {
FormAction, FormActionTypes, FormAddError, FormChangeAction, FormInitAction, FormRemoveAction,
FormAction,
FormActionTypes,
FormAddError,
FormChangeAction, FormClearErrorsAction,
FormInitAction,
FormRemoveAction,
FormRemoveErrorAction,
FormStatusChangeAction
} from './form.actions';
import { hasValue } from '../empty.util';
import { uniqWith, isEqual } from 'lodash';
import { isEqual, uniqWith } from 'lodash';
export interface FormError {
message: string;
@@ -45,6 +51,14 @@ export function formReducer(state = initialState, action: FormAction): FormState
return addFormErrors(state, action as FormAddError)
}
case FormActionTypes.FORM_REMOVE_ERROR: {
return removeFormError(state, action as FormRemoveErrorAction)
}
case FormActionTypes.FORM_CLEAR_ERRORS: {
return clearsFormErrors(state, action as FormClearErrorsAction)
}
default: {
return state;
}
@@ -60,7 +74,7 @@ function addFormErrors(state: FormState, action: FormAddError) {
};
return Object.assign({}, state, {
[ formId ]: {
[formId]: {
data: state[formId].data,
valid: state[formId].valid,
errors: state[formId].errors ? uniqWith(state[formId].errors.concat(error), isEqual) : [].concat(error),
@@ -71,6 +85,31 @@ function addFormErrors(state: FormState, action: FormAddError) {
}
}
function removeFormError(state: FormState, action: FormRemoveErrorAction) {
const formId = action.payload.formId;
const fieldId = action.payload.fieldId;
if (hasValue(state[formId])) {
const errors = state[formId].errors.filter((error) => error.fieldId !== fieldId);
const newState = Object.assign({}, state);
newState[formId] = Object.assign({}, state[formId], {errors});
return newState;
} else {
return state;
}
}
function clearsFormErrors(state: FormState, action: FormClearErrorsAction) {
const formId = action.payload.formId;
if (hasValue(state[formId])) {
const errors = [];
const newState = Object.assign({}, state);
newState[formId] = Object.assign({}, state[formId], {errors});
return newState;
} else {
return state;
}
}
/**
* Init form state.
*
@@ -82,22 +121,18 @@ function addFormErrors(state: FormState, action: FormAddError) {
* the new state, with the form initialized.
*/
function initForm(state: FormState, action: FormInitAction): FormState {
if (!hasValue(state[ action.payload.formId ])) {
return Object.assign({}, state, {
[ action.payload.formId ]: {
const formState = {
data: action.payload.formData,
valid: action.payload.valid,
errors: []
}
};
if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, {
[action.payload.formId]: formState
});
} else {
const newState = Object.assign({}, state);
newState[ action.payload.formId ] = Object.assign({}, newState[ action.payload.formId ], {
data: action.payload.formData,
valid: action.payload.valid,
errors: []
}
);
newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], formState);
return newState;
}
}
@@ -113,18 +148,18 @@ function initForm(state: FormState, action: FormInitAction): FormState {
* the new state, with the data changed.
*/
function changeDataForm(state: FormState, action: FormChangeAction): FormState {
if (!hasValue(state[ action.payload.formId ])) {
if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, {
[ action.payload.formId ]: {
[action.payload.formId]: {
data: action.payload.formData,
valid: state[ action.payload.formId ].valid
valid: state[action.payload.formId].valid
}
});
} else {
const newState = Object.assign({}, state);
newState[ action.payload.formId ] = Object.assign({}, newState[ action.payload.formId ], {
newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], {
data: action.payload.formData,
valid: state[ action.payload.formId ].valid
valid: state[action.payload.formId].valid
}
);
return newState;
@@ -142,17 +177,17 @@ function changeDataForm(state: FormState, action: FormChangeAction): FormState {
* the new state, with the status changed.
*/
function changeStatusForm(state: FormState, action: FormStatusChangeAction): FormState {
if (!hasValue(state[ action.payload.formId ])) {
if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, {
[ action.payload.formId ]: {
data: state[ action.payload.formId ].data,
[action.payload.formId]: {
data: state[action.payload.formId].data,
valid: action.payload.valid
}
});
} else {
const newState = Object.assign({}, state);
newState[ action.payload.formId ] = Object.assign({}, newState[ action.payload.formId ], {
data: state[ action.payload.formId ].data,
newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], {
data: state[action.payload.formId].data,
valid: action.payload.valid
}
);
@@ -171,9 +206,9 @@ function changeStatusForm(state: FormState, action: FormStatusChangeAction): For
* the new state, with the form initialized.
*/
function removeForm(state: FormState, action: FormRemoveAction): FormState {
if (hasValue(state[ action.payload.formId ])) {
if (hasValue(state[action.payload.formId])) {
const newState = Object.assign({}, state);
delete newState[ action.payload.formId ];
delete newState[action.payload.formId];
return newState;
} else {
return state;

View File

@@ -84,7 +84,7 @@ export class FormService {
error[errorKey] = message; // assign message
// if form control model has errorMessages object, create it
// if form control model has not errorMessages object, create it
if (!model.errorMessages) {
model.errorMessages = {};
}

View File

@@ -1,7 +1,8 @@
<div class="d-flex flex-column align-items-center justify-content-around">
<div class="d-flex flex-column align-items-center justify-content-around mr-3">
<button
type="button"
class="btn btn-link"
type="button"
tabindex="-1"
[disabled]="disabled"
(click)="increment()">
<span class="chevron"></span>
@@ -9,22 +10,23 @@
</button>
<input
type="text"
class="form-control"
class="form-control d-inline-block text-center"
maxlength="{{size}}"
size="{{size}}"
placeholder="{{placeholder}}"
[name]="name"
[(ngModel)]="value"
(change)="update($event)"
(focus)="onFocus()"
(change)="update($event); $event.stopPropagation();"
(focus)="onFocus($event); $event.stopPropagation();"
[readonly]="disabled"
[disabled]="disabled"
[ngClass]="{'is-invalid': invalid}"
aria-label="name"
>
<button
type="button"
class="btn btn-link"
type="button"
tabindex="-1"
[disabled]="disabled"
(click)="decrement()">
<span class="chevron bottom"></span>

View File

@@ -1,17 +1,7 @@
.ngb-tp {
display: flex;
align-items: center;
}
.ngb-tp-hour, .ngb-tp-minute, .ngb-tp-second, .ngb-tp-meridian {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}
.ngb-tp-spacer {
width: 1em;
text-align: center;
:host {
outline: none;
}
.chevron::before {
border-style: solid;
border-width: 0.29em 0.29em 0 0;
@@ -33,12 +23,7 @@
-ms-transform: rotate(135deg);
transform: rotate(135deg);
}
input {
text-align: center;
display: inline-block;
max-width: 80px !important;
}
//.error {
// border-color: red;
//}

View File

@@ -11,31 +11,22 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f
})
export class NumberPickerComponent implements OnInit, ControlValueAccessor {
@Output()
selected = new EventEmitter<number>();
@Output()
remove = new EventEmitter<number>();
@Output()
change = new EventEmitter<any>();
@Input()
step: number;
@Input()
min: number;
@Input()
max: number;
@Input()
size: number;
@Input()
placeholder: string;
@Input()
name: string;
@Input()
disabled: boolean;
@Input()
invalid: boolean;
@Input()
value: number;
@Input() step: number;
@Input() min: number;
@Input() max: number;
@Input() size: number;
@Input() placeholder: string;
@Input() name: string;
@Input() disabled: boolean;
@Input() invalid: boolean;
@Input() value: number;
@Output() selected = new EventEmitter<number>();
@Output() remove = new EventEmitter<number>();
@Output() change = new EventEmitter<any>();
@Output() focus = new EventEmitter<any>();
lastValue: number;
constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) {
@@ -109,19 +100,18 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {
this.value = null;
this.emitChange();
} else {
this.value = this.lastValue;
this.emitChange();
this.value = undefined;
}
} catch (e) {
this.value = this.lastValue;
this.emitChange();
this.value = undefined;
}
}
onFocus() {
onFocus(event) {
if (this.value) {
this.lastValue = this.value;
}
this.focus.emit(event);
}
writeValue(value) {
@@ -135,10 +125,11 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {
}
registerOnChange(fn) {
// this.change = fn;
return
}
registerOnTouched(fn) {
return
}
emitChange() {