2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 import {Component, Input, Output, EventEmitter} from "@angular/core";
22 import { PropertyFEModel, DerivedFEProperty, DerivedPropertyType } from "app/models";
23 import { PROPERTY_TYPES } from 'app/utils';
24 import { DataTypeService } from "../../../../services/data-type.service";
25 import { trigger, state, style, transition, animate } from '@angular/core';
26 import {PropertiesUtils} from "../../../../pages/properties-assignment/services/properties.utils";
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' }))])])]
35 export class DynamicPropertyComponent {
37 derivedPropertyTypes = DerivedPropertyType; //http://stackoverflow.com/questions/35835984/how-to-use-a-typescript-enum-value-in-an-angular2-ngswitch-statement
38 propType: DerivedPropertyType;
40 isPropertyFEModel: boolean;
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 @Input() hasChildren: boolean;
50 @Input() hasDeclareOption:boolean;
52 @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>();
53 @Output() expandChild: EventEmitter<string> = new EventEmitter<string>();
54 @Output() checkProperty: EventEmitter<string> = new EventEmitter<string>();
55 @Output() deleteItem: EventEmitter<string> = new EventEmitter<string>();
56 @Output() clickOnPropertyRow: EventEmitter<PropertyFEModel | DerivedFEProperty> = new EventEmitter<PropertyFEModel | DerivedFEProperty>();
57 @Output() mapKeyChanged: EventEmitter<string> = new EventEmitter<string>();
58 @Output() addChildPropsToParent: EventEmitter<Array<DerivedFEProperty>> = new EventEmitter<Array<DerivedFEProperty>>();
61 constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) {
65 this.isPropertyFEModel = this.property instanceof PropertyFEModel;
66 this.propType = this.property.derivedDataType;
67 this.propPath = (this.property instanceof PropertyFEModel) ? this.property.name : this.property.propertiesName;
68 this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length;
72 onClickPropertyRow = (property, event) => {
73 // Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined
74 event && event.stopPropagation && event.stopPropagation();
75 this.clickOnPropertyRow.emit(property);
79 expandChildById = (id: string) => {
80 this.expandedChildId = id;
81 this.expandChild.emit(id);
84 checkedChange = (propName: string) => {
85 this.checkProperty.emit(propName);
88 getHasChildren = (property:DerivedFEProperty): boolean => {// enter to this function only from base property (PropertyFEModel) and check for child property if it has children
89 return _.filter((<PropertyFEModel>this.property).flattenedChildren,(prop:DerivedFEProperty)=>{
90 return _.startsWith(prop.propertiesName + '#', property.propertiesName);
94 createNewChildProperty = (): void => {
96 let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined);
97 if (this.property instanceof PropertyFEModel) {
98 this.addChildProps(newProps, this.property.name);
100 this.addChildPropsToParent.emit(newProps);
104 addChildProps = (newProps: Array<DerivedFEProperty>, childPropName: string) => {
106 if (this.property instanceof PropertyFEModel) {
107 let insertIndex: number = this.property.getIndexOfChild(childPropName) + this.property.getCountOfChildren(childPropName); //insert after parent prop and existing children
108 this.property.flattenedChildren.splice(insertIndex, 0, ...newProps); //using ES6 spread operator
109 this.expandChildById(newProps[0].propertiesName);
112 if(!newProps[0].schema.property.isSimpleType){
113 if ( newProps[0].mapKey ) {//prevent update the new item value on parent property valueObj and saving on BE if it is map item, it will be updated and saved only after user enter key (when it is list item- the map key is the es type)
114 this.updateMapKeyValueOnMainParent(newProps);
115 if (this.property.getParentNamesArray(newProps[0].propertiesName, []).indexOf('') === -1) {
116 this.valueChanged.emit(this.property.name);
123 updateMapKeyValueOnMainParent(childrenProps: Array<DerivedFEProperty>){
124 if (this.property instanceof PropertyFEModel) {
125 //Update only if all this property parents has key name
126 if (this.property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){
127 angular.forEach(childrenProps, (prop:DerivedFEProperty):void => { //Update parent PropertyFEModel with value for each child, including nested props
128 (<PropertyFEModel>this.property).childPropUpdated(prop);
130 //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)
131 let parentNames = (<PropertyFEModel>this.property).getParentNamesArray(childrenProps[0].propertiesName, []);
132 childrenProps[0].valueObj = _.get(this.property.valueObj, parentNames.join('.'));
137 childValueChanged = (property: DerivedFEProperty) => { //value of child property changed
139 if (this.property instanceof PropertyFEModel) { // will always be the case
140 if (this.property.getParentNamesArray(property.propertiesName, []).indexOf('') === -1) {//If one of the parents is empty key -don't save
141 this.property.childPropUpdated(property);
142 this.dataTypeService.checkForCustomBehavior(this.property);
143 this.valueChanged.emit(this.property.name);
148 deleteListOrMapItem = (item: DerivedFEProperty) => {
149 if (this.property instanceof PropertyFEModel) {
150 this.removeValueFromParent(item);
151 this.property.flattenedChildren.splice(this.property.getIndexOfChild(item.propertiesName), this.property.getCountOfChildren(item.propertiesName));
152 this.expandChildById(item.propertiesName);
156 removeValueFromParent = (item: DerivedFEProperty, target?: any) => {
157 if (this.property instanceof PropertyFEModel) {
158 let itemParent = (item.parentName == this.property.name) ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName);
160 if (item.derivedDataType == DerivedPropertyType.MAP) {
161 let oldKey = item.mapKey;
162 if (target && typeof target.value == 'string') { //allow saving empty string
163 let replaceKey:string = target.value;
164 if (!replaceKey) {//prevent delete map key
167 if(Object.keys(itemParent.valueObj).indexOf(replaceKey) > -1){//the key is exists
168 target.setCustomValidity('This key is already exists.');
171 target.setCustomValidity('');
172 _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]);
173 item.mapKey = replaceKey;
174 //If the map key was empty its valueObj was not updated on its prent property valueObj, and now we should update it.
175 if(!oldKey && !item.schema.property.isSimpleType){
176 //Search this map item children and update these value on parent property valueOBj
177 let mapKeyFlattenChildren:Array<DerivedFEProperty> = _.filter(this.property.flattenedChildren, (prop:DerivedFEProperty) => {
178 return _.startsWith(prop.propertiesName, item.propertiesName);
180 this.updateMapKeyValueOnMainParent(mapKeyFlattenChildren);
184 delete itemParent.valueObj[oldKey];
186 let itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName);
187 itemParent.valueObj.splice(itemIndex, 1);
189 if (item.mapKey) {//prevent going to BE if user tries to delete map item without key (it was not saved in BE)
190 if (itemParent instanceof PropertyFEModel) { //direct child
191 this.valueChanged.emit(this.property.name);
192 } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc)
193 this.childValueChanged(itemParent);
199 preventInsertItem = (property:DerivedFEProperty):boolean => {
200 if(property.type == PROPERTY_TYPES.MAP && Object.keys(property.valueObj).indexOf('') > -1 ){