5e3214d888a1ebbef6293310f598d4eef868358c
[sdc.git] / catalog-ui / src / app / ng2 / components / ui / dynamic-element / dynamic-element.component.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  * Modifications copyright (c) 2018 Nokia
20  * ================================================================================
21  */
22
23 import * as _ from "lodash";
24 import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactoryResolver } from '@angular/core'
25 import {ValidationConfiguration, PropertyFEModel} from "app/models";
26 import {IUiElementChangeEvent} from "../form-components/ui-element-base.component";
27 import {UiElementInputComponent} from "../form-components/input/ui-element-input.component";
28 import {UiElementPopoverInputComponent} from "../form-components/popover-input/ui-element-popover-input.component";
29 import {UiElementIntegerInputComponent} from "../form-components/integer-input/ui-element-integer-input.component";
30 import {UiElementDropDownComponent, DropdownValue} from "../form-components/dropdown/ui-element-dropdown.component";
31 import {PROPERTY_DATA} from "../../../../utils/constants";
32
33 enum DynamicElementComponentCreatorIdentifier {
34     STRING,
35     INTEGER,
36     FLOAT,
37     BOOLEAN,
38     SUBNETPOOLID,
39     ENUM,
40     LIST,
41     DEFAULT
42 }
43
44 @Component({
45     selector: 'dynamic-element',
46     // Span - if constraints not empty
47     template: `<div #target></div>`,
48     styleUrls: ['./dynamic-element.component.less'],
49     entryComponents: [
50         UiElementInputComponent,
51         UiElementDropDownComponent,
52         UiElementPopoverInputComponent,
53         UiElementIntegerInputComponent
54     ]
55 })
56 export class DynamicElementComponent {
57
58     @ViewChild('target', { read: ViewContainerRef }) target: any;
59     @Input() type: any;
60     @Input() childType: any;
61     @Input() name: string;
62     @Input() testId: string;
63     @Input() readonly:boolean;
64     @Input() constraints: Array<any>;
65     @Input() path:string;//optional param. used only for for subnetpoolid type
66     @Input() declared:boolean;
67
68     @Input() value: any;
69     @Output() valueChange: EventEmitter<any> = new EventEmitter<any>();
70     @Output('elementChanged') emitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>();
71
72     cmpRef: ComponentRef<any>;
73     private isViewInitialized: boolean = false;
74     private elementCreatorIdentifier: DynamicElementComponentCreatorIdentifier;
75     validation = ValidationConfiguration.validation;
76
77     constructor(
78         
79         private componentFactoryResolver: ComponentFactoryResolver,
80         private compiler: Compiler,
81         private el: ElementRef) {
82            
83             
84     }
85
86     updateComponent() {
87         if (!this.isViewInitialized) {
88             return;
89         }
90         
91         // Factory to create component based on type or other property attributes.
92         const prevElementCreatorIdentifier: DynamicElementComponentCreatorIdentifier = this.elementCreatorIdentifier;
93         switch(true) {
94             case this.path && this.path.toUpperCase().indexOf("SUBNETPOOLID") !== -1:
95                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.SUBNETPOOLID;
96                 break;
97             case this.getValidValues() !== undefined && this.getValidValues() !== null:
98                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.ENUM;
99                 break;
100             case this.type === 'integer':
101                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
102                 break;
103             case this.type === 'float':
104                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT;
105                 break;
106             case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1:
107             case this.type === 'string':
108                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING;
109                 break;
110             case this.type === 'boolean':
111                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN;
112                 break;
113           case this.type === 'map':
114                 this.createElementCreatorIdentifierForChild();
115                 break;
116             default:
117                 this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT;
118         }
119
120         // In case the dynamic element creator is changed, then destroy old and build new.
121         if (this.declared || this.elementCreatorIdentifier !== prevElementCreatorIdentifier) {
122             if (this.cmpRef) {
123                 this.cmpRef.destroy();
124             }
125             this.createComponentByIdentifier();
126         }
127
128         // Update attributes in base element class
129         if (this.cmpRef) {
130             this.cmpRef.instance.name = this.name;
131             this.cmpRef.instance.type = this.type;
132             this.cmpRef.instance.testId = this.testId;
133             this.cmpRef.instance.value = this.value;
134             this.cmpRef.instance.readonly = this.readonly;
135         }
136     }
137
138     createElementCreatorIdentifierForChild():void{
139       switch(this.childType) {
140         case 'integer':
141           this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
142           break;
143         case 'float':
144           this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT;
145           break;
146         case 'string':
147           this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING;
148           break;
149         case 'boolean':
150           this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN;
151           break;
152         default:
153           this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT;
154       }
155     }
156
157     createComponentByIdentifier() {
158         // if(!this.constraints || this.declared){
159             switch(this.elementCreatorIdentifier) {
160                 case DynamicElementComponentCreatorIdentifier.SUBNETPOOLID:
161                     if(this.name.toUpperCase().indexOf("SUBNETPOOLID") == -1){//if it's an item of subnetpoolid list get the parent name
162                         let pathArray = this.path.split("#");
163                         this.name = pathArray[pathArray.length - 2];
164                     }
165                     this.createComponent(UiElementPopoverInputComponent);
166                     break;
167                 case DynamicElementComponentCreatorIdentifier.ENUM:
168                     this.createComponent(UiElementDropDownComponent);
169                     let validVals:Array<DropdownValue> = [...this.getValidValues()].map(val => new DropdownValue(val, val));
170                     if (this.type === 'float' || this.type === 'integer') {
171                         this.value = this.value && Number(this.value);
172                         validVals = _.map(
173                             validVals,
174                             (val) => new DropdownValue(Number(val.value), val.value)
175                         );
176                     }
177                     this.cmpRef.instance.values = validVals;
178                     break;
179                 case DynamicElementComponentCreatorIdentifier.INTEGER:
180                     this.createComponent(UiElementIntegerInputComponent);
181                     this.cmpRef.instance.pattern = this.validation.validationPatterns.integer;
182                     break;
183
184                 case DynamicElementComponentCreatorIdentifier.FLOAT:
185                     this.createComponent(UiElementIntegerInputComponent);
186                     this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/.source;
187                     break;
188
189                 case DynamicElementComponentCreatorIdentifier.STRING:
190                     this.createComponent(UiElementInputComponent);
191                     break;
192
193                 case DynamicElementComponentCreatorIdentifier.BOOLEAN:
194                     this.createComponent(UiElementDropDownComponent);
195
196                     // Build drop down values
197                     let tmp = [];
198                     tmp.push(new DropdownValue(true,'TRUE'));
199                     tmp.push(new DropdownValue(false,'FALSE'));
200                     this.cmpRef.instance.values = tmp;
201                     try {
202                         if(typeof this.value === 'string'){
203                             this.value = JSON.parse(this.value);
204                         }
205                     } catch (err) {
206                         this.value = null;
207                     }
208                     break;
209
210                 case DynamicElementComponentCreatorIdentifier.DEFAULT:
211                 default:
212                     this.createComponent(UiElementInputComponent);
213                     console.log("ERROR: No ui-models component to handle type: " + this.type);
214             }
215         // }
216         // //There are consraints
217         // else {
218             
219         //     this.createComponent(UiElementDropDownComponent);
220
221         //     // Build drop down values
222         //     let items = [];
223         //     this.constraints.forEach( (element) => {
224         //         items.push(new DropdownValue(element,element));
225         //     });
226
227         //     items.push(new DropdownValue(this.value,this.value, true, true));
228         //     this.cmpRef.instance.values = items;
229             
230             
231             
232         // }
233
234         // Subscribe to change event of of ui-models-element-basic and fire event to change the value
235         this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); });
236         this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); });
237     }
238
239     getValidValues(): Array<string> {
240         let validVals;
241         _.forEach(this.constraints, constraint => {
242             validVals = validVals || constraint.validValues;
243         });
244         return validVals;
245     }
246
247     createComponent(ComponentToCreate:any):void {
248         let factory = this.componentFactoryResolver.resolveComponentFactory(ComponentToCreate);
249         this.cmpRef = this.target.createComponent(factory);
250     }
251
252     ngOnChanges() {
253         this.updateComponent();
254     }
255
256     ngAfterContentInit() {
257         this.isViewInitialized = true;
258         this.updateComponent();
259     }
260
261     ngOnDestroy() {
262         if (this.cmpRef) {
263             this.cmpRef.destroy();
264         }
265     }
266
267 }