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 { 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';
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;
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>>();
59 constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) {
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;
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);
77 expandChildById = (id: string) => {
78 this.expandedChildId = id;
79 this.expandChild.emit(id);
82 checkedChange = (propName: string) => {
83 this.checkProperty.emit(propName);
86 hasChildren = (): number => {
87 return (this.property.valueObj && typeof this.property.valueObj == 'object') ? Object.keys(this.property.valueObj).length : 0;
90 createNewChildProperty = (): void => {
92 let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined);
93 if (this.property instanceof PropertyFEModel) {
94 this.addChildProps(newProps, this.property.name);
96 this.addChildPropsToParent.emit(newProps);
100 addChildProps = (newProps: Array<DerivedFEProperty>, childPropName: string) => {
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);
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);
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);
120 childValueChanged = (property: DerivedFEProperty) => { //value of child property changed
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);
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);
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);
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.');
149 target.setCustomValidity('');
150 _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]);
151 item.mapKey = replaceKey;
154 delete itemParent.valueObj[oldKey];
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);
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);
168 preventInsertItem = (property:DerivedFEProperty):boolean => {
169 if(property.type == PROPERTY_TYPES.MAP && Object.keys(property.valueObj).indexOf('') > -1 ){