added create-from functionality

This commit is contained in:
lotte
2020-06-29 22:17:41 +02:00
parent 32c8d9de03
commit 1cb0bb72e4
50 changed files with 408 additions and 237 deletions

View File

@@ -0,0 +1,16 @@
<div class="form-row mb-2 mx-0">
<select id="process-parameters"
class="form-control col"
name="parameter-{{index}}"
[(ngModel)]="selectedParameter"
#param="ngModel">
<option [ngValue]="undefined">Add a parameter...</option>
<option *ngFor="let param of parameters" [ngValue]="param.name">
{{param.nameLong || param.name}}
</option>
</select>
<ds-parameter-value-input [initialValue]="parameterValue.value" [parameter]="selectedScriptParameter" (updateValue)="selectedParameterValue = $event" class="d-block col" [index]="index"></ds-parameter-value-input>
<button *ngIf="removable" class="btn btn-light col-1 remove-button" (click)="removeParameter.emit(parameterValue);"><span class="fas fa-trash"></span></button>
<span *ngIf="!removable" class="col-1"></span>
</div>

View File

@@ -0,0 +1,71 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ParameterSelectComponent } from './parameter-select.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ScriptParameter } from '../../../scripts/script-parameter.model';
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
import { By } from '@angular/platform-browser';
describe('ParameterSelectComponent', () => {
let component: ParameterSelectComponent;
let fixture: ComponentFixture<ParameterSelectComponent>;
let scriptParams: ScriptParameter[];
function init() {
scriptParams = [
Object.assign(
new ScriptParameter(),
{
name: '-a',
type: ScriptParameterType.BOOLEAN
}
),
Object.assign(
new ScriptParameter(),
{
name: '-f',
type: ScriptParameterType.FILE
}
),
]
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ParameterSelectComponent],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ParameterSelectComponent);
component = fixture.componentInstance;
component.parameters = scriptParams;
component.removable = false;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should show the remove button when removable', () => {
component.removable = true;
fixture.detectChanges();
const button = fixture.debugElement.query(By.css('button.remove-button'));
expect(button).not.toBeNull();
});
it('should hide the remove button when not removable', () => {
component.removable = false;
fixture.detectChanges();
const button = fixture.debugElement.query(By.css('button.remove-button'));
expect(button).toBeNull();
});
});

View File

@@ -0,0 +1,90 @@
import { Component, EventEmitter, Input, OnInit, Output, Optional } from '@angular/core';
import { ProcessParameter } from '../../../processes/process-parameter.model';
import { ScriptParameter } from '../../../scripts/script-parameter.model';
import { hasNoValue } from '../../../../shared/empty.util';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../process-form.component';
/**
* Component to select a single parameter for a process
*/
@Component({
selector: 'ds-parameter-select',
templateUrl: './parameter-select.component.html',
styleUrls: ['./parameter-select.component.scss'],
viewProviders: [{
provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]]
}]
})
export class ParameterSelectComponent {
@Input() index: number;
/**
* The current parameter value of the selected parameter
*/
@Input() parameterValue: ProcessParameter = new ProcessParameter();
/**
* The available script parameters for the script
*/
@Input() parameters: ScriptParameter[];
/**
* Whether or not this selected parameter can be removed from the list
*/
@Input() removable: boolean;
/**
* Emits the parameter value when it's removed
*/
@Output() removeParameter: EventEmitter<ProcessParameter> = new EventEmitter<ProcessParameter>();
/**
* Emits the updated parameter value when it changes
*/
@Output() changeParameter: EventEmitter<ProcessParameter> = new EventEmitter<ProcessParameter>();
/**
* Returns the script parameter based on the currently selected name
*/
get selectedScriptParameter(): ScriptParameter {
return this.parameters.find((parameter: ScriptParameter) => parameter.name === this.selectedParameter);
}
/**
* Return the currently selected parameter name
*/
get selectedParameter(): string {
return this.parameterValue ? this.parameterValue.name : undefined;
}
/**
* Sets the currently selected parameter based on the provided parameter name
* Emits the new value from the changeParameter output
* @param value The parameter name to set
*/
set selectedParameter(value: string) {
this.parameterValue.name = value;
this.selectedParameterValue = undefined;
this.changeParameter.emit(this.parameterValue);
}
/**
* Returns the currently selected parameter value
*/
get selectedParameterValue(): any {
return this.parameterValue ? this.parameterValue.value : undefined;
}
/**
* Sets the currently selected value for the parameter
* Emits the new value from the changeParameter output
* @param value The parameter value to set
*/
set selectedParameterValue(value: any) {
this.parameterValue.value = value;
this.changeParameter.emit(this.parameterValue);
}
}

