re base code
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties-assignment.page.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 * as _ from "lodash";
22 import {Component, ViewChild, Inject, TemplateRef} from "@angular/core";
23 import { PropertiesService } from "../../services/properties.service";
24 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
25 import { ResourceType } from "app/utils";
26 import {ComponentServiceNg2} from "../../services/component-services/component.service";
27 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
28 import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models";
29 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
30 import {WorkspaceMode, EVENTS} from "../../../utils/constants";
31 import {EventListenerService} from "app/services/event-listener-service"
32 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
33 import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
34 import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/properties-table.component";
35 import {HierarchyNavService} from "./services/hierarchy-nav.service";
36 import {PropertiesUtils} from "./services/properties.utils";
37 import {ComponentModeService} from "../../services/component-services/component-mode.service";
38 import {ModalService} from "../../services/modal.service";
39 import {Tabs, Tab} from "../../components/ui/tabs/tabs.component";
40 import {InputsUtils} from "./services/inputs.utils";
41 import { InstanceFeDetails } from "../../../models/instance-fe-details";
42 import { SdcUiComponents } from "sdc-ui/lib/angular";
43 //import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service";
44 import { IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config";
45 import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
46
47 @Component({
48     templateUrl: './properties-assignment.page.component.html',
49     styleUrls: ['./properties-assignment.page.component.less']
50 })
51 export class PropertiesAssignmentComponent {
52     title = "Properties & Inputs";
53
54     component: ComponentData;
55     componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
56
57     propertiesNavigationData = [];
58     instancesNavigationData = [];
59
60     instanceFePropertiesMap:InstanceFePropertiesMap;
61     inputs: Array<InputFEModel> = [];
62     instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = [];
63     searchQuery: string;
64     propertyStructureHeader: string;
65
66     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
67     selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null;
68     checkedPropertiesCount: number = 0;
69
70     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
71     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
72     displayClearSearch = false;
73     searchPropertyName:string;
74     currentMainTab:Tab;
75     isInputsTabSelected:boolean;
76     isPropertiesTabSelected:boolean;
77     isReadonly:boolean;
78     resourceIsReadonly:boolean;
79     loadingInstances:boolean = false;
80     loadingInputs:boolean = false;
81     loadingProperties:boolean = false;
82     changedData:Array<PropertyFEModel|InputFEModel>;
83     hasChangedData:boolean;
84     isValidChangedData:boolean;
85     savingChangedData:boolean;
86     stateChangeStartUnregister:Function;
87
88     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
89     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
90     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
91    
92     constructor(private propertiesService: PropertiesService,
93                 private hierarchyNavService: HierarchyNavService,
94                 private propertiesUtils:PropertiesUtils,
95                 private inputsUtils:InputsUtils,
96                 private componentServiceNg2:ComponentServiceNg2,
97                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
98                 @Inject("$stateParams") _stateParams,
99                 @Inject("$scope") private $scope:ng.IScope,
100                 @Inject("$state") private $state:ng.ui.IStateService,
101                 @Inject("Notification") private Notification:any,
102                 private componentModeService:ComponentModeService,
103                 private ModalService:ModalService,
104                 private EventListenerService:EventListenerService,
105                 private ModalServiceSdcUI: SdcUiComponents.ModalService) {
106
107         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
108
109         /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time
110         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
111         this.component = _stateParams.component;
112         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
113         this.updateViewMode();
114
115         this.changedData = [];
116         this.updateHasChangedData();
117         this.isValidChangedData = true;
118     }
119
120     ngOnInit() {
121         console.log("==>" + this.constructor.name + ": ngOnInit");
122         this.loadingInputs = true;
123         this.loadingInstances = true;
124         this.loadingProperties = true;
125         this.componentServiceNg2
126             .getComponentInputs(this.component)
127             .subscribe(response => {
128                 _.forEach(response.inputs, (input: InputBEModel) => {
129                     const newInput: InputFEModel = new InputFEModel(input);
130                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
131                     this.inputs.push(newInput); //only push items that were declared via SDC
132                 });
133                 this.loadingInputs = false;
134
135             }, error => {}); //ignore error
136         this.componentServiceNg2
137             .getComponentResourcePropertiesData(this.component)
138             .subscribe(response => {
139                 this.instances = [];
140                 this.instances.push(...response.componentInstances);
141                 this.instances.push(...response.groupInstances);
142                 this.instances.push(...response.policies);
143
144                 _.forEach(this.instances, (instance) => {
145                     this.instancesNavigationData.push(instance);
146                     this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived};
147                 });
148                 this.loadingInstances = false;
149                 if (this.instancesNavigationData[0] == undefined) {
150                     this.loadingProperties = false;
151                 }
152                 this.selectFirstInstanceByDefault();
153             }, error => {}); //ignore error
154
155         this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
156             // stop if has changed properties
157             if (this.hasChangedData) {
158                 event.preventDefault();
159                 this.showUnsavedChangesAlert().then(() => {
160                     this.$state.go(toState, toParams);
161                 }, () => {});
162             }
163         });
164     };
165
166     ngOnDestroy() {
167         this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
168         this.stateChangeStartUnregister();
169     }
170
171     selectFirstInstanceByDefault = () => {
172         if (this.instancesNavigationData[0] !== undefined) {
173             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
174         }
175     };
176
177     updateViewMode = () => {
178         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
179     }
180
181     onCheckout = (component:ComponentData) => {
182         this.component = component;
183         this.updateViewMode();
184     }
185
186
187     onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
188         // stop if has changed properties
189         if (this.hasChangedData) {
190             this.showUnsavedChangesAlert().then((resolve)=> {
191                 this.changeSelectedInstance(instance)
192             }, (reject) => {
193             });
194             return;
195         }
196         this.changeSelectedInstance(instance);
197     };
198
199     changeSelectedInstance =  (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
200         this.selectedInstanceData = instance;
201         this.loadingProperties = true;
202         if (instance instanceof ComponentInstance) {
203             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
204             if (this.isInput(instance.originType)) {
205                 this.componentInstanceServiceNg2
206                     .getComponentInstanceInputs(this.component, instance)
207                     .subscribe(response => {
208                         instanceBePropertiesMap[instance.uniqueId] = response;
209                         this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
210                         this.loadingProperties = false;
211                     }, error => {
212                     }); //ignore error
213             } else {
214                 this.componentInstanceServiceNg2
215                     .getComponentInstanceProperties(this.component, instance.uniqueId)
216                     .subscribe(response => {
217                         instanceBePropertiesMap[instance.uniqueId] = response;
218                         this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
219                         this.loadingProperties = false;
220                     }, error => {
221                     }); //ignore error
222             }
223
224             this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
225         } else if (instance instanceof GroupInstance) {
226             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
227             this.componentInstanceServiceNg2
228                 .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
229                 .subscribe((response) => {
230                     instanceBePropertiesMap[instance.uniqueId] = response;
231                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
232                     this.loadingProperties = false;
233                 });
234         } else if (instance instanceof PolicyInstance) {
235             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
236             this.componentInstanceServiceNg2
237                 .getComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
238                 .subscribe((response) => {
239                     instanceBePropertiesMap[instance.uniqueId] = response;
240                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
241                     this.loadingProperties = false;
242                 });
243         } else {
244             this.loadingProperties = false;
245         }
246
247         if (this.searchPropertyName) {
248             this.clearSearch();
249         }
250         //clear selected property from the navigation
251         this.selectedFlatProperty = new SimpleFlatProperty();
252         this.propertiesNavigationData = [];
253     };
254
255     /**
256      * Entry point handling response from server
257      */
258     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
259         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
260         this.checkedPropertiesCount = 0;
261     };
262
263
264     /*** VALUE CHANGE EVENTS ***/
265     dataChanged = (item:PropertyFEModel|InputFEModel) => {
266         let itemHasChanged;
267         if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
268             itemHasChanged = item.hasValueObjChanged();
269         } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
270             itemHasChanged = item.hasDefaultValueChanged();
271         }
272
273         const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
274         if (itemHasChanged) {
275             if (dataChangedIdx === -1) {
276                 this.changedData.push(item);
277             }
278         } else {
279             if (dataChangedIdx !== -1) {
280                 this.changedData.splice(dataChangedIdx, 1);
281             }
282         }
283
284         if (this.isPropertiesTabSelected) {
285             this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
286         } else if (this.isInputsTabSelected) {
287             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
288         }
289         this.updateHasChangedData();
290     };
291
292
293     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
294
295     /**
296      * Handle select node in navigation area, and select the row in table
297      */
298     onPropertySelectedUpdate = ($event) => {
299         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
300         this.selectedFlatProperty = $event;
301         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
302         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
303     };
304
305     /**
306      * When user select row in table, this will prepare the hirarchy object for the tree.
307      */
308     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
309         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
310         let property = propertyRowSelectedEvent.propertyModel;
311         let instanceName = propertyRowSelectedEvent.instanceName;
312         this.propertyStructureHeader = null;
313
314         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
315         if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
316             let simpleFlatProperty:Array<SimpleFlatProperty>;
317             if (property instanceof PropertyFEModel) {
318                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
319             } else if (property instanceof DerivedFEProperty) {
320                 // Need to find parent PropertyFEModel
321                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
322                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
323                 });
324                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
325             }
326             this.propertiesNavigationData = simpleFlatProperty;
327         }
328
329         // Update the header in the navigation tree with property name.
330         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
331
332         // Set selected property in table
333         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
334         this.hierarchyNavTabs.triggerTabChange('Property Structure');
335     };
336
337
338     selectInstanceRow = ($event) => {//get instance name
339         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
340             return instance.name == $event;
341         });
342         this.hierarchyNavTabs.triggerTabChange('Composition');
343     };
344
345     tabChanged = (event) => {
346         // stop if has changed properties
347         if (this.hasChangedData) {
348             this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
349             this.showUnsavedChangesAlert().then((proceed) => {
350                 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
351             }, ()=> {
352             });
353             return;
354         }
355
356         console.log("==>" + this.constructor.name + ": tabChanged " + event);
357         this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
358         this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
359         this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
360         this.propertyStructureHeader = null;
361         this.searchQuery = '';
362     };
363
364
365
366     /*** DECLARE PROPERTIES/INPUTS ***/
367     declareProperties = (): void => {
368         console.log("==>" + this.constructor.name + ": declareProperties");
369
370         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
371         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
372         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
373         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
374         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
375
376         angular.forEach(instancesIds, (instanceId: string): void => {
377             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
378             if (selectedInstanceData instanceof ComponentInstance) {
379                 if (!this.isInput(selectedInstanceData.originType)) {
380                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
381                 } else {
382                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
383                 }
384             } else if (selectedInstanceData instanceof GroupInstance) {
385                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
386             } else if (selectedInstanceData instanceof PolicyInstance) {
387                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
388             }
389         });
390
391         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
392
393         this.componentServiceNg2
394             .createInput(this.component, inputsToCreate)
395             .subscribe(response => {
396                 this.setInputTabIndication(response.length);
397                 this.checkedPropertiesCount = 0;
398                 _.forEach(response, (input: InputBEModel) => {
399                     let newInput: InputFEModel = new InputFEModel(input);
400                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
401                     this.inputs.push(newInput);
402                     this.updatePropertyValueAfterDeclare(newInput);
403                 });
404             }, error => {}); //ignore error
405     };
406
407     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
408         return new Promise((resolve, reject) => {
409             if (!this.isValidChangedData) {
410                 reject('Changed data is invalid - cannot save!');
411                 return;
412             }
413             if (!this.changedData.length) {
414                 resolve([]);
415                 return;
416             }
417
418             // make request and its handlers
419             let request;
420             let handleSuccess, handleError;
421             if (this.isPropertiesTabSelected) {
422                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
423                     changedProp = <PropertyFEModel>changedProp;
424                     const propBE = new PropertyBEModel(changedProp);
425                     propBE.value = changedProp.getJSONValue();
426                     return propBE;
427                 });
428
429                 if (this.selectedInstanceData instanceof ComponentInstance) {
430                     if (this.isInput(this.selectedInstanceData.originType)) {
431                         request = this.componentInstanceServiceNg2
432                             .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties);
433                         handleSuccess = (response) => {
434                             // reset each changed property with new value and remove it from changed properties list
435                             response.forEach((resInput) => {
436                                 const changedProp = <PropertyFEModel>this.changedData.shift();
437                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
438                             });
439                             console.log('updated instance inputs:', response);
440                         };
441                     } else {
442                         request = this.componentInstanceServiceNg2
443                             .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties)
444                         handleSuccess = (response) => {
445                             // reset each changed property with new value and remove it from changed properties list
446                             response.forEach((resProp) => {
447                                 const changedProp = <PropertyFEModel>this.changedData.shift();
448                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
449                             });
450                             resolve(response);
451                             console.log("updated instance properties: ", response);
452                         };
453                     }
454                 } else if (this.selectedInstanceData instanceof GroupInstance) {
455                     request = this.componentInstanceServiceNg2
456                         .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
457                     handleSuccess = (response) => {
458                         // reset each changed property with new value and remove it from changed properties list
459                         response.forEach((resProp) => {
460                             const changedProp = <PropertyFEModel>this.changedData.shift();
461                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
462                         });
463                         resolve(response);
464                         console.log("updated group instance properties: ", response);
465                     };
466                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
467                     request = this.componentInstanceServiceNg2
468                         .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
469                     handleSuccess = (response) => {
470                         // reset each changed property with new value and remove it from changed properties list
471                         response.forEach((resProp) => {
472                             const changedProp = <PropertyFEModel>this.changedData.shift();
473                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
474                         });
475                         resolve(response);
476                         console.log("updated policy instance properties: ", response);
477                     };
478                 }
479             } else if (this.isInputsTabSelected) {
480                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
481                     changedInput = <InputFEModel>changedInput;
482                     const inputBE = new InputBEModel(changedInput);
483                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
484                     return inputBE;
485                 });
486                 request = this.componentServiceNg2
487                     .updateComponentInputs(this.component, changedInputs);
488                 handleSuccess = (response) => {
489                     // reset each changed property with new value and remove it from changed properties list
490                     response.forEach((resInput) => {
491                         const changedInput = <InputFEModel>this.changedData.shift();
492                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
493                     });
494                     console.log("updated the component inputs and got this response: ", response);
495                 }
496             }
497
498             this.savingChangedData = true;
499             request.subscribe(
500                 (response) => {
501                     this.savingChangedData = false;
502                     handleSuccess && handleSuccess(response);
503                     this.updateHasChangedData();
504                     resolve(response);
505                 },
506                 (error) => {
507                     this.savingChangedData = false;
508                     handleError && handleError(error);
509                     this.updateHasChangedData();
510                     reject(error);
511                 }
512             );
513         });
514     };
515
516     reverseChangedData = ():void => {
517         // make reverse item handler
518         let handleReverseItem;
519         if (this.isPropertiesTabSelected) {
520             handleReverseItem = (changedItem) => {
521                 changedItem = <PropertyFEModel>changedItem;
522                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
523             };
524         } else if (this.isInputsTabSelected) {
525             handleReverseItem = (changedItem) => {
526                 changedItem = <InputFEModel>changedItem;
527                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
528             };
529         }
530
531         this.changedData.forEach(handleReverseItem);
532         this.changedData = [];
533         this.updateHasChangedData();
534     };
535
536     updateHasChangedData = ():boolean => {
537         const curHasChangedData:boolean = (this.changedData.length > 0);
538         if (curHasChangedData !== this.hasChangedData) {
539             this.hasChangedData = curHasChangedData;
540             if(this.hasChangedData) {
541                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
542             } else {
543                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
544             }
545         } 
546         return this.hasChangedData;
547     };
548
549     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
550         this.saveChangedData().then(
551             () => {
552                 this.Notification.success({
553                     message: 'Successfully saved changes',
554                     title: 'Saved'
555                 });
556                 if(onSuccessFunction) onSuccessFunction();
557             },
558             () => {
559                 this.Notification.error({
560                     message: 'Failed to save changes!',
561                     title: 'Failure'
562                 });
563                 if(onError) onError();
564             }
565         );
566     };
567
568     showUnsavedChangesAlert = ():Promise<any> => {
569         let modalTitle:string;
570         if (this.isPropertiesTabSelected) {
571             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
572         } else if (this.isInputsTabSelected) {
573             modalTitle = `Unsaved inputs for ${this.component.name}`;
574         }
575
576         return new Promise<any>((resolve, reject) => {
577             const modal = this.ModalServiceSdcUI.openCustomModal(
578                 {
579                     title: modalTitle,
580                     size: 'sm',
581                     type: 'custom',
582                     testId: "id",
583                     
584                     buttons: [
585                         {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()},
586                         {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
587                         {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
588                 ] as IModalButtonComponent[]
589             }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
590         });
591
592     }
593
594     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
595         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
596             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
597                 return feProperty.name == input.relatedPropertyName;
598             });
599             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
600             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
601             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
602             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
603         }
604     }
605
606     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
607     updateCheckedPropertyCount = (increment: boolean): void => {
608         this.checkedPropertiesCount += (increment) ? 1 : -1;
609         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
610     };
611
612     setInputTabIndication = (numInputs: number): void => {
613         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
614     };
615
616     resetUnsavedChangesForInput = (input:InputFEModel) => {
617         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
618         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
619         this.updateHasChangedData();
620     }
621
622     deleteInput = (input: InputFEModel) => {
623         //reset any unsaved changes to the input before deleting it
624         this.resetUnsavedChangesForInput(input);
625
626         console.log("==>" + this.constructor.name + ": deleteInput");
627         let inputToDelete = new InputBEModel(input);
628
629         this.componentServiceNg2
630             .deleteInput(this.component, inputToDelete)
631             .subscribe(response => {
632                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
633
634                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
635                 this.changeSelectedInstance(this.selectedInstanceData);
636                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
637
638                 // if (instanceFeProperties) {
639                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
640                 //         return prop.name == input.propertyName;
641                 //     });
642
643                 //     if (propToEnable) {
644                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
645                 //         propToEnable.setNonDeclared(response.inputPath);
646                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
647                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
648                 //     }
649                 // }
650             }, error => {}); //ignore error
651     };
652
653
654
655     /*** SEARCH RELATED FUNCTIONS ***/
656     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
657         let instanceBePropertiesMap:InstanceBePropertiesMap;
658         this.componentServiceNg2
659             .filterComponentInstanceProperties(this.component, filterData)
660             .subscribe(response => {
661
662                 this.processInstancePropertiesResponse(response, false);
663                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
664                 this.searchPropertyName = filterData.propertyName;//mark in table
665                 this.hierarchyNavTabs.triggerTabChange('Composition');
666                 this.propertiesNavigationData = [];
667                 this.displayClearSearch = true;
668             }, error => {}); //ignore error
669
670     };
671
672     clearSearch = () => {
673         this.instancesNavigationData = this.instances;
674         this.searchPropertyName = "";
675         this.hierarchyPropertiesDisplayOptions.searchText = "";
676         this.displayClearSearch = false;
677         this.advanceSearch.clearAll();
678         this.searchQuery = '';
679     };
680
681     clickOnClearSearch = () => {
682         this.clearSearch();
683         this.selectFirstInstanceByDefault();
684         this.hierarchyNavTabs.triggerTabChange('Composition');
685     };
686
687     private isInput = (instanceType:string):boolean =>{
688         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
689     }
690
691 }