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)" [showWeekdays]="getAdditional('showWeekdays', true)"
[showWeekNumbers]="getAdditional('showWeekNumbers', false)" [showWeekNumbers]="getAdditional('showWeekNumbers', false)"
[startDate]="model.focusedDate" [startDate]="model.focusedDate"
(dateSelect)="onValueChange($event)"
(blur)="onBlur($event)" (blur)="onBlur($event)"
(change)="onValueChange($event)"
(focus)="onFocus($event)"> (focus)="onFocus($event)">
<div class="input-group-append"> <div class="input-group-append">

View File

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

View File

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

View File

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

View File

@@ -18,10 +18,11 @@
<div class="pt-2" [ngClass]="{'border-top': !invalid, 'border border-danger': invalid}"> <div class="pt-2" [ngClass]="{'border-top': !invalid, 'border border-danger': invalid}">
<div *ngIf="!(formCollapsed | async)" class="pl-2 row" @shrinkInOut> <div *ngIf="!(formCollapsed | async)" class="pl-2 row" @shrinkInOut>
<ds-form #formRef="formComponent" <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" [formId]="formId"
[formModel]="formModel" [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"> <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 class="d-flex">
<div *ngIf="!chips.hasItems()"> <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> </div>
<ds-chips <ds-chips
*ngIf="chips.hasItems()" *ngIf="chips.hasItems()"

View File

@@ -11,7 +11,12 @@ import {
} from '@angular/core'; } from '@angular/core';
import { Observable } from 'rxjs/Observable'; 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 { isEqual } from 'lodash';
import { DynamicGroupModel, PLACEHOLDER_PARENT_METADATA } from './dynamic-group.model'; 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 { GLOBAL_CONFIG } from '../../../../../../../config';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription'; 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({ @Component({
selector: 'ds-dynamic-group', selector: 'ds-dynamic-group',
@@ -67,9 +75,13 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
if (isNotEmpty(this.model.value)) { if (isNotEmpty(this.model.value)) {
this.formCollapsed = Observable.of(true); 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.formId = this.formService.getUniqueId(this.model.id);
this.formModel = this.formBuilderService.modelFromConfiguration(config, this.model.scopeUUID, {}); 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.subs.push(
this.chips.chipsItems this.chips.chipsItems
.subscribe((subItems: any[]) => { .subscribe((subItems: any[]) => {
@@ -118,20 +130,32 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
return res; return res;
} }
onChange(event: DynamicFormControlEvent) {
return
}
onChipSelected(event) { onChipSelected(event) {
this.expandForm(); this.expandForm();
this.selectedChipItem = this.chips.getChipByIndex(event); this.selectedChipItem = this.chips.getChipByIndex(event);
this.formModel.forEach((row) => { this.formModel.forEach((row) => {
const modelRow = row as DynamicFormGroupModel; const modelRow = row as DynamicFormGroupModel;
modelRow.group.forEach((model: DynamicInputModel) => { modelRow.group.forEach((model: DynamicInputModel) => {
const value = this.selectedChipItem.item[model.name] === PLACEHOLDER_PARENT_METADATA ? null : this.selectedChipItem.item[model.name]; const value = (this.selectedChipItem.item[model.name] === PLACEHOLDER_PARENT_METADATA
if (model instanceof DynamicLookupModel) { || this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
(model as DynamicLookupModel).valueUpdates.next(value); ? null
} else if (model instanceof DynamicInputModel) { : this.selectedChipItem.item[model.name];
model.valueUpdates.next(value); if (value instanceof FormFieldMetadataValueObject || value instanceof AuthorityValueModel) {
model.valueUpdates.next(value.display);
} else { } 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 { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
import { hasValue, isNotEmpty } from '../../../../../empty.util'; import { hasValue, isNotEmpty } from '../../../../../empty.util';
import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model'; 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 { FormBuilderService } from '../../../form-builder.service';
import { DynamicCheckboxModel } from '@ng-dynamic-forms/core'; import { DynamicCheckboxModel } from '@ng-dynamic-forms/core';
import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model'; import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model';
import { DynamicListRadioGroupModel } from './dynamic-list-radio-group.model'; import { DynamicListRadioGroupModel } from './dynamic-list-radio-group.model';
import { IntegrationData } from '../../../../../../core/integration/integration-data';
export interface ListItem { export interface ListItem {
id: string, id: string,
@@ -26,7 +25,6 @@ export interface ListItem {
templateUrl: './dynamic-list.component.html' templateUrl: './dynamic-list.component.html'
}) })
// TODO Fare questo componente da zero
export class DsDynamicListComponent implements OnInit { export class DsDynamicListComponent implements OnInit {
@Input() bindId = true; @Input() bindId = true;
@Input() group: FormGroup; @Input() group: FormGroup;
@@ -91,13 +89,13 @@ export class DsDynamicListComponent implements OnInit {
protected setOptionsFromAuthority() { protected setOptionsFromAuthority() {
if (this.model.authorityOptions.name && this.model.authorityOptions.name.length > 0) { if (this.model.authorityOptions.name && this.model.authorityOptions.name.length > 0) {
const listGroup = this.group.controls[this.model.id] as FormGroup; 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 groupCounter = 0;
let itemsPerGroup = 0; let itemsPerGroup = 0;
let tempList: ListItem[] = []; 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' // 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 value = option.id || option.value;
const checked: boolean = isNotEmpty(findKey( const checked: boolean = isNotEmpty(findKey(
this.model.value, this.model.value,

View File

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

View File

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

View File

@@ -30,11 +30,11 @@
(scrolled)="onScroll()" (scrolled)="onScroll()"
[scrollWindow]="false"> [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 }}"> <button class="dropdown-item collection-item" *ngFor="let listEntry of optionsList" (click)="onSelect(listEntry)" title="{{ listEntry.display }}">
{{inputFormatter(listEntry)}} {{inputFormatter(listEntry)}}
</button> </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>
</div> </div>

View File

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

View File

@@ -10,20 +10,26 @@ import {
DynamicPathable, DynamicPathable,
JSONUtils, JSONUtils,
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { mergeWith } from 'lodash'; import { mergeWith, isObject } from 'lodash';
import { isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util'; import { isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util';
import { DynamicComboboxModel } from './ds-dynamic-form-ui/models/ds-dynamic-combobox.model'; import { DynamicComboboxModel } from './ds-dynamic-form-ui/models/ds-dynamic-combobox.model';
import { SubmissionFormsModel } from '../../../core/shared/config/config-submission-forms.model'; import { SubmissionFormsModel } from '../../../core/shared/config/config-submission-forms.model';
import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.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 { 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 {
import { DynamicTagModel } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model'; 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 { DynamicListRadioGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model';
import { RowParser } from './parsers/row-parser'; import { RowParser } from './parsers/row-parser';
import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; 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 { 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() @Injectable()
export class FormBuilderService extends DynamicFormService { 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({}); let iterateResult = Object.create({});
// Iterate over all group's controls // Iterate over all group's controls
for (const controlModel of findGroupModel) { 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)) { if (controlModel instanceof DynamicRowGroupModel && !this.isCustomGroup(controlModel)) {
iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group), customizer); iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group), customizer);
@@ -113,7 +133,7 @@ export class FormBuilderService extends DynamicFormService {
if (controlModel instanceof DynamicRowArrayModel) { if (controlModel instanceof DynamicRowArrayModel) {
for (const arrayItemModel of controlModel.groups) { for (const arrayItemModel of controlModel.groups) {
iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group), customizer); iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index), customizer);
} }
continue; continue;
} }
@@ -121,7 +141,7 @@ export class FormBuilderService extends DynamicFormService {
if (controlModel instanceof DynamicFormArrayModel) { if (controlModel instanceof DynamicFormArrayModel) {
iterateResult[controlModel.name] = []; iterateResult[controlModel.name] = [];
for (const arrayItemModel of controlModel.groups) { for (const arrayItemModel of controlModel.groups) {
iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group)); iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index));
} }
continue; continue;
} }
@@ -135,12 +155,37 @@ export class FormBuilderService extends DynamicFormService {
controlId = controlModel.name; controlId = controlModel.name;
} }
const controlValue = isNotUndefined((controlModel as any).value) ? (controlModel as any).value : null; if (controlModel instanceof DynamicGroupModel) {
if (controlId && iterateResult.hasOwnProperty(controlId) && isNotNull(iterateResult[controlId])) { const values = (controlModel as any).value;
iterateResult[controlId].push(controlValue); 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 { } 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; return iterateResult;
@@ -182,16 +227,29 @@ export class FormBuilderService extends DynamicFormService {
|| model.parent instanceof DynamicGroupModel); || model.parent instanceof DynamicGroupModel);
} }
isComboboxGroup(model: DynamicFormControlModel) {
return model && model instanceof DynamicComboboxModel;
}
isCustomGroup(model: DynamicFormControlModel) { isCustomGroup(model: DynamicFormControlModel) {
return model && return model &&
(model instanceof DynamicConcatModel (model instanceof DynamicConcatModel
|| model instanceof DynamicComboboxModel || model instanceof DynamicComboboxModel
|| model instanceof DynamicListCheckboxGroupModel || this.isListGroup(model));
}
isListGroup(model: DynamicFormControlModel) {
return model &&
(model instanceof DynamicListCheckboxGroupModel
|| model instanceof DynamicListRadioGroupModel); || model instanceof DynamicListRadioGroupModel);
} }
isModelInAuthorityGroup(model: DynamicFormControlModel) { 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) { getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0) {

View File

@@ -2,7 +2,7 @@ import { isNotEmpty } from '../../../empty.util';
export class FormFieldMetadataValueObject { export class FormFieldMetadataValueObject {
metadata?: string; metadata?: string;
value: string; value: any;
display: string; display: string;
language: any; language: any;
authority: string; authority: string;
@@ -11,14 +11,14 @@ export class FormFieldMetadataValueObject {
closed: boolean; closed: boolean;
label: string; label: string;
constructor(value: string, constructor(value: any = null,
language: any = null, language: any = null,
authority: string = null, authority: string = null,
display: string = null, display: string = null,
place: number = 0,
confidence: number = -1, confidence: number = -1,
place: number = -1,
metadata: string = null) { metadata: string = null) {
this.value = value; this.value = isNotNull(value) ? ((typeof value === 'string') ? value.trim() : value) : null;
this.language = language; this.language = language;
this.authority = authority; this.authority = authority;
this.display = display || value; this.display = display || value;
@@ -39,4 +39,8 @@ export class FormFieldMetadataValueObject {
hasAuthority(): boolean { hasAuthority(): boolean {
return isNotEmpty(this.authority); 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 { DynamicDsDatePickerModel } from '../ds-dynamic-form-ui/models/date-picker/date-picker.model';
import { isNotEmpty } from '../../../empty.util'; import { isNotEmpty } from '../../../empty.util';
import { DS_DATE_PICKER_SEPARATOR } from '../ds-dynamic-form-ui/models/date-picker/date-picker.component'; 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 { export class DateFieldParser extends FieldParser {
public modelFactory(): any { public modelFactory(fieldValue: FormFieldMetadataValueObject): any {
const inputDateModelConfig: DynamicDatePickerModelConfig = this.initModel(); const inputDateModelConfig: DynamicDatePickerModelConfig = this.initModel();
inputDateModelConfig.toggleIcon = 'fa fa-calendar'; inputDateModelConfig.toggleIcon = 'fa fa-calendar';
this.setValues(inputDateModelConfig as any, fieldValue);
const dateModel = new DynamicDsDatePickerModel(inputDateModelConfig);
// Init Data and validity check // Init Data and validity check
if (isNotEmpty(this.getInitFieldValue())) { if (isNotEmpty(inputDateModelConfig.value)) {
let malformedData = false; let malformedData = false;
const value = this.getInitFieldValue().toString(); const value = inputDateModelConfig.value.toString();
if (value.length >= 4) { if (value.length >= 4) {
const valuesArray = value.split(DS_DATE_PICKER_SEPARATOR); const valuesArray = value.split(DS_DATE_PICKER_SEPARATOR);
if (valuesArray.length < 4) { if (valuesArray.length < 4) {
@@ -29,12 +28,8 @@ export class DateFieldParser extends FieldParser {
} }
} }
if (!malformedData) { if (malformedData) {
dateModel.valueUpdates.next(this.getInitFieldValue());
} else {
// TODO Set error message // TODO Set error message
dateModel.malformedDate = true;
// TODO
// const errorMessage = 'The stored date is not compliant'; // const errorMessage = 'The stored date is not compliant';
// dateModel.validators = Object.assign({}, dateModel.validators, {malformedDate: null}); // dateModel.validators = Object.assign({}, dateModel.validators, {malformedDate: null});
// dateModel.errorMessages = Object.assign({}, dateModel.errorMessages, {malformedDate: errorMessage}); // dateModel.errorMessages = Object.assign({}, dateModel.errorMessages, {malformedDate: errorMessage});
@@ -44,6 +39,7 @@ export class DateFieldParser extends FieldParser {
} }
} }
const dateModel = new DynamicDsDatePickerModel(inputDateModelConfig);
return dateModel; return dateModel;
} }
} }

View File

@@ -118,9 +118,9 @@ export abstract class FieldParser {
const values: FormFieldMetadataValueObject[] = []; const values: FormFieldMetadataValueObject[] = [];
fieldIds.forEach((id) => { fieldIds.forEach((id) => {
if (this.initFormValues.hasOwnProperty(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.metadata = id;
valueObj.value = this.initFormValues[id][innerIndex]; // valueObj.value = this.initFormValues[id][innerIndex];
values.push(valueObj); values.push(valueObj);
} }
}); });
@@ -243,16 +243,21 @@ export abstract class FieldParser {
if (typeof fieldValue === 'object') { if (typeof fieldValue === 'object') {
modelConfig.language = fieldValue.language; modelConfig.language = fieldValue.language;
if (hasValue(fieldValue.language)) { if (forceValueAsObj) {
// 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; 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 { } else {
if (forceValueAsObj) { if (forceValueAsObj) {
// If value isn't an instance of FormFieldMetadataValueObject instantiate it // If value isn't an instance of FormFieldMetadataValueObject instantiate it

View File

@@ -68,7 +68,6 @@ export class OneboxFieldParser extends FieldParser {
if (isNotEmpty(fieldValue)) { if (isNotEmpty(fieldValue)) {
selectModelConfig.value = fieldValue.metadata; selectModelConfig.value = fieldValue.metadata;
} }
selectModelConfig.disabled = true;
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect)); inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + COMBOBOX_VALUE_SUFFIX, true, true); 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 */ /* tslint:enable:max-classes-per-file */
/** /**
@@ -125,3 +148,5 @@ export type FormAction = FormInitAction
| FormRemoveAction | FormRemoveAction
| FormStatusChangeAction | FormStatusChangeAction
| FormAddError | FormAddError
| FormClearErrorsAction
| FormRemoveErrorAction

View File

@@ -10,7 +10,13 @@ import {
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '../../app.reducer'; 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 { FormBuilderService } from './builder/form-builder.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs/Subscription';
@@ -144,7 +150,7 @@ export class FormComponent implements OnDestroy, OnInit {
.filter((formState: FormEntry) => !!formState && !isEmpty(formState.errors)) .filter((formState: FormEntry) => !!formState && !isEmpty(formState.errors))
.map((formState) => formState.errors) .map((formState) => formState.errors)
.distinctUntilChanged() .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[]) => { .subscribe((errors: FormError[]) => {
const {formGroup, formModel} = this; const {formGroup, formModel} = this;
@@ -172,10 +178,10 @@ export class FormComponent implements OnDestroy, OnInit {
* Method provided by Angular. Invoked when the instance is destroyed * Method provided by Angular. Invoked when the instance is destroyed
*/ */
ngOnDestroy() { ngOnDestroy() {
this.store.dispatch(new FormRemoveAction(this.formId));
this.subs this.subs
.filter((sub) => hasValue(sub)) .filter((sub) => hasValue(sub))
.forEach((sub) => sub.unsubscribe()); .forEach((sub) => sub.unsubscribe());
this.store.dispatch(new FormRemoveAction(this.formId));
} }
/** /**
@@ -206,7 +212,6 @@ export class FormComponent implements OnDestroy, OnInit {
} }
onChange(event) { onChange(event) {
console.log(event, this.formGroup);
const action: FormChangeAction = new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel)); const action: FormChangeAction = new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel));
this.store.dispatch(action); this.store.dispatch(action);
@@ -215,7 +220,10 @@ export class FormComponent implements OnDestroy, OnInit {
this.change.emit(event); this.change.emit(event);
const control: FormControl = event.control; 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 { import {
FormAction, FormActionTypes, FormAddError, FormChangeAction, FormInitAction, FormRemoveAction, FormAction,
FormActionTypes,
FormAddError,
FormChangeAction, FormClearErrorsAction,
FormInitAction,
FormRemoveAction,
FormRemoveErrorAction,
FormStatusChangeAction FormStatusChangeAction
} from './form.actions'; } from './form.actions';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
import { uniqWith, isEqual } from 'lodash'; import { isEqual, uniqWith } from 'lodash';
export interface FormError { export interface FormError {
message: string; message: string;
@@ -45,6 +51,14 @@ export function formReducer(state = initialState, action: FormAction): FormState
return addFormErrors(state, action as FormAddError) 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: { default: {
return state; return state;
} }
@@ -60,7 +74,7 @@ function addFormErrors(state: FormState, action: FormAddError) {
}; };
return Object.assign({}, state, { return Object.assign({}, state, {
[ formId ]: { [formId]: {
data: state[formId].data, data: state[formId].data,
valid: state[formId].valid, valid: state[formId].valid,
errors: state[formId].errors ? uniqWith(state[formId].errors.concat(error), isEqual) : [].concat(error), 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. * Init form state.
* *
@@ -82,22 +121,18 @@ function addFormErrors(state: FormState, action: FormAddError) {
* the new state, with the form initialized. * the new state, with the form initialized.
*/ */
function initForm(state: FormState, action: FormInitAction): FormState { function initForm(state: FormState, action: FormInitAction): FormState {
if (!hasValue(state[ action.payload.formId ])) { const formState = {
return Object.assign({}, state, {
[ action.payload.formId ]: {
data: action.payload.formData, data: action.payload.formData,
valid: action.payload.valid, valid: action.payload.valid,
errors: [] errors: []
} };
if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, {
[action.payload.formId]: formState
}); });
} else { } else {
const newState = Object.assign({}, state); 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], formState);
data: action.payload.formData,
valid: action.payload.valid,
errors: []
}
);
return newState; return newState;
} }
} }
@@ -113,18 +148,18 @@ function initForm(state: FormState, action: FormInitAction): FormState {
* the new state, with the data changed. * the new state, with the data changed.
*/ */
function changeDataForm(state: FormState, action: FormChangeAction): FormState { function changeDataForm(state: FormState, action: FormChangeAction): FormState {
if (!hasValue(state[ action.payload.formId ])) { if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, { return Object.assign({}, state, {
[ action.payload.formId ]: { [action.payload.formId]: {
data: action.payload.formData, data: action.payload.formData,
valid: state[ action.payload.formId ].valid valid: state[action.payload.formId].valid
} }
}); });
} else { } else {
const newState = Object.assign({}, state); 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, data: action.payload.formData,
valid: state[ action.payload.formId ].valid valid: state[action.payload.formId].valid
} }
); );
return newState; return newState;
@@ -142,17 +177,17 @@ function changeDataForm(state: FormState, action: FormChangeAction): FormState {
* the new state, with the status changed. * the new state, with the status changed.
*/ */
function changeStatusForm(state: FormState, action: FormStatusChangeAction): FormState { function changeStatusForm(state: FormState, action: FormStatusChangeAction): FormState {
if (!hasValue(state[ action.payload.formId ])) { if (!hasValue(state[action.payload.formId])) {
return Object.assign({}, state, { return Object.assign({}, state, {
[ action.payload.formId ]: { [action.payload.formId]: {
data: state[ action.payload.formId ].data, data: state[action.payload.formId].data,
valid: action.payload.valid valid: action.payload.valid
} }
}); });
} else { } else {
const newState = Object.assign({}, state); 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: state[ action.payload.formId ].data, data: state[action.payload.formId].data,
valid: action.payload.valid valid: action.payload.valid
} }
); );
@@ -171,9 +206,9 @@ function changeStatusForm(state: FormState, action: FormStatusChangeAction): For
* the new state, with the form initialized. * the new state, with the form initialized.
*/ */
function removeForm(state: FormState, action: FormRemoveAction): FormState { function removeForm(state: FormState, action: FormRemoveAction): FormState {
if (hasValue(state[ action.payload.formId ])) { if (hasValue(state[action.payload.formId])) {
const newState = Object.assign({}, state); const newState = Object.assign({}, state);
delete newState[ action.payload.formId ]; delete newState[action.payload.formId];
return newState; return newState;
} else { } else {
return state; return state;

View File

@@ -84,7 +84,7 @@ export class FormService {
error[errorKey] = message; // assign message 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) { if (!model.errorMessages) {
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 <button
type="button"
class="btn btn-link" class="btn btn-link"
type="button"
tabindex="-1"
[disabled]="disabled" [disabled]="disabled"
(click)="increment()"> (click)="increment()">
<span class="chevron"></span> <span class="chevron"></span>
@@ -9,22 +10,23 @@
</button> </button>
<input <input
type="text" type="text"
class="form-control" class="form-control d-inline-block text-center"
maxlength="{{size}}" maxlength="{{size}}"
size="{{size}}" size="{{size}}"
placeholder="{{placeholder}}" placeholder="{{placeholder}}"
[name]="name" [name]="name"
[(ngModel)]="value" [(ngModel)]="value"
(change)="update($event)" (change)="update($event); $event.stopPropagation();"
(focus)="onFocus()" (focus)="onFocus($event); $event.stopPropagation();"
[readonly]="disabled" [readonly]="disabled"
[disabled]="disabled" [disabled]="disabled"
[ngClass]="{'is-invalid': invalid}" [ngClass]="{'is-invalid': invalid}"
aria-label="name" aria-label="name"
> >
<button <button
type="button"
class="btn btn-link" class="btn btn-link"
type="button"
tabindex="-1"
[disabled]="disabled" [disabled]="disabled"
(click)="decrement()"> (click)="decrement()">
<span class="chevron bottom"></span> <span class="chevron bottom"></span>

View File

@@ -1,17 +1,7 @@
.ngb-tp { :host {
display: flex; outline: none;
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;
} }
.chevron::before { .chevron::before {
border-style: solid; border-style: solid;
border-width: 0.29em 0.29em 0 0; border-width: 0.29em 0.29em 0 0;
@@ -33,12 +23,7 @@
-ms-transform: rotate(135deg); -ms-transform: rotate(135deg);
transform: rotate(135deg); transform: rotate(135deg);
} }
input { input {
text-align: center;
display: inline-block;
max-width: 80px !important; 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 { export class NumberPickerComponent implements OnInit, ControlValueAccessor {
@Output()
selected = new EventEmitter<number>();
@Output()
remove = new EventEmitter<number>();
@Output()
change = new EventEmitter<any>();
@Input() @Input() step: number;
step: number; @Input() min: number;
@Input() @Input() max: number;
min: number; @Input() size: number;
@Input() @Input() placeholder: string;
max: number; @Input() name: string;
@Input() @Input() disabled: boolean;
size: number; @Input() invalid: boolean;
@Input() @Input() value: number;
placeholder: string;
@Input() @Output() selected = new EventEmitter<number>();
name: string; @Output() remove = new EventEmitter<number>();
@Input() @Output() change = new EventEmitter<any>();
disabled: boolean; @Output() focus = new EventEmitter<any>();
@Input()
invalid: boolean;
@Input()
value: number;
lastValue: number; lastValue: number;
constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) {
@@ -109,19 +100,18 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {
this.value = null; this.value = null;
this.emitChange(); this.emitChange();
} else { } else {
this.value = this.lastValue; this.value = undefined;
this.emitChange();
} }
} catch (e) { } catch (e) {
this.value = this.lastValue; this.value = undefined;
this.emitChange();
} }
} }
onFocus() { onFocus(event) {
if (this.value) { if (this.value) {
this.lastValue = this.value; this.lastValue = this.value;
} }
this.focus.emit(event);
} }
writeValue(value) { writeValue(value) {
@@ -135,10 +125,11 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {
} }
registerOnChange(fn) { registerOnChange(fn) {
// this.change = fn; return
} }
registerOnTouched(fn) { registerOnTouched(fn) {
return
} }
emitChange() { emitChange() {