View File

@@ -0,0 +1 @@
<input type="hidden" value="true" name="boolean-value-{{index}}" id="boolean-value-{{index}}"/>

View File

@@ -0,0 +1,32 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BooleanValueInputComponent } from './boolean-value-input.component';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
describe('BooleanValueInputComponent', () => {
let component: BooleanValueInputComponent;
let fixture: ComponentFixture<BooleanValueInputComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BooleanValueInputComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BooleanValueInputComponent);
component = fixture.componentInstance;
spyOn(component.updateValue, 'emit');
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should emit true onInit', () => {
expect(component.updateValue.emit).toHaveBeenCalledWith(true);
});
});

View File

@@ -0,0 +1,21 @@
import { Component, OnInit, Optional } from '@angular/core';
import { ValueInputComponent } from '../value-input.component';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../../process-form.component';
/**
* Represents the value of a boolean parameter
*/
@Component({
selector: 'ds-boolean-value-input',
templateUrl: './boolean-value-input.component.html',
styleUrls: ['./boolean-value-input.component.scss'],
viewProviders: [ { provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] } ]
})
export class BooleanValueInputComponent extends ValueInputComponent<boolean> implements OnInit {
ngOnInit() {
this.updateValue.emit(true)
}
}

View File

@@ -0,0 +1,7 @@
<input required #string="ngModel" type="text" class="form-control" name="date-value-{{index}}" id="date-value-{{index}}" [ngModel]="value" (ngModelChange)="setValue($event)"/>
<div *ngIf="string.invalid && (string.dirty || string.touched)"
class="alert alert-danger validation-error">
<div *ngIf="string.errors.required">
{{'process.new.parameter.string.required' | translate}}
</div>
</div>

View File

@@ -0,0 +1,70 @@
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { DateValueInputComponent } from './date-value-input.component';
import { FormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
describe('DateValueInputComponent', () => {
let component: DateValueInputComponent;
let fixture: ComponentFixture<DateValueInputComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [DateValueInputComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DateValueInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should not show a validation error if the input field was left untouched but left empty', () => {
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
});
it('should show a validation error if the input field was touched but left empty', fakeAsync(() => {
component.value = '';
fixture.detectChanges();
tick();
const input = fixture.debugElement.query(By.css('input'));
input.triggerEventHandler('blur', null);
fixture.detectChanges();
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeTruthy();
}));
it('should not show a validation error if the input field was touched but not left empty', fakeAsync(() => {
component.value = 'testValue';
fixture.detectChanges();
tick();
const input = fixture.debugElement.query(By.css('input'));
input.triggerEventHandler('blur', null);
fixture.detectChanges();
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
}));
});

View File

@@ -0,0 +1,32 @@
import { Component, OnInit, Optional, Input } from '@angular/core';
import { ValueInputComponent } from '../value-input.component';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../../process-form.component';
/**
* Represents the user inputted value of a date parameter
*/
@Component({
selector: 'ds-date-value-input',
templateUrl: './date-value-input.component.html',
styleUrls: ['./date-value-input.component.scss'],
viewProviders: [ { provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] } ]
})
export class DateValueInputComponent extends ValueInputComponent<string> {
/**
* The current value of the date string
*/
value: string;
@Input() initialValue;
ngOnInit() {
this.value = this.initialValue;
}
setValue(value) {
this.value = value;
this.updateValue.emit(value)
}
}

View File

@@ -0,0 +1,13 @@
<label for="file-upload-{{index}}" class="d-flex align-items-center m-0">
<span class="btn btn-light">
{{'process.new.parameter.file.upload-button' | translate}}
</span>
<span class="file-name ml-1">{{fileObject?.name}}</span>
</label>
<input requireFile #file="ngModel" type="file" name="file-upload-{{index}}" id="file-upload-{{index}}" class="form-control-file d-none" [ngModel]="fileObject" (ngModelChange)="setFile($event)"/>
<div *ngIf="file.invalid && (file.dirty || file.touched)"
class="alert alert-danger validation-error">
<div *ngIf="file.errors.required">
{{'process.new.parameter.file.required' | translate}}
</div>
</div>

View File

@@ -0,0 +1,6 @@
.file-name {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@@ -0,0 +1,57 @@
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule, NgForm, ReactiveFormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { FileValueInputComponent } from './file-value-input.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FileValueAccessorDirective } from '../../../../../shared/utils/file-value-accessor.directive';
import { FileValidator } from '../../../../../shared/utils/require-file.validator';
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
describe('FileValueInputComponent', () => {
let component: FileValueInputComponent;
let fixture: ComponentFixture<FileValueInputComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [FileValueInputComponent, FileValueAccessorDirective, FileValidator],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FileValueInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should not show a validation error if the input field was left untouched but left empty', () => {
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
});
it('should show a validation error if the input field was touched but left empty', () => {
const input = fixture.debugElement.query(By.css('input'));
input.triggerEventHandler('blur', null);
fixture.detectChanges();
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import { Component, Optional } from '@angular/core';
import { ValueInputComponent } from '../value-input.component';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../../process-form.component';
/**
* Represents the user inputted value of a file parameter
*/
@Component({
selector: 'ds-file-value-input',
templateUrl: './file-value-input.component.html',
styleUrls: ['./file-value-input.component.scss'],
viewProviders: [ { provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] } ]
})
export class FileValueInputComponent extends ValueInputComponent<File> {
/**
* The current value of the file
*/
fileObject: File;
setFile(files) {
this.fileObject = files.length > 0 ? files[0] : undefined;
this.updateValue.emit(this.fileObject);
}
}

View File

@@ -0,0 +1,7 @@
<div [ngSwitch]="parameter?.type">
<ds-string-value-input *ngSwitchCase="parameterTypes.STRING" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-string-value-input>
<ds-string-value-input *ngSwitchCase="parameterTypes.OUTPUT" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-string-value-input>
<ds-date-value-input *ngSwitchCase="parameterTypes.DATE" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-date-value-input>
<ds-file-value-input *ngSwitchCase="parameterTypes.FILE" (updateValue)="updateValue.emit($event)" [index]="index"></ds-file-value-input>
<ds-boolean-value-input *ngSwitchCase="parameterTypes.BOOLEAN" (updateValue)="updateValue.emit($event)" [index]="index"></ds-boolean-value-input>
</div>

View File

@@ -0,0 +1,105 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ParameterValueInputComponent } from './parameter-value-input.component';
import { ScriptParameter } from '../../../scripts/script-parameter.model';
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
import { By } from '@angular/platform-browser';
import { BooleanValueInputComponent } from './boolean-value-input/boolean-value-input.component';
import { StringValueInputComponent } from './string-value-input/string-value-input.component';
import { FileValueInputComponent } from './file-value-input/file-value-input.component';
import { DateValueInputComponent } from './date-value-input/date-value-input.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { FileValueAccessorDirective } from '../../../../shared/utils/file-value-accessor.directive';
import { FileValidator } from '../../../../shared/utils/require-file.validator';
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
describe('ParameterValueInputComponent', () => {
let component: ParameterValueInputComponent;
let fixture: ComponentFixture<ParameterValueInputComponent>;
let booleanParameter;
let stringParameter;
let fileParameter;
let dateParameter;
let outputParameter;
function init() {
booleanParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.BOOLEAN });
stringParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.STRING });
fileParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.FILE });
dateParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.DATE });
outputParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.OUTPUT });
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [
ParameterValueInputComponent,
BooleanValueInputComponent,
StringValueInputComponent,
FileValueInputComponent,
DateValueInputComponent,
FileValueAccessorDirective,
FileValidator
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ParameterValueInputComponent);
component = fixture.componentInstance;
component.parameter = stringParameter;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should show a BooleanValueInputComponent when the parameter type is boolean', () => {
component.parameter = booleanParameter;
fixture.detectChanges();
const valueInput = fixture.debugElement.query(By.directive(BooleanValueInputComponent));
expect(valueInput).toBeTruthy();
});
it('should show a StringValueInputComponent when the parameter type is string', () => {
component.parameter = stringParameter;
fixture.detectChanges();
const valueInput = fixture.debugElement.query(By.directive(StringValueInputComponent));
expect(valueInput).toBeTruthy();
});
it('should show a FileValueInputComponent when the parameter type is file', () => {
component.parameter = fileParameter;
fixture.detectChanges();
const valueInput = fixture.debugElement.query(By.directive(FileValueInputComponent));
expect(valueInput).toBeTruthy();
});
it('should show a DateValueInputComponent when the parameter type is date', () => {
component.parameter = dateParameter;
fixture.detectChanges();
const valueInput = fixture.debugElement.query(By.directive(DateValueInputComponent));
expect(valueInput).toBeTruthy();
});
it('should show a StringValueInputComponent when the parameter type is output', () => {
component.parameter = outputParameter;
fixture.detectChanges();
const valueInput = fixture.debugElement.query(By.directive(StringValueInputComponent));
expect(valueInput).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output } from '@angular/core';
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
import { ScriptParameter } from '../../../scripts/script-parameter.model';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../process-form.component';
/**
* Component that renders the correct parameter value input based the script parameter's type
*/
@Component({
selector: 'ds-parameter-value-input',
templateUrl: './parameter-value-input.component.html',
styleUrls: ['./parameter-value-input.component.scss'],
viewProviders: [ { provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] } ]
})
export class ParameterValueInputComponent {
@Input() index: number;
/**
* The current script parameter
*/
@Input() parameter: ScriptParameter;
@Input() initialValue: any;
/**
* Emits the value of the input when its updated
*/
@Output() updateValue: EventEmitter<any> = new EventEmitter();
/**
* The available script parameter types
*/
parameterTypes = ScriptParameterType;
}

View File

@@ -0,0 +1,7 @@
<input required #string="ngModel" type="text" name="string-value-{{index}}" class="form-control" id="string-value-{{index}}" [ngModel]="value" (ngModelChange)="setValue($event)"/>
<div *ngIf="string.invalid && (string.dirty || string.touched)"
class="alert alert-danger validation-error">
<div *ngIf="string.errors.required">
{{'process.new.parameter.string.required' | translate}}
</div>
</div>

View File

@@ -0,0 +1,72 @@
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule, NgForm } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { StringValueInputComponent } from './string-value-input.component';
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
describe('StringValueInputComponent', () => {
let component: StringValueInputComponent;
let fixture: ComponentFixture<StringValueInputComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [StringValueInputComponent],
providers: [
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StringValueInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should not show a validation error if the input field was left untouched but left empty', () => {
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
});
it('should show a validation error if the input field was touched but left empty', fakeAsync(() => {
component.value = '';
fixture.detectChanges();
tick();
const input = fixture.debugElement.query(By.css('input'));
input.triggerEventHandler('blur', null);
fixture.detectChanges();
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeTruthy();
}));
it('should not show a validation error if the input field was touched but not left empty', fakeAsync(() => {
component.value = 'testValue';
fixture.detectChanges();
tick();
const input = fixture.debugElement.query(By.css('input'));
input.triggerEventHandler('blur', null);
fixture.detectChanges();
const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
}));
});

View File

@@ -0,0 +1,32 @@
import { Component, Optional, Input } from '@angular/core';
import { ValueInputComponent } from '../value-input.component';
import { ControlContainer, NgForm } from '@angular/forms';
import { controlContainerFactory } from '../../../process-form.component';
/**
* Represents the user inputted value of a string parameter
*/
@Component({
selector: 'ds-string-value-input',
templateUrl: './string-value-input.component.html',
styleUrls: ['./string-value-input.component.scss'],
viewProviders: [ { provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]] } ]
})
export class StringValueInputComponent extends ValueInputComponent<string> {
/**
* The current value of the string
*/
value: string;
@Input() initialValue;
ngOnInit() {
this.value = this.initialValue;
}
setValue(value) {
this.value = value;
this.updateValue.emit(value)
}
}

View File

@@ -0,0 +1,12 @@
import { EventEmitter, Input, Output } from '@angular/core';
/**
* Abstract class that represents value input components
*/
export abstract class ValueInputComponent<T> {
@Input() index: number;
/**
* Used by the subclasses to emit the value when it's updated
*/
@Output() updateValue: EventEmitter<T> = new EventEmitter<T>()
}

View File

@@ -0,0 +1,11 @@
<div class="form-group" *ngIf="script">
<label>{{'process.new.select-parameters' | translate}}</label>
<ds-parameter-select
*ngFor="let value of parameterValues; let i = index; let last = last"
[parameters]="script.parameters"
[parameterValue]="value"
[removable]="!last"
[index]="i"
(removeParameter)="removeParameter(i)"
(changeParameter)="updateParameter($event, i)"></ds-parameter-select>
</div>

