TSK-611 Add validation form errors on save
- Add custom form validation on save with highlighting animation and snack bar warning - Added validation for classifications, workbaskets information and access items. - Enable save button always - Refactored animations - Test configuration cleanup
This commit is contained in:
parent
cc62283437
commit
42197bee6c
|
@ -11,7 +11,7 @@
|
|||
<div *ngIf="classification" id="classification" class="panel panel-default classification">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<button type="button" [disabled]="!ClassificationForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary"
|
||||
<button type="button" (click)="onSubmit()" class="btn btn-default btn-primary"
|
||||
data-toggle="tooltip" title="Save">
|
||||
<span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<label for="classification-key" class="control-label">Key</label>
|
||||
<input type="text" required #key="ngModel" [disabled]="action!== 'CREATE'? true : false" class="form-control" id="classification-key"
|
||||
placeholder="Key" [(ngModel)]="classification.key" name="classification.key">
|
||||
<div *ngIf="!key.valid && action === 'CREATE'" class="required-text">
|
||||
<div *ngIf="!key.valid && action === 'CREATE'" class="required-text" [@validation]="this.toogleValidationMap.get('classification.key')">
|
||||
* Key is required
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<label for="classification-name" class="control-label">Name</label>
|
||||
<input type="text" required #name="ngModel" class="form-control" id="classification-name" placeholder="Name" [(ngModel)]="classification.name"
|
||||
name="classification.name">
|
||||
<div *ngIf="!name.valid" class="required-text">
|
||||
<div *ngIf="!name.valid" class="required-text" [@validation]="this.toogleValidationMap.get('classification.name')">
|
||||
* Name is required
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { ACTION } from 'app/models/action';
|
|||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
|
||||
import { highlight } from 'app/shared/animations/validation.animation';
|
||||
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
||||
|
||||
import { ClassificationsService } from 'app/administration/services/classifications/classifications.service';
|
||||
|
@ -24,10 +25,13 @@ import { ClassificationCategoriesService } from 'app/administration/services/cla
|
|||
import { DomainService } from 'app/services/domain/domain.service';
|
||||
import { CustomFieldsService } from '../../../services/custom-fields/custom-fields.service';
|
||||
import { Pair } from 'app/models/pair';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-classification-details',
|
||||
templateUrl: './classification-details.component.html',
|
||||
animations: [highlight],
|
||||
styleUrls: ['./classification-details.component.scss']
|
||||
})
|
||||
export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||
|
@ -62,6 +66,9 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
|||
private categoriesSubscription: Subscription;
|
||||
private domainSubscription: Subscription;
|
||||
|
||||
@ViewChild('ClassificationForm') classificationForm: NgForm;
|
||||
toogleValidationMap = new Map<string, boolean>();
|
||||
|
||||
constructor(private classificationsService: ClassificationsService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
|
@ -74,7 +81,8 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
|||
private categoryService: ClassificationCategoriesService,
|
||||
private domainService: DomainService,
|
||||
private customFieldsService: CustomFieldsService,
|
||||
private removeConfirmationService: RemoveConfirmationService) { }
|
||||
private removeConfirmationService: RemoveConfirmationService,
|
||||
private formsValidatorService: FormsValidatorService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.classificationTypeService.getClassificationTypes().subscribe((classificationTypes: Array<string>) => {
|
||||
|
@ -133,7 +141,13 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
|||
`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`);
|
||||
}
|
||||
|
||||
onSave() {
|
||||
onSubmit() {
|
||||
if (this.formsValidatorService.validate(this.classificationForm, this.toogleValidationMap)) {
|
||||
this.onSave();
|
||||
}
|
||||
}
|
||||
|
||||
private onSave() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
if (this.action === ACTION.CREATE) {
|
||||
this.classificationSavingSubscription = this.classificationsService.postClassification(this.classification)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<button type="button" (click)="onSave()" [disabled]="!AccessItemsForm.valid || action === 'COPY'" class="btn btn-default btn-primary"
|
||||
data-toggle="tooltip" title="Save">
|
||||
<button type="button" (click)="onSubmit()" [disabled]="action === 'COPY'" class="btn btn-default btn-primary" data-toggle="tooltip"
|
||||
title="Save">
|
||||
<span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" (click)="clear()" class="btn btn-default" data-toggle="tooltip" title="Undo Changes">
|
||||
|
@ -50,13 +50,15 @@
|
|||
<td *ngIf="accessIdField.lookupField else accessIdInput" class="input-group text-align text-width taskana-type-ahead" [ngClass]="{
|
||||
'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
|
||||
'has-error': !accessItem.value.accessId }">
|
||||
<taskana-type-ahead [(ngModel)]="accessItem.value.accessId" formControlName="accessId" placeHolderMessage="Access id is required"></taskana-type-ahead>
|
||||
<taskana-type-ahead [(ngModel)]="accessItem.value.accessId" formControlName="accessId" placeHolderMessage="* Access id is required"
|
||||
[validationValue]="toogleValidationAccessIdMap.get(index)"></taskana-type-ahead>
|
||||
</td>
|
||||
<ng-template #accessIdInput>
|
||||
<td class="input-group text-align text-width">
|
||||
<div [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
|
||||
'has-error': !accessItem.value.accessId }">
|
||||
<input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid? 'Access id is required': ''}}">
|
||||
<div [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !==accessItem.value.accessId), 'has-error':
|
||||
!accessItem.value.accessId }">
|
||||
<input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid?
|
||||
'* Access id is required': ''}}" [@validation]="toogleValidationAccessIdMap.get(index)">
|
||||
</div>
|
||||
</td>
|
||||
</ng-template>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { HttpClientModule } from '@angular/common/http';
|
|||
import { HttpModule } from '@angular/http';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { configureTests } from 'app/app.test.configuration';
|
||||
|
||||
import { Workbasket } from 'app/models/workbasket';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
|
@ -16,46 +17,16 @@ import { ICONTYPES } from 'app/models/type';
|
|||
import { AccessItemsComponent } from './access-items.component';
|
||||
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
||||
import { GeneralMessageModalComponent } from 'app/shared/general-message-modal/general-message-modal.component';
|
||||
import { TaskanaTypeAheadMockComponent } from 'app/shared/type-ahead/type-ahead.mock.component';
|
||||
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { configureTests } from 'app/app.test.configuration';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('AccessItemsComponent', () => {
|
||||
let component: AccessItemsComponent;
|
||||
let fixture: ComponentFixture<AccessItemsComponent>;
|
||||
|
@ -65,7 +36,7 @@ describe('AccessItemsComponent', () => {
|
|||
beforeEach(done => {
|
||||
const configure = (testBed: TestBed) => {
|
||||
testBed.configureTestingModule({
|
||||
declarations: [SpinnerComponent, AccessItemsComponent, GeneralMessageModalComponent, TaskanaTypeAheadComponent],
|
||||
declarations: [SpinnerComponent, AccessItemsComponent, GeneralMessageModalComponent, TaskanaTypeAheadMockComponent],
|
||||
imports: [FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule, ReactiveFormsModule],
|
||||
providers: [WorkbasketService, AlertService, ErrorModalService, SavingWorkbasketService, RequestInProgressService,
|
||||
CustomFieldsService]
|
||||
|
@ -124,7 +95,7 @@ describe('AccessItemsComponent', () => {
|
|||
});
|
||||
|
||||
it('should show alert successfull after saving', () => {
|
||||
component.onSave();
|
||||
component.onSubmit();
|
||||
expect(alertService.triggerAlert).toHaveBeenCalledWith(
|
||||
new AlertModel(AlertType.SUCCESS, `Workbasket ${component.workbasket.key} Access items were saved successfully`));
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Component, OnInit, Input, AfterViewInit, OnDestroy, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Component, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { NgForm, FormGroup, FormControl, FormBuilder, Validators, FormArray } from '@angular/forms';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FormBuilder, Validators, FormArray } from '@angular/forms';
|
||||
|
||||
import { Workbasket } from 'app/models/workbasket';
|
||||
import { WorkbasketAccessItems } from 'app/models/workbasket-access-items';
|
||||
|
@ -15,14 +14,14 @@ import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
|||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { TitlesService } from 'app/services/titles/titles.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
|
||||
import { highlight } from 'app/shared/animations/validation.animation';
|
||||
|
||||
declare var $: any;
|
||||
declare const $: any;
|
||||
@Component({
|
||||
selector: 'taskana-workbasket-access-items',
|
||||
templateUrl: './access-items.component.html',
|
||||
animations: [highlight],
|
||||
styleUrls: ['./access-items.component.scss']
|
||||
})
|
||||
export class AccessItemsComponent implements OnChanges, OnDestroy {
|
||||
|
@ -61,6 +60,7 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
accessItemsGroups: this.formBuilder.array([
|
||||
])
|
||||
});
|
||||
toogleValidationAccessIdMap = new Map<number, boolean>();
|
||||
|
||||
private initialized = false;
|
||||
|
||||
|
@ -141,7 +141,33 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
this.accessItemsClone.splice(index, 1);
|
||||
}
|
||||
|
||||
onSave() {
|
||||
onSubmit() {
|
||||
let valid = true;
|
||||
for (let i = 0; i < this.accessItemsGroups.length; i++) {
|
||||
if (this.accessItemsGroups.controls[i].invalid) {
|
||||
const validationState = this.toogleValidationAccessIdMap.get(i);
|
||||
validationState ? this.toogleValidationAccessIdMap.set(i, !validationState) : this.toogleValidationAccessIdMap.set(i, true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
this.alertService.triggerAlert(new AlertModel(AlertType.WARNING, `There are some empty fields which are required.`))
|
||||
return false;
|
||||
}
|
||||
this.onSave();
|
||||
}
|
||||
|
||||
checkAll(row: number, value: any) {
|
||||
const checkAll = value.target.checked;
|
||||
const workbasketAccessItemsObj = new WorkbasketAccessItems();
|
||||
for (const property in workbasketAccessItemsObj) {
|
||||
if (property !== 'accessId' && property !== '_links' && property !== 'workbasketId' && property !== 'accessItemId') {
|
||||
this.accessItemsGroups.controls[row].get(property).setValue(checkAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onSave() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
this.workbasketService.updateWorkBasketAccessItem(this.accessItemsResource._links.self.href, this.AccessItemsForm.value.accessItemsGroups)
|
||||
.subscribe(response => {
|
||||
|
@ -156,16 +182,6 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
})
|
||||
}
|
||||
|
||||
checkAll(row: number, value: any) {
|
||||
const checkAll = value.target.checked;
|
||||
const workbasketAccessItemsObj = new WorkbasketAccessItems();
|
||||
for (const property in workbasketAccessItemsObj) {
|
||||
if (property !== 'accessId' && property !== '_links' && property !== 'workbasketId' && property !== 'accessItemId') {
|
||||
this.accessItemsGroups.controls[row].get(property).setValue(checkAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setBadge() {
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div [@toggle]="toolbarState" *ngIf="toolbarState" class="row">
|
||||
<div [@toggleDown]="toolbarState" *ngIf="toolbarState" class="row">
|
||||
<taskana-filter class="col-xs-12" (performFilter)="performAvailableFilter($event)"></taskana-filter>
|
||||
</div>
|
||||
<taskana-spinner [isRunning]="requestInProgress" positionClass="centered-spinner" class="floating"></taskana-spinner>
|
||||
|
|
|
@ -1,28 +1,14 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { WorkbasketSummary } from 'app/models/workbasket-summary';
|
||||
import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
|
||||
import { FilterModel } from 'app/models/filter';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { Side } from '../distribution-targets.component';
|
||||
import { expandDown } from 'app/shared/animations/expand.animation';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-dual-list',
|
||||
templateUrl: './dual-list.component.html',
|
||||
styleUrls: ['./dual-list.component.scss'],
|
||||
animations: [
|
||||
trigger('toggle', [
|
||||
state('*', style({ opacity: '1' })),
|
||||
state('void', style({ opacity: '0' })),
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0, height: '0px' }),
|
||||
style({ opacity: 0.5, height: '50px' }),
|
||||
style({ opacity: 1, height: '*' })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1, height: '*' }),
|
||||
style({ opacity: 0.5, height: '50px' }),
|
||||
style({ opacity: 0, height: '0px' })])))
|
||||
]
|
||||
)],
|
||||
animations: [expandDown]
|
||||
})
|
||||
export class DualListComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<button type="button" [disabled]="!WorkbasketForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary" data-toggle="tooltip"
|
||||
title="Save">
|
||||
<button type="button" (click)="onSubmit()" class="btn btn-default btn-primary" data-toggle="tooltip" title="Save">
|
||||
<span class="glyphicon glyphicon-floppy-save" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default" data-toggle="tooltip" title="Undo Changes">
|
||||
|
@ -30,7 +29,7 @@
|
|||
<label for="wb-key" class="control-label">Key</label>
|
||||
<input type="text" required #key="ngModel" class="form-control" id="wb-key" placeholder="Key" [(ngModel)]="workbasket.key"
|
||||
name="workbasket.key">
|
||||
<div *ngIf="!key.valid" class="required-text">
|
||||
<div *ngIf="!key.valid" class="required-text" [@validation]="this.toogleValidationMap.get('workbasket.key')">
|
||||
* Key is required
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,18 +37,18 @@
|
|||
<label for="wb-name" class="control-label">Name</label>
|
||||
<input type="text" required #name="ngModel" class="form-control" id="wb-name" placeholder="Name" [(ngModel)]="workbasket.name"
|
||||
name="workbasket.name">
|
||||
<div *ngIf="!name.valid" class="required-text">
|
||||
<div *ngIf="!name.valid" class="required-text" [@validation]="this.toogleValidationMap.get('workbasket.name')">
|
||||
* Name is required
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group required">
|
||||
<label for="wb-owner" class="control-label">Owner</label>
|
||||
<taskana-type-ahead *ngIf="ownerField.lookupField else ownerInput" required #owner="ngModel" name="owner" [(ngModel)]="workbasket.owner"
|
||||
placeHolderMessage="Owner is required"></taskana-type-ahead>
|
||||
<taskana-type-ahead *ngIf="ownerField.lookupField else ownerInput" required #owner="ngModel" name="workbasket.owner" [(ngModel)]="workbasket.owner"
|
||||
placeHolderMessage="* Owner is required" [validationValue]="this.toogleValidationMap.get('workbasket.owner')"></taskana-type-ahead>
|
||||
<ng-template #ownerInput>
|
||||
<input type="text" required #owner="ngModel" class="form-control" id="wb-owner" placeholder="Owner" [(ngModel)]="workbasket.owner"
|
||||
name="workbasket.owner">
|
||||
<div *ngIf="!owner?.valid" class="required-text">
|
||||
<div *ngIf="!owner?.valid" class="required-text" [@validation]="this.toogleValidationMap.get('workbasket.owner')">
|
||||
* Owner is required
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { WorkbasketInformationComponent } from './workbasket-information.component';
|
||||
import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Component, Input, forwardRef } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { Workbasket } from 'app/models/workbasket';
|
||||
|
@ -18,11 +18,13 @@ import { Links } from 'app/models/links';
|
|||
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
|
||||
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
||||
import { GeneralMessageModalComponent } from 'app/shared/general-message-modal/general-message-modal.component';
|
||||
import { TaskanaTypeAheadMockComponent } from 'app/shared/type-ahead/type-ahead.mock.component';
|
||||
|
||||
import { MapValuesPipe } from 'app/shared/pipes/mapValues/map-values.pipe';
|
||||
import { RemoveNoneTypePipe } from 'app/shared/pipes/removeNoneType/remove-none-type.pipe';
|
||||
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
|
@ -35,36 +37,6 @@ import { configureTests } from 'app/app.test.configuration';
|
|||
export class DummyDetailComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' },
|
||||
{ path: 'someNewId', component: DummyDetailComponent }
|
||||
|
@ -80,7 +52,7 @@ describe('InformationComponent', () => {
|
|||
testBed.configureTestingModule({
|
||||
declarations: [WorkbasketInformationComponent, IconTypeComponent, MapValuesPipe,
|
||||
RemoveNoneTypePipe, SpinnerComponent, GeneralMessageModalComponent, DummyDetailComponent,
|
||||
TaskanaTypeAheadComponent],
|
||||
TaskanaTypeAheadMockComponent],
|
||||
imports: [FormsModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientModule,
|
||||
|
@ -153,7 +125,7 @@ describe('InformationComponent', () => {
|
|||
'orgLevel3', 'orgLevel4', new Links({ 'href': 'someUrl' }));
|
||||
spyOn(workbasketService, 'updateWorkbasket').and.returnValue(Observable.of(component.workbasket));
|
||||
spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(Observable.of(component.workbasket));
|
||||
component.onSave();
|
||||
component.onSubmit();
|
||||
expect(component.requestInProgress).toBeFalsy();
|
||||
|
||||
}));
|
||||
|
@ -164,7 +136,8 @@ describe('InformationComponent', () => {
|
|||
'orgLevel3', 'orgLevel4', new Links({ 'href': 'someUrl' }));
|
||||
spyOn(workbasketService, 'updateWorkbasket').and.returnValue(Observable.of(component.workbasket));
|
||||
spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(Observable.of(component.workbasket));
|
||||
component.onSave();
|
||||
fixture.detectChanges();
|
||||
component.onSubmit();
|
||||
expect(workbasketService.triggerWorkBasketSaved).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -177,8 +150,8 @@ describe('InformationComponent', () => {
|
|||
new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
|
||||
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
|
||||
'orgLevel3', 'orgLevel4', new Links({ 'href': 'someUrl' }))));
|
||||
|
||||
component.onSave();
|
||||
fixture.detectChanges();
|
||||
component.onSubmit();
|
||||
expect(alertService.triggerAlert).toHaveBeenCalled();
|
||||
expect(component.workbasket.workbasketId).toBe('someNewId');
|
||||
});
|
||||
|
@ -199,8 +172,8 @@ describe('InformationComponent', () => {
|
|||
|
||||
spyOn(savingWorkbasketService, 'triggerDistributionTargetSaving');
|
||||
spyOn(savingWorkbasketService, 'triggerAccessItemsSaving');
|
||||
|
||||
component.onSave();
|
||||
fixture.detectChanges();
|
||||
component.onSubmit();
|
||||
expect(alertService.triggerAlert).toHaveBeenCalled();
|
||||
expect(component.workbasket.workbasketId).toBe('someNewId');
|
||||
expect(savingWorkbasketService.triggerDistributionTargetSaving).toHaveBeenCalled();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { ICONTYPES } from 'app/models/type';
|
||||
import { ErrorModel } from 'app/models/modal-error';
|
||||
|
@ -16,9 +17,12 @@ import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
|||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service';
|
||||
import { highlight } from 'app/shared/animations/validation.animation';
|
||||
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-workbasket-information',
|
||||
animations: [highlight],
|
||||
templateUrl: './workbasket-information.component.html',
|
||||
styleUrls: ['./workbasket-information.component.scss']
|
||||
})
|
||||
|
@ -28,6 +32,7 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
@Input()
|
||||
workbasket: Workbasket;
|
||||
workbasketClone: Workbasket;
|
||||
workbasketErrors
|
||||
@Input()
|
||||
action: string;
|
||||
|
||||
|
@ -41,8 +46,11 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
custom3Field = this.customFieldsService.getCustomField('Custom 3', 'workbaskets.information.custom3');
|
||||
custom4Field = this.customFieldsService.getCustomField('Custom 4', 'workbaskets.information.custom4');
|
||||
|
||||
toogleValidationMap = new Map<string, boolean>();
|
||||
|
||||
private workbasketSubscription: Subscription;
|
||||
private routeSubscription: Subscription;
|
||||
@ViewChild('WorkbasketForm') workbasketForm: NgForm;
|
||||
|
||||
constructor(private workbasketService: WorkbasketService,
|
||||
private alertService: AlertService,
|
||||
|
@ -52,7 +60,8 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
private savingWorkbasket: SavingWorkbasketService,
|
||||
private requestInProgressService: RequestInProgressService,
|
||||
private customFieldsService: CustomFieldsService,
|
||||
private removeConfirmationService: RemoveConfirmationService) {
|
||||
private removeConfirmationService: RemoveConfirmationService,
|
||||
private formsValidatorService: FormsValidatorService) {
|
||||
this.allTypes = new Map([['PERSONAL', 'Personal'], ['GROUP', 'Group'],
|
||||
['CLEARANCE', 'Clearance'], ['TOPIC', 'Topic']])
|
||||
|
||||
|
@ -75,7 +84,13 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
this.workbasket.type = type;
|
||||
}
|
||||
|
||||
onSave() {
|
||||
onSubmit() {
|
||||
if (this.workbasketForm && this.formsValidatorService.validate(this.workbasketForm, this.toogleValidationMap)) {
|
||||
this.onSave();
|
||||
}
|
||||
}
|
||||
|
||||
private onSave() {
|
||||
this.beforeRequest();
|
||||
if (!this.workbasket.workbasketId) {
|
||||
this.postNewWorkbasket();
|
||||
|
|
|
@ -21,6 +21,10 @@ import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
|||
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { configureTests } from 'app/app.test.configuration';
|
||||
|
||||
import { WorkbasketDetailsComponent } from './workbasket-details.component';
|
||||
import { WorkbasketInformationComponent } from './information/workbasket-information.component';
|
||||
|
@ -31,13 +35,14 @@ import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
|||
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
|
||||
import { AlertComponent } from 'app/shared/alert/alert.component';
|
||||
import { GeneralMessageModalComponent } from 'app/shared/general-message-modal/general-message-modal.component';
|
||||
import { TaskanaTypeAheadMockComponent } from 'app/shared/type-ahead/type-ahead.mock.component';
|
||||
|
||||
import { MapValuesPipe } from 'app/shared/pipes/mapValues/map-values.pipe';
|
||||
import { RemoveNoneTypePipe } from 'app/shared/pipes/removeNoneType/remove-none-type.pipe';
|
||||
import { SelectWorkBasketPipe } from 'app/shared/pipes/selectedWorkbasket/seleted-workbasket.pipe';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
import { configureTests } from 'app/app.test.configuration';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-filter',
|
||||
template: ''
|
||||
|
@ -55,37 +60,6 @@ export class FilterComponent {
|
|||
export class DummyDetailComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('WorkbasketDetailsComponent', () => {
|
||||
let component: WorkbasketDetailsComponent;
|
||||
let fixture: ComponentFixture<WorkbasketDetailsComponent>;
|
||||
|
@ -107,7 +81,7 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent, SpinnerComponent,
|
||||
IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent,
|
||||
DistributionTargetsComponent, FilterComponent, DualListComponent, DummyDetailComponent,
|
||||
TaskanaTypeAheadComponent, SelectWorkBasketPipe],
|
||||
TaskanaTypeAheadMockComponent, SelectWorkBasketPipe],
|
||||
providers: [WorkbasketService, MasterAndDetailService, ErrorModalService, RequestInProgressService,
|
||||
AlertService, SavingWorkbasketService,
|
||||
CustomFieldsService]
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<div [@toggle]="toolbarState" *ngIf="toolbarState" class="row no-overflow">
|
||||
<div [@toggleDown]="toolbarState" *ngIf="toolbarState" class="row no-overflow">
|
||||
<taskana-filter (performFilter)="filtering($event)"></taskana-filter>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -1,34 +1,21 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter, AfterViewChecked } from '@angular/core';
|
||||
import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { SortingModel } from 'app/models/sorting';
|
||||
import { FilterModel } from 'app/models/filter';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { WorkbasketSummary } from 'app/models/workbasket-summary';
|
||||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { ImportType } from 'app/models/import-type';
|
||||
import { expandDown } from 'app/shared/animations/expand.animation';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-workbasket-list-toolbar',
|
||||
animations: [
|
||||
trigger('toggle', [
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ height: '0px' }),
|
||||
style({ height: '50px' }),
|
||||
style({ height: '*' })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ height: '*' }),
|
||||
style({ height: '50px' }),
|
||||
style({ height: '0px' })])))
|
||||
]
|
||||
)],
|
||||
animations: [expandDown],
|
||||
templateUrl: './workbasket-list-toolbar.component.html',
|
||||
styleUrls: ['./workbasket-list-toolbar.component.scss']
|
||||
})
|
||||
|
|
|
@ -20,6 +20,8 @@ import { ErrorModalService } from './services/errorModal/error-modal.service';
|
|||
import { RequestInProgressService } from './services/requestInProgress/request-in-progress.service';
|
||||
import { OrientationService } from './services/orientation/orientation.service';
|
||||
import { SelectedRouteService } from './services/selected-route/selected-route';
|
||||
import { FormsValidatorService } from './shared/services/forms/forms-validator.service';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
|
||||
export const configureTests = (configure: (testBed: TestBed) => void) => {
|
||||
|
@ -33,9 +35,10 @@ export const configureTests = (configure: (testBed: TestBed) => void) => {
|
|||
|
||||
configure(testBed);
|
||||
testBed.configureTestingModule({
|
||||
imports: [BrowserAnimationsModule],
|
||||
providers: [{ provide: TaskanaEngineService, useClass: TaskanaEngineServiceMock },
|
||||
{ provide: DomainService, useClass: DomainServiceMock }, CustomFieldsService, RemoveConfirmationService,
|
||||
AlertService, ErrorModalService, RequestInProgressService, OrientationService, SelectedRouteService]
|
||||
AlertService, ErrorModalService, RequestInProgressService, OrientationService, SelectedRouteService, FormsValidatorService]
|
||||
});
|
||||
|
||||
return testBed.compileComponents().then(() => testBed);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { TaskanaTypeAheadMockComponent } from 'app/shared/type-ahead/type-ahead.mock.component';
|
||||
|
||||
const MODULES = [
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
TaskanaTypeAheadMockComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: DECLARATIONS,
|
||||
imports: MODULES,
|
||||
providers: []
|
||||
})
|
||||
export class AppTestModule {
|
||||
}
|
|
@ -30,7 +30,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div [@toggle]="showNavbar" *ngIf="showNavbar" class="navbar-inverse sidenav full-height col-xs-9 col-sm-3" data-html="false"
|
||||
<div [@toggleRight]="showNavbar" *ngIf="showNavbar" class="navbar-inverse sidenav full-height col-xs-9 col-sm-3" data-html="false"
|
||||
aria-expanded="true">
|
||||
<div class="row">
|
||||
<ul class="nav">
|
||||
|
|
|
@ -2,29 +2,18 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
|
|||
import { environment } from 'environments/environment';
|
||||
import { SelectedRouteService } from 'app/services/selected-route/selected-route';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { trigger, state, style, transition, keyframes, animate } from '@angular/animations';
|
||||
import { DomainService } from 'app/services/domain/domain.service';
|
||||
import { BusinessAdminGuard } from 'app/guards/business-admin-guard';
|
||||
import { MonitorGuard } from 'app/guards/monitor-guard';
|
||||
import { WindowRefService } from 'app/services/window/window.service';
|
||||
import { UserGuard } from 'app/guards/user-guard';
|
||||
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||
import { expandRight } from 'app/shared/animations/expand.animation';
|
||||
@Component({
|
||||
selector: 'taskana-nav-bar',
|
||||
templateUrl: './nav-bar.component.html',
|
||||
styleUrls: ['./nav-bar.component.scss'],
|
||||
animations: [
|
||||
trigger('toggle', [
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0, width: '0px' }),
|
||||
style({ opacity: 1, width: '150px' }),
|
||||
style({ opacity: 1, width: '*' })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1, width: '*' }),
|
||||
style({ opacity: 0, width: '150px' }),
|
||||
style({ opacity: 0, width: '0px' })])))
|
||||
]
|
||||
)],
|
||||
animations: [expandRight],
|
||||
})
|
||||
export class NavBarComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { environment } from 'environments/environment';
|
||||
import { UserInfoModel } from 'app/models/user-info';
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TaskanaEngineServiceMock {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div *ngIf="alert" [@alertState]="alert" class="alert alert-{{alert.type}} {{alert.autoClosing? '':'alert-dismissible'}} footer"
|
||||
<div *ngIf="alert" [@toggleTop]="alert" class="alert alert-{{alert.type}} {{alert.autoClosing? '':'alert-dismissible'}} footer"
|
||||
role="alert">
|
||||
<span class="glyphicon {{alert.type === 'success'? 'glyphicon-thumbs-up': 'glyphicon-exclamation-sign' }}" aria-hidden="true"></span>
|
||||
{{alert.text}}
|
||||
|
|
|
@ -1,25 +1,14 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { trigger, state, style, animate, transition } from '@angular/animations';
|
||||
|
||||
import { AlertModel } from 'app/models/alert';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { expandTop } from '../animations/expand.animation';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-alert',
|
||||
templateUrl: './alert.component.html',
|
||||
styleUrls: ['./alert.component.scss'],
|
||||
animations: [
|
||||
trigger('alertState', [
|
||||
state('in', style({ transform: 'translateY(0)', overflow: 'hidden' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateY(100%)', overflow: 'hidden' }),
|
||||
animate(100)
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate(100, style({ transform: 'translateY(100%)', overflow: 'hidden' }))
|
||||
])
|
||||
])
|
||||
]
|
||||
animations: [expandTop]
|
||||
})
|
||||
|
||||
export class AlertComponent implements OnInit {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { trigger, style, transition, animate, keyframes, state } from '@angular/core';
|
||||
|
||||
export const expandDown =
|
||||
trigger('toggleDown', [
|
||||
state('*', style({ opacity: '1' })),
|
||||
state('void', style({ opacity: '0' })),
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0, height: '0px' }),
|
||||
style({ opacity: 0.5, height: '50px' }),
|
||||
style({ opacity: 1, height: '*' })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1, height: '*' }),
|
||||
style({ opacity: 0.5, height: '50px' }),
|
||||
style({ opacity: 0, height: '0px' })])))
|
||||
]);
|
||||
|
||||
|
||||
export const expandRight = trigger('toggleRight', [
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0, width: '0px' }),
|
||||
style({ opacity: 1, width: '150px' }),
|
||||
style({ opacity: 1, width: '*' })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1, width: '*' }),
|
||||
style({ opacity: 0, width: '150px' }),
|
||||
style({ opacity: 0, width: '0px' })])))
|
||||
]);
|
||||
|
||||
export const expandTop = trigger('toggleTop', [
|
||||
state('in', style({ transform: 'translateY(0)', overflow: 'hidden' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateY(100%)', overflow: 'hidden' }),
|
||||
animate(100)
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate(100, style({ transform: 'translateY(100%)', overflow: 'hidden' }))
|
||||
])
|
||||
])
|
||||
|
||||
export const opacity = trigger('toggleOpacity', [
|
||||
state('*', style({ opacity: '1' })),
|
||||
state('void', style({ opacity: '0' })),
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0 }),
|
||||
style({ opacity: 0.5 }),
|
||||
style({ opacity: 1 })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1 }),
|
||||
style({ opacity: 0.5 }),
|
||||
style({ opacity: 0 })])))
|
||||
])
|
|
@ -0,0 +1,12 @@
|
|||
import { trigger, style, transition, animate, keyframes } from '@angular/core';
|
||||
|
||||
export const highlight = trigger('validation', [
|
||||
transition('true => false, false => true, * => true', animate('1500ms', keyframes([
|
||||
style({ opacity: '1', }),
|
||||
style({ opacity: '0.3' }),
|
||||
style({ opacity: '1' }),
|
||||
style({ opacity: '0.3' }),
|
||||
style({ opacity: '1' }),
|
||||
style({ opacity: '0.3' }),
|
||||
style({ opacity: '1' })])))
|
||||
]);
|
|
@ -0,0 +1,25 @@
|
|||
import { NgForm } from '@angular/forms';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
|
||||
@Injectable()
|
||||
export class FormsValidatorService {
|
||||
constructor(
|
||||
private alertService: AlertService) { }
|
||||
|
||||
public validate(form: NgForm, toogleValidationMap: Map<any, boolean>): boolean {
|
||||
let valid = true;
|
||||
for (const control in form.form.controls) {
|
||||
if (form.form.controls[control].invalid) {
|
||||
const validationState = toogleValidationMap.get(control);
|
||||
validationState ? toogleValidationMap.set(control, !validationState) : toogleValidationMap.set(control, true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
this.alertService.triggerAlert(new AlertModel(AlertType.WARNING, `There are some empty fields which are required.`))
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import { AlertComponent } from 'app/shared/alert/alert.component';
|
|||
import { MasterAndDetailComponent } from 'app/shared/master-and-detail/master-and-detail.component';
|
||||
import { TaskanaTreeComponent } from 'app/shared/tree/tree.component';
|
||||
import { TypeAheadComponent } from 'app/shared/type-ahead/type-ahead.component';
|
||||
import { SortComponent } from './sort/sort.component';
|
||||
import { RemoveConfirmationComponent } from 'app/shared/remove-confirmation/remove-confirmation.component';
|
||||
|
||||
/**
|
||||
|
@ -31,7 +32,7 @@ import { MapToIterable } from './pipes/mapToIterable/mapToIterable';
|
|||
*/
|
||||
import { HttpClientInterceptor } from './services/httpClientInterceptor/http-client-interceptor.service';
|
||||
import { AccessIdsService } from './services/access-ids/access-ids.service';
|
||||
import { SortComponent } from './sort/sort.component';
|
||||
import { FormsValidatorService } from './services/forms/forms-validator.service';
|
||||
|
||||
|
||||
|
||||
|
@ -73,7 +74,8 @@ const DECLARATIONS = [
|
|||
useClass: HttpClientInterceptor,
|
||||
multi: true
|
||||
},
|
||||
AccessIdsService
|
||||
AccessIdsService,
|
||||
FormsValidatorService
|
||||
]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
required #accessItemName="ngModel" [(ngModel)]="value" [typeahead]="dataSource" typeaheadOptionField="name" [typeaheadItemTemplate]="customItemTemplate"
|
||||
(typeaheadOnSelect)="typeaheadOnSelect($event, index)" [typeaheadScrollable]="true" [typeaheadOptionsInScrollableView]="typeaheadOptionsInScrollableView"
|
||||
[typeaheadMinLength]="typeaheadMinLength" [typeaheadWaitMs]="typeaheadWaitMs" (typeaheadLoading)="changeTypeaheadLoading($event)"
|
||||
placeholder="{{accessItemName.invalid? placeHolderMessage: ''}}">
|
||||
|
||||
placeholder="{{accessItemName.invalid? placeHolderMessage: ''}}" [@validation]="validationValue">
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +1,25 @@
|
|||
$blue: #2e9eca;
|
||||
$grey: #ddd;
|
||||
$invalid: #a94442;
|
||||
.wrapper-text {
|
||||
height: 47px;
|
||||
& label {
|
||||
width: 100%;
|
||||
margin-bottom: 0px;
|
||||
padding-left: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
& div {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid $grey;
|
||||
margin-top:6px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
> div{
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +35,7 @@ $grey: #ddd;
|
|||
&:focus{
|
||||
border-bottom: 1px solid $blue;
|
||||
}
|
||||
padding-left: 12px;
|
||||
|
||||
}
|
||||
|
||||
|
@ -50,3 +56,15 @@ $grey: #ddd;
|
|||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: $invalid;
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||
color: $invalid;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder { /* Microsoft Edge */
|
||||
color: $invalid;
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
import { Component, OnInit, Input, EventEmitter, Output, ViewChild, ElementRef, forwardRef } from '@angular/core';
|
||||
import { Component, OnInit, Input, ViewChild, forwardRef } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
|
||||
|
||||
import { AccessIdsService } from 'app/shared/services/access-ids/access-ids.service';
|
||||
import { AccessItemsComponent } from 'app/administration/workbasket/details/access-items/access-items.component';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { highlight } from 'app/shared/animations/validation.animation';
|
||||
|
||||
const noop = () => {
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
templateUrl: './type-ahead.component.html',
|
||||
styleUrls: ['./type-ahead.component.scss'],
|
||||
animations: [highlight],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
|
@ -29,6 +28,9 @@ export class TypeAheadComponent implements OnInit, ControlValueAccessor {
|
|||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
@Input()
|
||||
validationValue;
|
||||
|
||||
@ViewChild('inputTypeAhead')
|
||||
private inputTypeAhead;
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { Component, forwardRef, Input } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadMockComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadMockComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
@Input()
|
||||
validationValue;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,24 +1,12 @@
|
|||
import { Component, OnInit, HostListener } from '@angular/core';
|
||||
import { trigger, transition, keyframes, style, animate, state } from '@angular/animations';
|
||||
import { opacity } from 'app/shared/animations/expand.animation';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-code',
|
||||
templateUrl: './code.component.html',
|
||||
styleUrls: ['./code.component.scss'],
|
||||
animations: [
|
||||
trigger('toggle', [
|
||||
state('*', style({ opacity: '1' })),
|
||||
state('void', style({ opacity: '0' })),
|
||||
transition('void => *', animate('300ms ease-in', keyframes([
|
||||
style({ opacity: 0 }),
|
||||
style({ opacity: 0.5 }),
|
||||
style({ opacity: 1 })]))),
|
||||
transition('* => void', animate('300ms ease-out', keyframes([
|
||||
style({ opacity: 1 }),
|
||||
style({ opacity: 0.5 }),
|
||||
style({ opacity: 0 })])))
|
||||
])
|
||||
]
|
||||
animations: [opacity]
|
||||
})
|
||||
export class CodeComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
}
|
||||
|
||||
.required-text {
|
||||
padding-left: 15px;
|
||||
padding-left: 3px;
|
||||
color: $invalid;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue