/*-
* ============LICENSE_START=======================================================
* SDC
* ================================================================================
* Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
* Modifications copyright (c) 2018 Nokia
* ================================================================================
*/
import * as _ from "lodash";
import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactoryResolver } from '@angular/core'
import {ValidationConfiguration} from "app/models";
import {IUiElementChangeEvent} from "../form-components/ui-element-base.component";
import {UiElementInputComponent} from "../form-components/input/ui-element-input.component";
import {UiElementPopoverInputComponent} from "../form-components/popover-input/ui-element-popover-input.component";
import {UiElementIntegerInputComponent} from "../form-components/integer-input/ui-element-integer-input.component";
import {UiElementDropDownComponent, DropdownValue} from "../form-components/dropdown/ui-element-dropdown.component";
import {PROPERTY_DATA, PROPERTY_TYPES} from "../../../../utils/constants";
import {UiElementValidValuesInputComponent} from "../form-components/valid-values-input/ui-element-valid-values-input.component";
import {UiElementRangeInputComponent} from "../form-components/range-input/ui-element-range-input.component";
enum DynamicElementComponentCreatorIdentifier {
STRING,
INTEGER,
FLOAT,
BOOLEAN,
SUBNETPOOLID,
ENUM,
LIST,
DEFAULT,
TIMESTAMP,
RANGE,
VALID_VALUES
}
@Component({
selector: 'dynamic-element',
// Span - if constraints not empty
template: `
`,
styleUrls: ['./dynamic-element.component.less'],
entryComponents: [
UiElementInputComponent,
UiElementDropDownComponent,
UiElementPopoverInputComponent,
UiElementIntegerInputComponent,
UiElementRangeInputComponent,
UiElementValidValuesInputComponent
]
})
export class DynamicElementComponent {
@ViewChild('target', { read: ViewContainerRef }) target: any;
@Input() type: any;
@Input() operator: any;
@Input() childType: any;
@Input() name: string;
@Input() testId: string;
@Input() readonly:boolean;
@Input() constraints: Array;
@Input() path:string;//optional param. used only for for subnetpoolid type
@Input() declared:boolean;
@Input() value: any;
@Output() valueChange: EventEmitter = new EventEmitter();
@Output('elementChanged') emitter: EventEmitter = new EventEmitter();
cmpRef: ComponentRef;
private isViewInitialized: boolean = false;
private elementCreatorIdentifier: DynamicElementComponentCreatorIdentifier;
validation = ValidationConfiguration.validation;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler,
private el: ElementRef) {
}
updateComponent() {
if (!this.isViewInitialized) {
return;
}
// Factory to create component based on type or other property attributes.
const prevElementCreatorIdentifier: DynamicElementComponentCreatorIdentifier = this.elementCreatorIdentifier;
switch(true) {
case this.path && this.path.toUpperCase().indexOf("SUBNETPOOLID") !== -1 && this.operator != 'valid_values':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.SUBNETPOOLID;
break;
case this.getValidValues() !== undefined && this.getValidValues() !== null:
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.ENUM;
break;
case this.operator === 'length' || this.operator === 'min_length' || this.operator === 'max_length':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
break;
case this.type === 'integer' && this.operator != 'valid_values' && this.operator != 'in_range':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
break;
case this.type === 'float' && this.operator != 'valid_values' && this.operator != 'in_range':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT;
break;
case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1 && this.operator != 'valid_values' && this.operator != 'in_range':
case this.type === 'string' && this.operator != 'valid_values' && this.operator != 'in_range' && this.operator != 'length' && this.operator != 'min_length' && this.operator != 'max_length':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING;
break;
case this.type === PROPERTY_TYPES.TIMESTAMP && this.operator != 'valid_values' && this.operator != 'in_range':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.TIMESTAMP;
break;
case this.type === 'boolean' && this.operator != 'valid_values':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN;
break;
case this.type === 'range' || this.operator === 'in_range':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.RANGE;
break;
case this.operator === 'valid_values':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.VALID_VALUES;
break;
case this.type === 'map':
this.createElementCreatorIdentifierForChild();
break;
default:
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT;
}
// In case the dynamic element creator is changed, then destroy old and build new.
if (this.declared || this.elementCreatorIdentifier !== prevElementCreatorIdentifier) {
if (this.cmpRef) {
this.cmpRef.destroy();
}
this.createComponentByIdentifier();
}
// Update attributes in base element class
if (this.cmpRef) {
this.cmpRef.instance.name = this.name;
this.cmpRef.instance.type = this.type;
this.cmpRef.instance.testId = this.testId;
this.cmpRef.instance.value = this.value;
this.cmpRef.instance.readonly = this.readonly;
}
}
createElementCreatorIdentifierForChild():void{
switch(this.childType) {
case 'integer':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
break;
case 'float':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT;
break;
case 'string':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING;
break;
case PROPERTY_TYPES.TIMESTAMP:
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.TIMESTAMP;
break;
case 'boolean':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN;
break;
case 'range':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.RANGE;
break;
case 'valid-values':
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.VALID_VALUES;
break;
default:
this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT;
}
}
createComponentByIdentifier() {
// if(!this.constraints || this.declared){
switch(this.elementCreatorIdentifier) {
case DynamicElementComponentCreatorIdentifier.SUBNETPOOLID:
if(this.name.toUpperCase().indexOf("SUBNETPOOLID") == -1){//if it's an item of subnetpoolid list get the parent name
let pathArray = this.path.split("#");
this.name = pathArray[pathArray.length - 2];
}
this.createComponent(UiElementPopoverInputComponent);
break;
case DynamicElementComponentCreatorIdentifier.ENUM:
this.createComponent(UiElementDropDownComponent);
let validVals:Array = [...this.getValidValues()].map(val => new DropdownValue(val, val));
if (this.type === 'float' || this.type === 'integer') {
this.value = this.value && Number(this.value);
validVals = _.map(
validVals,
(val) => new DropdownValue(Number(val.value), val.value)
);
}
this.cmpRef.instance.values = validVals;
break;
case DynamicElementComponentCreatorIdentifier.INTEGER:
this.createComponent(UiElementIntegerInputComponent);
this.cmpRef.instance.pattern = this.validation.validationPatterns.integer;
break;
case DynamicElementComponentCreatorIdentifier.FLOAT:
this.createComponent(UiElementIntegerInputComponent);
this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/.source;
break;
case DynamicElementComponentCreatorIdentifier.STRING:
this.createComponent(UiElementInputComponent);
break;
case DynamicElementComponentCreatorIdentifier.TIMESTAMP:
this.createComponent(UiElementInputComponent);
break;
case DynamicElementComponentCreatorIdentifier.RANGE:
this.createComponent(UiElementRangeInputComponent);
break;
case DynamicElementComponentCreatorIdentifier.VALID_VALUES:
this.createComponent(UiElementValidValuesInputComponent);
this.cmpRef.instance.type = this.type;
break;
case DynamicElementComponentCreatorIdentifier.BOOLEAN:
this.createComponent(UiElementDropDownComponent);
// Build drop down values
let tmp = [];
tmp.push(new DropdownValue(true,'TRUE'));
tmp.push(new DropdownValue(false,'FALSE'));
this.cmpRef.instance.values = tmp;
try {
if(typeof this.value === 'string'){
this.value = JSON.parse(this.value);
}
} catch (err) {
this.value = null;
}
break;
case DynamicElementComponentCreatorIdentifier.DEFAULT:
default:
this.createComponent(UiElementInputComponent);
console.error("ERROR: No ui-models component to handle type: " + this.type);
}
// }
// //There are consraints
// else {
// this.createComponent(UiElementDropDownComponent);
// // Build drop down values
// let items = [];
// this.constraints.forEach( (element) => {
// items.push(new DropdownValue(element,element));
// });
// items.push(new DropdownValue(this.value,this.value, true, true));
// this.cmpRef.instance.values = items;
// }
// Subscribe to change event of of ui-models-element-basic and fire event to change the value
this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); });
this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); });
}
getValidValues(): Array {
let validVals;
_.forEach(this.constraints, constraint => {
validVals = validVals || constraint.validValues;
});
return validVals;
}
createComponent(ComponentToCreate:any):void {
let factory = this.componentFactoryResolver.resolveComponentFactory(ComponentToCreate);
this.cmpRef = this.target.createComponent(factory);
}
ngOnChanges() {
this.updateComponent();
}
ngAfterContentInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}