View File

@@ -0,0 +1,64 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ProcessParametersComponent } from './process-parameters.component';
import { ProcessParameter } from '../../processes/process-parameter.model';
import { By } from '@angular/platform-browser';
import { ParameterSelectComponent } from './parameter-select/parameter-select.component';
import { FormsModule } from '@angular/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Script } from '../../scripts/script.model';
import { ScriptParameter } from '../../scripts/script-parameter.model';
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
describe('ProcessParametersComponent', () => {
let component: ProcessParametersComponent;
let fixture: ComponentFixture<ProcessParametersComponent>;
let parameterValues;
let script;
function init() {
const param1 = new ScriptParameter();
const param2 = new ScriptParameter();
script = Object.assign(new Script(), { parameters: [param1, param2] });
parameterValues = [
Object.assign(new ProcessParameter(), { name: '-a', value: 'bla' }),
Object.assign(new ProcessParameter(), { name: '-b', value: '123' }),
Object.assign(new ProcessParameter(), { name: '-c', value: 'value' }),
]
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [ProcessParametersComponent, ParameterSelectComponent],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProcessParametersComponent);
component = fixture.componentInstance;
component.script = script;
component.parameterValues = parameterValues;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render a ParameterSelectComponent for each parameter value of the component', () => {
const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent));
expect(selectComponents.length).toBe(parameterValues.length);
});
});

View File

@@ -0,0 +1,109 @@
import { Component, EventEmitter, Input, OnChanges, Optional, Output, SimpleChanges } from '@angular/core';
import { Script } from '../../scripts/script.model';
import { ProcessParameter } from '../../processes/process-parameter.model';
import { hasValue } from '../../../shared/empty.util';
import { ControlContainer, NgForm } from '@angular/forms';
import { ScriptParameter } from '../../scripts/script-parameter.model';
import { controlContainerFactory } from '../process-form.component';
/**
* Component that represents the selected list of parameters for a script
*/
@Component({
selector: 'ds-process-parameters',
templateUrl: './process-parameters.component.html',
styleUrls: ['./process-parameters.component.scss'],
viewProviders: [{
provide: ControlContainer,
useFactory: controlContainerFactory,
deps: [[new Optional(), NgForm]]
}]
})
export class ProcessParametersComponent implements OnChanges {
/**
* The currently selected script
*/
@Input() script: Script;
@Input() initialParams: ProcessParameter[];
/**
* Emits the parameter values when they're updated
*/
@Output() updateParameters: EventEmitter<ProcessParameter[]> = new EventEmitter();
/**
* The current parameter values
*/
parameterValues: ProcessParameter[];
ngOnInit() {
if (hasValue(this.initialParams)) {
this.parameterValues = this.initialParams;
}
}
/**
* Makes sure the parameters are reset when the script changes
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void {
if (changes.script) {
this.initParameters()
}
}
/**
* Empties the parameter values
* Initializes the first parameter value
*/
initParameters() {
if (hasValue(this.initialParams)) {
this.parameterValues = this.initialParams;
} else {
this.parameterValues = [];
this.initializeParameter();
}
}
/**
* Updates a single parameter value using its new value and index
* Adds a new parameter when the last of the parameter values is changed
* @param processParameter The new value of the parameter
* @param index The index of the parameter
*/
updateParameter(processParameter: ProcessParameter, index: number) {
this.parameterValues[index] = processParameter;
if (index === this.parameterValues.length - 1) {
this.addParameter();
}
this.updateParameters.emit(this.parameterValues.filter((param: ProcessParameter) => hasValue(param.name)));
}
/**
* Removes a parameter value from the list
* @param index The index of the parameter to remove
*/
removeParameter(index: number) {
this.parameterValues = this.parameterValues.filter((value, i) => i !== index);
}
/**
* Initializes parameter values based on the selected script
*/
initializeParameter() {
if (hasValue(this.script)) {
this.parameterValues = this.script.parameters
.filter((param) => param.mandatory)
.map(
(parameter: ScriptParameter) => Object.assign(new ProcessParameter(), { name: parameter.name })
);
}
this.addParameter();
}
/**
* Adds an empty parameter value to the end of the list
*/
addParameter() {
this.parameterValues = [...this.parameterValues, new ProcessParameter()];
}
}