[SDC] rebase 1710 code
[sdc.git] / catalog-ui / src / app / ng2 / components / properties-table / dynamic-property / dynamic-property.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  */
20
21 import {Component, Input, Output, EventEmitter} from "@angular/core";
22 import { PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedPropertyType, SchemaPropertyGroupModel, DataTypeModel } from "app/models";
23 import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils';
24 import { PropertiesUtils } from "app/ng2/pages/properties-assignment/properties.utils";
25 import { DataTypeService } from "../../../services/data-type.service";
26 import { trigger, state, style, transition, animate } from '@angular/core';
27
28
29 @Component({
30     selector: 'dynamic-property',
31     templateUrl: './dynamic-property.component.html',
32     styleUrls: ['./dynamic-property.component.less'],
33     animations: [trigger('fadeIn', [transition(':enter', [style({ opacity: '0' }), animate('.7s ease-out', style({ opacity: '1' }))])])]
34 })
35 export class DynamicPropertyComponent {
36
37     derivedPropertyTypes = DerivedPropertyType; //http://stackoverflow.com/questions/35835984/how-to-use-a-typescript-enum-value-in-an-angular2-ngswitch-statement
38     propType: DerivedPropertyType;
39     propPath: string;
40     isPropertyFEModel: boolean;
41     nestedLevel: number;
42
43     @Input() canBeDeclared: boolean;
44     @Input() property: PropertyFEModel | DerivedFEProperty;
45     @Input() expandedChildId: string;
46     @Input() selectedPropertyId: string;
47     @Input() propertyNameSearchText: string;
48     @Input() readonly: boolean;
49
50     @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>();
51     @Output() expandChild: EventEmitter<string> = new EventEmitter<string>();
52     @Output() checkProperty: EventEmitter<string> = new EventEmitter<string>();
53     @Output() deleteItem: EventEmitter<string> = new EventEmitter<string>();
54     @Output() clickOnPropertyRow: EventEmitter<PropertyFEModel | DerivedFEProperty> = new EventEmitter<PropertyFEModel | DerivedFEProperty>();
55     @Output() mapKeyChanged: EventEmitter<string> = new EventEmitter<string>();
56     @Output() addChildPropsToParent: EventEmitter<Array<DerivedFEProperty>> = new EventEmitter<Array<DerivedFEProperty>>();
57
58
59     constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) {
60     }
61
62     ngOnInit() {
63         this.isPropertyFEModel = this.property instanceof PropertyFEModel;
64         this.propType = this.property.derivedDataType;
65         this.propPath = (this.property instanceof PropertyFEModel) ? this.property.name : this.property.propertiesName;
66         this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length;
67     }
68
69
70     onClickPropertyRow = (property, event) => {
71         // Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined
72         event && event.stopPropagation && event.stopPropagation();
73         this.clickOnPropertyRow.emit(property);
74     }
75
76
77     expandChildById = (id: string) => {
78         this.expandedChildId = id;
79         this.expandChild.emit(id);
80     }
81
82     checkedChange = (propName: string) => {
83         this.checkProperty.emit(propName);
84     }
85
86     hasChildren = (): number => {
87         return (this.property.valueObj && typeof this.property.valueObj == 'object') ? Object.keys(this.property.valueObj).length : 0;
88     }
89
90     createNewChildProperty = (): void => {
91
92         let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined);
93         if (this.property instanceof PropertyFEModel) {
94             this.addChildProps(newProps, this.property.name);
95         } else {
96             this.addChildPropsToParent.emit(newProps);
97         }
98     }
99
100     addChildProps = (newProps: Array<DerivedFEProperty>, childPropName: string) => {
101
102         if (this.property instanceof PropertyFEModel) {
103             let insertIndex: number = this.property.getIndexOfChild(childPropName) + this.property.getCountOfChildren(childPropName); //insert after parent prop and existing children
104             this.property.flattenedChildren.splice(insertIndex, 0, ...newProps); //using ES6 spread operator
105             this.expandChildById(newProps[0].propertiesName);
106
107
108             if(!newProps[0].schema.property.isSimpleType){
109                 angular.forEach(newProps, (prop:DerivedFEProperty):void => { //Update parent PropertyFEModel with value for each child, including nested props
110                     (<PropertyFEModel>this.property).childPropUpdated(prop);
111                 },this);
112                 //grab the cumulative value for the new item from parent PropertyFEModel and assign that value to DerivedFEProp[0] (which is the list or map parent with UUID of the set we just added)
113                 let parentNames = (<PropertyFEModel>this.property).getParentNamesArray(newProps[0].propertiesName, []);
114                 newProps[0].valueObj = _.get(this.property.valueObj, parentNames.join('.'));
115                 this.valueChanged.emit(this.property.name);
116             }
117         }
118     }
119
120     childValueChanged = (property: DerivedFEProperty) => { //value of child property changed
121
122         if (this.property instanceof PropertyFEModel) { // will always be the case
123             this.property.childPropUpdated(property);
124             this.dataTypeService.checkForCustomBehavior(this.property);
125             this.valueChanged.emit(this.property.name);
126         }
127     }
128
129     deleteListOrMapItem = (item: DerivedFEProperty) => {
130         if (this.property instanceof PropertyFEModel) {
131             this.removeValueFromParent(item);
132             this.property.flattenedChildren.splice(this.property.getIndexOfChild(item.propertiesName), this.property.getCountOfChildren(item.propertiesName));
133             this.expandChildById(item.propertiesName);
134         }
135     }
136
137     removeValueFromParent = (item: DerivedFEProperty, target?: any) => {
138         if (this.property instanceof PropertyFEModel) {
139             let itemParent = (item.parentName == this.property.name) ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName);
140
141             if (item.derivedDataType == DerivedPropertyType.MAP) {
142                 let oldKey = item.mapKey;
143                 if (target && typeof target.value == 'string') { //allow saving empty string
144                     let replaceKey:string = target.value;
145                     if(Object.keys(itemParent.valueObj).indexOf(replaceKey) > -1){//the key is exists
146                         target.setCustomValidity('This key is already exists.');
147                         return;
148                     }else {
149                         target.setCustomValidity('');
150                         _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]);
151                         item.mapKey = replaceKey;
152                     }
153                 }
154                 delete itemParent.valueObj[oldKey];
155             } else {
156                 let itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName);
157                 itemParent.valueObj.splice(itemIndex, 1);
158             }
159
160             if (itemParent instanceof PropertyFEModel) { //direct child
161                 this.valueChanged.emit(this.property.name);
162             } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc)
163                 this.childValueChanged(itemParent);
164             }
165         }
166     }
167
168     preventInsertItem = (property:DerivedFEProperty):boolean => {
169         if(property.type == PROPERTY_TYPES.MAP && Object.keys(property.valueObj).indexOf('') > -1 ){
170             return true;
171         }
172         return false;
173     }
174
175 }