Support Capability Properties
[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 {
25     PropertyFEModel,
26     InstanceFePropertiesMap,
27     InstanceBePropertiesMap,
28     InstancePropertiesAPIMap,
29     Component as ComponentData,
30     FilterPropertiesAssignmentData,
31     ModalModel,
32     ButtonModel,
33     Capability,
34     ToscaPresentationData
35 } from "app/models";
36 import { ResourceType } from "app/utils";
37 import {ComponentServiceNg2} from "../../services/component-services/component.service";
38 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
39 import {
40     InputBEModel,
41     InputFEModel,
42     ComponentInstance,
43     GroupInstance,
44     PolicyInstance,
45     PropertyBEModel,
46     DerivedFEProperty,
47     SimpleFlatProperty,
48     CapabilitiesGroup
49 } from "app/models";
50 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
51 import {WorkspaceMode, EVENTS} from "../../../utils/constants";
52 import {EventListenerService} from "app/services/event-listener-service"
53 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
54 import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
55 import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/properties-table.component";
56 import {HierarchyNavService} from "./services/hierarchy-nav.service";
57 import {PropertiesUtils} from "./services/properties.utils";
58 import {ComponentModeService} from "../../services/component-services/component-mode.service";
59 import {ModalService} from "../../services/modal.service";
60 import {Tabs, Tab} from "../../components/ui/tabs/tabs.component";
61 import {InputsUtils} from "./services/inputs.utils";
62 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
63 import {DeclareListComponent} from "./declare-list/declare-list.component";
64 import { InstanceFeDetails } from "../../../models/instance-fe-details";
65 import { SdcUiComponents } from "sdc-ui/lib/angular";
66 //import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service";
67 import { IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config";
68 import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
69 import {Observable} from "rxjs";
70 import { DataTypeService } from "app/ng2/services/data-type.service";
71 import { DataTypeModel } from "app/models";
72 import { PROPERTY_DATA, PROPERTY_TYPES } from "app/utils";
73 import { PropertyDeclareAPIModel} from "app/models";
74
75 const SERVICE_SELF_TITLE = "SELF";
76 @Component({
77     templateUrl: './properties-assignment.page.component.html',
78     styleUrls: ['./properties-assignment.page.component.less']
79 })
80 export class PropertiesAssignmentComponent {
81     title = "Properties & Inputs";
82
83     component: ComponentData;
84     componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
85
86     propertiesNavigationData = [];
87     instancesNavigationData = [];
88
89     instanceFePropertiesMap:InstanceFePropertiesMap;
90     inputs: Array<InputFEModel> = [];
91     policies: Array<PolicyInstance> = [];
92     instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = [];
93     searchQuery: string;
94     propertyStructureHeader: string;
95
96     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
97     selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null;
98     checkedPropertiesCount: number = 0;
99     checkedChildPropertiesCount: number = 0;
100
101     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
102     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
103     displayClearSearch = false;
104     searchPropertyName:string;
105     currentMainTab:Tab;
106     isInputsTabSelected:boolean;
107     isPropertiesTabSelected:boolean;
108     isPoliciesTabSelected:boolean;
109     isReadonly:boolean;
110     resourceIsReadonly:boolean;
111     loadingInstances:boolean = false;
112     loadingInputs:boolean = false;
113     loadingPolicies:boolean = false;
114     loadingProperties:boolean = false;
115     changedData:Array<PropertyFEModel|InputFEModel>;
116     hasChangedData:boolean;
117     isValidChangedData:boolean;
118     savingChangedData:boolean;
119     stateChangeStartUnregister:Function;
120     serviceBePropertiesMap: InstanceBePropertiesMap;
121     serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap;
122     selectedInstance_FlattenCapabilitiesList: Array<Capability>;
123
124     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
125     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
126     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
127    
128     constructor(private propertiesService: PropertiesService,
129                 private hierarchyNavService: HierarchyNavService,
130                 private propertiesUtils:PropertiesUtils,
131                 private inputsUtils:InputsUtils,
132                 private componentServiceNg2:ComponentServiceNg2,
133                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
134                 @Inject("$stateParams") _stateParams,
135                 @Inject("$scope") private $scope:ng.IScope,
136                 @Inject("$state") private $state:ng.ui.IStateService,
137                 @Inject("Notification") private Notification:any,
138                 private componentModeService:ComponentModeService,
139                 private ModalService:ModalService,
140                 private EventListenerService:EventListenerService,
141                 private ModalServiceSdcUI: SdcUiComponents.ModalService) {
142
143         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
144
145         /* 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
146         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
147         this.component = _stateParams.component;
148         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
149         this.updateViewMode();
150
151         this.changedData = [];
152         this.updateHasChangedData();
153         this.isValidChangedData = true;
154     }
155
156     ngOnInit() {
157         console.log("==>" + this.constructor.name + ": ngOnInit");
158         this.loadingInputs = true;
159         this.loadingPolicies = true;
160         this.loadingInstances = true;
161         this.loadingProperties = true;
162         this.componentServiceNg2
163             .getComponentInputsWithProperties(this.component)
164             .subscribe(response => {
165                 _.forEach(response.inputs, (input: InputBEModel) => {
166                     const newInput: InputFEModel = new InputFEModel(input);
167                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
168                     this.inputs.push(newInput); //only push items that were declared via SDC
169                 });
170                 this.loadingInputs = false;
171
172             });
173         this.componentServiceNg2
174             .getComponentResourcePropertiesData(this.component)
175             .subscribe(response => {
176                 this.loadingPolicies = false;
177                 this.instances = [];
178                 this.instances.push(...response.componentInstances);
179                 this.instances.push(...response.groupInstances);
180
181                 _.forEach(response.policies, (policy: any) => {
182                     const newPolicy: InputFEModel = new InputFEModel(policy);
183                     this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
184                     this.policies.push(policy);
185                 });
186
187                 // add the service self instance to the top of the list.
188                 const serviceInstance = new ComponentInstance();
189                 serviceInstance.name = SERVICE_SELF_TITLE;
190                 serviceInstance.uniqueId = this.component.uniqueId;
191                 this.instances.unshift(serviceInstance);
192
193                 _.forEach(this.instances, (instance) => {
194                     this.instancesNavigationData.push(instance);
195                     this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived};
196                 });
197                 this.loadingInstances = false;
198                 if (this.instancesNavigationData[0] == undefined) {
199                     this.loadingProperties = false;
200                 }
201                 this.selectFirstInstanceByDefault();
202             });
203
204         this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
205             // stop if has changed properties
206             if (this.hasChangedData) {
207                 event.preventDefault();
208                 this.showUnsavedChangesAlert().then(() => {
209                     this.$state.go(toState, toParams);
210                 }, () => {});
211             }
212         });
213     };
214
215     ngOnDestroy() {
216         this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
217         this.stateChangeStartUnregister();
218     }
219
220     selectFirstInstanceByDefault = () => {
221         if (this.instancesNavigationData[0] !== undefined) {
222             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
223         }
224     };
225
226     updateViewMode = () => {
227         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
228     }
229
230     onCheckout = (component:ComponentData) => {
231         this.component = component;
232         this.updateViewMode();
233     }
234
235     isSelf = ():boolean => {
236         return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
237     }
238
239     getServiceProperties(){
240         this.loadingProperties = false;
241         this.componentServiceNg2
242             .getServiceProperties(this.component)
243             .subscribe(response => {
244                 this.serviceBePropertiesMap = new InstanceBePropertiesMap();
245                 this.serviceBePropertiesMap[this.component.uniqueId] = response;
246                 this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false);
247                 this.loadingProperties = false;
248             }, error => {
249                 this.loadingProperties = false;
250             });
251     }
252
253     onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
254         // stop if has changed properties
255         if (this.hasChangedData) {
256             this.showUnsavedChangesAlert().then((resolve)=> {
257                 this.changeSelectedInstance(instance)
258             }, (reject) => {
259             });
260             return;
261         }
262         this.changeSelectedInstance(instance);
263     };
264
265     changeSelectedInstance =  (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
266         this.selectedInstanceData = instance;
267         this.loadingProperties = true;
268         if (instance instanceof ComponentInstance) {
269             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
270             this.selectedInstance_FlattenCapabilitiesList = instance.capabilities ? CapabilitiesGroup.getFlattenedCapabilities(instance.capabilities) : [];
271             if (this.isInput(instance.originType)) {
272                 this.componentInstanceServiceNg2
273                     .getComponentInstanceInputs(this.component, instance)
274                     .subscribe(response => {
275                         instanceBePropertiesMap[instance.uniqueId] = response;
276                         this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
277                         this.processInstanceCapabilitiesPropertiesResponse(false);
278                         this.loadingProperties = false;
279                     }, error => {
280                     }); //ignore error
281             } else if (this.isSelf()) {
282                 this.getServiceProperties();
283             } else {
284                 this.componentInstanceServiceNg2
285                     .getComponentInstanceProperties(this.component, instance.uniqueId)
286                     .subscribe(response => {
287                         instanceBePropertiesMap[instance.uniqueId] = response;
288                         this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
289                         this.processInstanceCapabilitiesPropertiesResponse(false);
290                         this.loadingProperties = false;
291                     }, error => {
292                     }); //ignore error
293             }
294
295             this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
296         } else if (instance instanceof GroupInstance) {
297             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
298             this.componentInstanceServiceNg2
299                 .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
300                 .subscribe((response) => {
301                     instanceBePropertiesMap[instance.uniqueId] = response;
302                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
303                     this.loadingProperties = false;
304                 });
305         } else if (instance instanceof PolicyInstance) {
306             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
307             this.componentInstanceServiceNg2
308                 .getComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
309                 .subscribe((response) => {
310                     instanceBePropertiesMap[instance.uniqueId] = response;
311                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
312                     this.loadingProperties = false;
313                 });
314         } else {
315             this.loadingProperties = false;
316         }
317
318         if (this.searchPropertyName) {
319             this.clearSearch();
320         }
321         //clear selected property from the navigation
322         this.selectedFlatProperty = new SimpleFlatProperty();
323         this.propertiesNavigationData = [];
324     };
325
326     /**
327      * Entry point handling response from server
328      */
329     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
330         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
331         this.checkedPropertiesCount = 0;
332         this.checkedChildPropertiesCount = 0;
333     };
334
335     processInstanceCapabilitiesPropertiesResponse = (originTypeIsVF: boolean) => {
336         let selectedComponentInstanceData = <ComponentInstance>(this.selectedInstanceData);
337         let currentUniqueId = this.selectedInstanceData.uniqueId;
338         this.serviceBeCapabilitiesPropertiesMap = new InstanceBePropertiesMap();
339         let isCapabilityOwnedByInstance: boolean;
340         this.serviceBeCapabilitiesPropertiesMap[currentUniqueId] = _.reduce(
341             this.selectedInstance_FlattenCapabilitiesList,
342             (result, cap: Capability) => {
343                 isCapabilityOwnedByInstance = cap.ownerId === currentUniqueId ||
344                     selectedComponentInstanceData.isServiceProxy() && cap.ownerId === selectedComponentInstanceData.sourceModelUid;
345                 if (cap.properties && isCapabilityOwnedByInstance) {
346                     _.forEach(cap.properties, prop => {
347                         if (!prop.origName) {
348                             prop.origName = prop.name;
349                             prop.name = cap.name + '_' + prop.name;//for display. (before save - the name returns to its orig value: prop.name)
350                         }
351                     });
352                     return result.concat(cap.properties);
353                 }
354                 return result;
355             }, []);
356         let instanceFECapabilitiesPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(this.serviceBeCapabilitiesPropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
357         //update FECapabilitiesProperties with their origName according to BeCapabilitiesProperties
358         _.forEach(instanceFECapabilitiesPropertiesMap[currentUniqueId], prop => {
359             prop.origName = _.find(this.serviceBeCapabilitiesPropertiesMap[currentUniqueId], p => p.uniqueId === prop.uniqueId).origName;
360         });
361         //concatenate capabilitiesProps to all props list
362         this.instanceFePropertiesMap[currentUniqueId] = (this.instanceFePropertiesMap[currentUniqueId] || []).concat(instanceFECapabilitiesPropertiesMap[currentUniqueId]);
363         this.checkedPropertiesCount = 0;
364     };
365
366     isCapabilityProperty = (prop: PropertyBEModel) => {
367         return _.find(this.selectedInstance_FlattenCapabilitiesList, cap => cap.uniqueId === prop.parentUniqueId);
368     };
369
370     /*** VALUE CHANGE EVENTS ***/
371     dataChanged = (item:PropertyFEModel|InputFEModel) => {
372         let itemHasChanged;
373         if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
374             itemHasChanged = item.hasValueObjChanged();
375         } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
376             itemHasChanged = item.hasDefaultValueChanged();
377         } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
378             itemHasChanged = item.hasDefaultValueChanged();
379         }
380
381         const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
382         if (itemHasChanged) {
383             if (dataChangedIdx === -1) {
384                 this.changedData.push(item);
385             }
386         } else {
387             if (dataChangedIdx !== -1) {
388                 this.changedData.splice(dataChangedIdx, 1);
389             }
390         }
391
392         if (this.isPropertiesTabSelected) {
393             this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
394         } else if (this.isInputsTabSelected || this.isPoliciesTabSelected) {
395             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
396         }
397         this.updateHasChangedData();
398     };
399
400
401     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
402
403     /**
404      * Handle select node in navigation area, and select the row in table
405      */
406     onPropertySelectedUpdate = ($event) => {
407         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
408         this.selectedFlatProperty = $event;
409         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
410         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
411     };
412
413     /**
414      * When user select row in table, this will prepare the hirarchy object for the tree.
415      */
416     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
417         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
418         let property = propertyRowSelectedEvent.propertyModel;
419         let instanceName = propertyRowSelectedEvent.instanceName;
420         this.propertyStructureHeader = null;
421
422         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
423         if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
424             let simpleFlatProperty:Array<SimpleFlatProperty>;
425             if (property instanceof PropertyFEModel) {
426                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
427             } else if (property instanceof DerivedFEProperty) {
428                 // Need to find parent PropertyFEModel
429                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
430                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
431                 });
432                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
433             }
434             this.propertiesNavigationData = simpleFlatProperty;
435         }
436
437         // Update the header in the navigation tree with property name.
438         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
439
440         // Set selected property in table
441         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
442         this.hierarchyNavTabs.triggerTabChange('Property Structure');
443     };
444
445
446     selectInstanceRow = ($event) => {//get instance name
447         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
448             return instance.name == $event;
449         });
450         this.hierarchyNavTabs.triggerTabChange('Composition');
451     };
452
453     tabChanged = (event) => {
454         // stop if has changed properties
455         if (this.hasChangedData) {
456             this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
457             this.showUnsavedChangesAlert().then((proceed) => {
458                 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
459             }, ()=> {
460             });
461             return;
462         }
463
464         console.log("==>" + this.constructor.name + ": tabChanged " + event);
465         this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
466         this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
467         this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
468         this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
469         this.propertyStructureHeader = null;
470         this.searchQuery = '';
471     };
472
473
474
475     /*** DECLARE PROPERTIES/INPUTS ***/
476     declareProperties = (): void => {
477         console.log("==>" + this.constructor.name + ": declareProperties");
478
479         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
480         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
481         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
482         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
483         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
484
485         angular.forEach(instancesIds, (instanceId: string): void => {
486             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
487             if (selectedInstanceData instanceof ComponentInstance) {
488                 if (!this.isInput(selectedInstanceData.originType)) {
489                     // convert Property FE model -> Property BE model, extract only checked
490                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
491                 } else {
492                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
493                 }
494             } else if (selectedInstanceData instanceof GroupInstance) {
495                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
496             } else if (selectedInstanceData instanceof PolicyInstance) {
497                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
498             }
499         });
500
501         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
502
503         //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
504         inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
505             (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
506                 _.filter(
507                     inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
508                     (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
509                 )
510             );
511         inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
512             inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
513             prop => !this.isCapabilityProperty(prop)
514         );
515         if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
516             delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
517         }
518
519         let isCapabilityPropertyChanged = false;
520         _.forEach(
521             inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
522             (prop: PropertyBEModel) => {
523                 prop.name = prop.origName || prop.name;
524                 if (this.isCapabilityProperty(prop)) {
525                     isCapabilityPropertyChanged = true;
526                 }
527             }
528         );
529
530         this.componentServiceNg2
531             .createInput(this.component, inputsToCreate, this.isSelf())
532             .subscribe(response => {
533                 this.setInputTabIndication(response.length);
534                 this.checkedPropertiesCount = 0;
535                 this.checkedChildPropertiesCount = 0;
536                 _.forEach(response, (input: InputBEModel) => {
537                     let newInput: InputFEModel = new InputFEModel(input);
538                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
539                     this.inputs.push(newInput);
540                     this.updatePropertyValueAfterDeclare(newInput);
541                 });
542                 if (isCapabilityPropertyChanged) {
543                     this.reloadInstanceCapabilities();
544                 }
545             }, error => {}); //ignore error
546     };
547
548     declareListProperties = (): void => {
549         console.log('declareListProperties() - enter');
550
551         // get selected properties
552         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
553         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
554         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
555         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
556         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
557         let propertyNameList: Array<string> = [];
558         let insId :string;
559
560         angular.forEach(instancesIds, (instanceId: string): void => {
561             console.log("instanceId="+instanceId);
562             insId = instanceId;
563             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
564             let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
565
566             if (selectedInstanceData instanceof ComponentInstance) {
567                 if (!this.isInput(selectedInstanceData.originType)) {
568                     // convert Property FE model -> Property BE model, extract only checked
569                     selectedComponentInstancesProperties[instanceId] = checkedProperties;
570                 } else {
571                     selectedComponentInstancesInputs[instanceId] = checkedProperties;
572                 }
573             } else if (selectedInstanceData instanceof GroupInstance) {
574                 selectedGroupInstancesProperties[instanceId] = checkedProperties;
575             } else if (selectedInstanceData instanceof PolicyInstance) {
576                 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
577             }
578
579             angular.forEach(checkedProperties, (property: PropertyBEModel) => {
580                 propertyNameList.push(property.name);
581             });
582         });
583
584         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
585
586         let modalTitle = 'Declare Properties as List Input';
587         const modal = this.ModalService.createCustomModal(new ModalModel(
588             'sm', /* size */
589             modalTitle, /* title */
590             null, /* content */
591             [ /* buttons */
592                 new ButtonModel(
593                     'Save', /* text */
594                     'blue', /* css class */
595                     () => { /* callback */
596                         let content:any = modal.instance.dynamicContent.instance;
597
598                         /* listInput */
599                         let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
600                         let typelist: any = PROPERTY_TYPES.LIST;
601                         let uniID: any = insId;
602                         let boolfalse: any = false;
603                         let schem :any = {
604                             "empty": boolfalse,
605                             "property": {
606                                 "type": content.propertyModel.simpleType,
607                                 "required": boolfalse
608                             }
609                         }
610                         let schemaProp :any = {
611                             "type": content.propertyModel.simpleType,
612                             "required": boolfalse
613                         }
614
615                         reglistInput.description = content.propertyModel.description;
616                         reglistInput.name = content.propertyModel.name;
617                         reglistInput.type = typelist;
618                         reglistInput.schemaType = content.propertyModel.simpleType;
619                         reglistInput.instanceUniqueId = uniID;
620                         reglistInput.uniqueId = uniID;
621                         reglistInput.required =boolfalse;
622                         reglistInput.schema = schem;
623                         reglistInput.schemaProperty = schemaProp;
624
625                         let input = {
626                             componentInstInputsMap: content.inputsToCreate,
627                             listInput: reglistInput
628                         };
629                         console.log("save button clicked. input=", input);
630
631                         this.componentServiceNg2
632                         .createListInput(this.component, input, this.isSelf())
633                         .subscribe(response => {
634                             this.setInputTabIndication(response.length);
635                             this.checkedPropertiesCount = 0;
636                             this.checkedChildPropertiesCount = 0;
637                             _.forEach(response, (input: InputBEModel) => {
638                                 let newInput: InputFEModel = new InputFEModel(input);
639                                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
640                                 this.inputs.push(newInput);
641                                 // create list input does not return updated properties info, so need to reload
642                                 //this.updatePropertyValueAfterDeclare(newInput);
643                                 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
644                                 this.changeSelectedInstance(this.selectedInstanceData);
645
646                                 modal.instance.close();
647                             });
648                         }, error => {}); //ignore error
649             
650                     }
651                     /*, getDisabled: function */
652                 ),
653                 new ButtonModel('Cancel', 'outline grey', () => {
654                     modal.instance.close();
655                 }),
656             ],
657             null /* type */
658         ));
659         // 3rd arg is passed to DeclareListComponent instance
660         this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
661         modal.instance.open();
662         console.log('declareListProperties() - leave');
663     };
664
665     /*** DECLARE PROPERTIES/POLICIES ***/
666     declarePropertiesToPolicies = (): void => {
667         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
668         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
669
670         angular.forEach(instancesIds, (instanceId: string): void => {
671             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
672             if (selectedInstanceData instanceof ComponentInstance) {
673                 if (!this.isInput(selectedInstanceData.originType)) {
674                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
675                 }
676             }
677         });
678
679         let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
680         this.loadingPolicies = true;
681
682         this.componentServiceNg2
683             .createPolicy(this.component, policiesToCreate, this.isSelf())
684             .subscribe(response => {
685                 this.setPolicyTabIndication(response.length);
686                 this.checkedPropertiesCount = 0;
687                 this.displayPoliciesAsDeclared(response);
688                 this.loadingPolicies = false;
689             }); //ignore error
690
691     };
692
693     displayPoliciesAsDeclared = (policies) => {
694         _.forEach(policies, (policy: any) => {
695             let newPolicy: InputFEModel = new InputFEModel(policy);
696             this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
697             newPolicy.relatedPropertyName = policy.name;
698             newPolicy.relatedPropertyValue = policy.value;
699             this.updatePropertyValueAfterDeclare(newPolicy);
700             this.policies.push(policy);
701         });
702     };
703
704
705     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
706         return new Promise((resolve, reject) => {
707             if (!this.isValidChangedData) {
708                 reject('Changed data is invalid - cannot save!');
709                 return;
710             }
711             if (!this.changedData.length) {
712                 resolve([]);
713                 return;
714             }
715
716             // make request and its handlers
717             let request;
718             let handleSuccess, handleError;
719             let changedInputsProperties = [], changedCapabilitiesProperties = [];
720             if (this.isPropertiesTabSelected) {
721                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
722                     changedProp = <PropertyFEModel>changedProp;
723                     const propBE = new PropertyBEModel(changedProp);
724                     propBE.toscaPresentation = new ToscaPresentationData();
725                     propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
726                     propBE.value = changedProp.getJSONValue();
727                     propBE.name = changedProp.origName || changedProp.name;
728                     delete propBE.origName;
729                     return propBE;
730                 });
731                 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
732
733                 if (this.selectedInstanceData instanceof ComponentInstance) {
734                     if (this.isInput(this.selectedInstanceData.originType)) {
735                         changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
736                         if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
737                             request = Observable.forkJoin(
738                                 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
739                                 this.componentInstanceServiceNg2.updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
740                             );
741                         }
742                         else if (changedInputsProperties.length) {
743                             request = this.componentInstanceServiceNg2
744                                 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
745                         }
746                         else if (changedCapabilitiesProperties.length) {
747                             request = this.componentInstanceServiceNg2
748                                 .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
749                         }
750                         handleSuccess = (response) => {
751                             // reset each changed property with new value and remove it from changed properties list
752                             response.forEach((resInput) => {
753                                 const changedProp = <PropertyFEModel>this.changedData.shift();
754                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
755                             });
756                             console.log('updated instance inputs:', response);
757                         };
758                     } else {
759                         if (this.isSelf()) {
760                             request = this.componentServiceNg2.updateServiceProperties(this.component, _.map(changedProperties, cp => {
761                                 delete cp.constraints;
762                                 return cp;
763                             }));
764                         } else {
765                             request = this.componentInstanceServiceNg2
766                                 .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
767                         }
768                         handleSuccess = (response) => {
769                             // reset each changed property with new value and remove it from changed properties list
770                             response.forEach((resProp) => {
771                                 const changedProp = <PropertyFEModel>_.find(this.changedData, changedDataObject => changedDataObject.uniqueId === resProp.uniqueId);
772                                 this.changedData = _.filter(this.changedData, changedDataObject => changedDataObject.uniqueId !== resProp.uniqueId);
773                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
774                             });
775                             resolve(response);
776                             console.log("updated instance properties: ", response);
777                         };
778                     }
779                 } else if (this.selectedInstanceData instanceof GroupInstance) {
780                     request = this.componentInstanceServiceNg2
781                         .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
782                     handleSuccess = (response) => {
783                         // reset each changed property with new value and remove it from changed properties list
784                         response.forEach((resProp) => {
785                             const changedProp = <PropertyFEModel>this.changedData.shift();
786                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
787                         });
788                         resolve(response);
789                         console.log("updated group instance properties: ", response);
790                     };
791                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
792                     request = this.componentInstanceServiceNg2
793                         .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
794                     handleSuccess = (response) => {
795                         // reset each changed property with new value and remove it from changed properties list
796                         response.forEach((resProp) => {
797                             const changedProp = <PropertyFEModel>this.changedData.shift();
798                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
799                         });
800                         resolve(response);
801                         console.log("updated policy instance properties: ", response);
802                     };
803                 }
804             } else if (this.isInputsTabSelected) {
805                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
806                     changedInput = <InputFEModel>changedInput;
807                     const inputBE = new InputBEModel(changedInput);
808                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
809                     return inputBE;
810                 });
811                 request = this.componentServiceNg2
812                     .updateComponentInputs(this.component, changedInputs);
813                 handleSuccess = (response) => {
814                     // reset each changed property with new value and remove it from changed properties list
815                     response.forEach((resInput) => {
816                         const changedInput = <InputFEModel>this.changedData.shift();
817                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
818                     });
819                     console.log("updated the component inputs and got this response: ", response);
820                 }
821             }
822
823             this.savingChangedData = true;
824             request.subscribe(
825                 (response) => {
826                     this.savingChangedData = false;
827                     if (changedCapabilitiesProperties.length) {
828                         this.reloadInstanceCapabilities();
829                     }
830                     handleSuccess && handleSuccess(response);
831                     this.updateHasChangedData();
832                     resolve(response);
833                 },
834                 (error) => {
835                     this.savingChangedData = false;
836                     handleError && handleError(error);
837                     this.updateHasChangedData();
838                     reject(error);
839                 }
840             );
841         });
842     };
843
844     reloadInstanceCapabilities = (): void => {
845         let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
846         this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
847             let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
848                 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
849                     return instance.capabilities;
850                 }
851                 return res;
852             }, new CapabilitiesGroup());
853             (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
854         });
855     };
856
857     reverseChangedData = ():void => {
858         // make reverse item handler
859         let handleReverseItem;
860         if (this.isPropertiesTabSelected) {
861             handleReverseItem = (changedItem) => {
862                 changedItem = <PropertyFEModel>changedItem;
863                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
864                 this.checkedPropertiesCount = 0;
865                 this.checkedChildPropertiesCount = 0;
866             };
867         } else if (this.isInputsTabSelected) {
868             handleReverseItem = (changedItem) => {
869                 changedItem = <InputFEModel>changedItem;
870                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
871             };
872         }
873
874         this.changedData.forEach(handleReverseItem);
875         this.changedData = [];
876         this.updateHasChangedData();
877     };
878
879     updateHasChangedData = ():boolean => {
880         const curHasChangedData:boolean = (this.changedData.length > 0);
881         if (curHasChangedData !== this.hasChangedData) {
882             this.hasChangedData = curHasChangedData;
883             if(this.hasChangedData) {
884                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
885             } else {
886                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
887             }
888         } 
889         return this.hasChangedData;
890     };
891
892     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
893         this.saveChangedData().then(
894             () => {
895                 this.Notification.success({
896                     message: 'Successfully saved changes',
897                     title: 'Saved'
898                 });
899                 if(onSuccessFunction) onSuccessFunction();
900                 if(this.isPropertiesTabSelected){
901                     this.checkedPropertiesCount = 0;
902                     this.checkedChildPropertiesCount = 0;
903                 }
904             },
905             () => {
906                 this.Notification.error({
907                     message: 'Failed to save changes!',
908                     title: 'Failure'
909                 });
910                 if(onError) onError();
911             }
912         );
913     };
914
915     showUnsavedChangesAlert = ():Promise<any> => {
916         let modalTitle:string;
917         if (this.isPropertiesTabSelected) {
918             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
919         } else if (this.isInputsTabSelected) {
920             modalTitle = `Unsaved inputs for ${this.component.name}`;
921         }
922
923         return new Promise<any>((resolve, reject) => {
924             const modal = this.ModalServiceSdcUI.openCustomModal(
925                 {
926                     title: modalTitle,
927                     size: 'sm',
928                     type: 'custom',
929                     testId: "id",
930                     
931                     buttons: [
932                         {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()},
933                         {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
934                         {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
935                 ] as IModalButtonComponent[]
936             }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
937         });
938
939     }
940
941     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
942         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
943             let instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
944             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
945                 return feProperty.uniqueId === input.propertyId &&
946                     (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
947             });
948             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
949             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
950             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
951             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
952         }
953     }
954
955     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
956     updateCheckedPropertyCount = (increment: boolean): void => {
957         this.checkedPropertiesCount += (increment) ? 1 : -1;
958         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
959     };
960
961     updateCheckedChildPropertyCount = (increment: boolean): void => {
962         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
963     };
964
965     setInputTabIndication = (numInputs: number): void => {
966         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
967     };
968
969     setPolicyTabIndication = (numPolicies: number): void => {
970         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
971     };
972
973     resetUnsavedChangesForInput = (input:InputFEModel) => {
974         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
975         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
976         this.updateHasChangedData();
977     }
978
979     deleteInput = (input: InputFEModel) => {
980         //reset any unsaved changes to the input before deleting it
981         this.resetUnsavedChangesForInput(input);
982
983         console.log("==>" + this.constructor.name + ": deleteInput");
984         let inputToDelete = new InputBEModel(input);
985
986         this.componentServiceNg2
987             .deleteInput(this.component, inputToDelete)
988             .subscribe(response => {
989                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
990
991                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
992                 this.changeSelectedInstance(this.selectedInstanceData);
993                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
994
995                 // if (instanceFeProperties) {
996                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
997                 //         return prop.name == input.propertyName;
998                 //     });
999
1000                 //     if (propToEnable) {
1001                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
1002                 //         propToEnable.setNonDeclared(response.inputPath);
1003                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
1004                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
1005                 //     }
1006                 // }
1007             }, error => {}); //ignore error
1008     };
1009
1010     deletePolicy = (policy: PolicyInstance) => {
1011         this.loadingPolicies = true;
1012         this.componentServiceNg2
1013             .deletePolicy(this.component, policy)
1014             .subscribe(response => {
1015                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
1016                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
1017                 this.changeSelectedInstance(this.selectedInstanceData);
1018                 this.loadingPolicies = false;
1019             });
1020     };
1021
1022     deleteProperty = (property: PropertyFEModel) => {
1023         let propertyToDelete = new PropertyFEModel(property);
1024         this.loadingProperties = true;
1025         let feMap = this.instanceFePropertiesMap;
1026         this.componentServiceNg2
1027             .deleteServiceProperty(this.component, propertyToDelete)
1028             .subscribe(response => {
1029                 const props = feMap[this.component.uniqueId];
1030                 props.splice(props.findIndex(p => p.uniqueId === response),1);
1031                 this.loadingProperties = false;
1032             }, error => {
1033                 this.loadingProperties = false;
1034                 console.error(error);
1035             });
1036     };
1037
1038     /*** addProperty ***/
1039     addProperty = () => {
1040         let modalTitle = 'Add Property';
1041         const modal = this.ModalService.createCustomModal(new ModalModel(
1042             'sm',
1043             modalTitle,
1044             null,
1045             [
1046                 new ButtonModel('Save', 'blue', () => {
1047                     modal.instance.dynamicContent.instance.isLoading = true;
1048                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1049                     this.componentServiceNg2.createServiceProperty(this.component, newProperty)
1050                         .subscribe(response => {
1051                             modal.instance.dynamicContent.instance.isLoading = false;
1052                             let newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1053                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1054                             modal.instance.close();
1055                         }, (error) => {
1056                             modal.instance.dynamicContent.instance.isLoading = false;
1057                             this.Notification.error({
1058                                 message: 'Failed to add property:' + error,
1059                                 title: 'Failure'
1060                             });
1061                         });
1062
1063                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1064                 new ButtonModel('Cancel', 'outline grey', () => {
1065                     modal.instance.close();
1066                 }),
1067             ],
1068             null
1069         ));
1070         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1071         modal.instance.open();
1072     };
1073
1074     /*** SEARCH RELATED FUNCTIONS ***/
1075     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1076         let instanceBePropertiesMap:InstanceBePropertiesMap;
1077         this.componentServiceNg2
1078             .filterComponentInstanceProperties(this.component, filterData)
1079             .subscribe(response => {
1080
1081                 this.processInstancePropertiesResponse(response, false);
1082                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1083                 this.searchPropertyName = filterData.propertyName;//mark in table
1084                 this.hierarchyNavTabs.triggerTabChange('Composition');
1085                 this.propertiesNavigationData = [];
1086                 this.displayClearSearch = true;
1087             }, error => {}); //ignore error
1088
1089     };
1090
1091     clearSearch = () => {
1092         this.instancesNavigationData = this.instances;
1093         this.searchPropertyName = "";
1094         this.hierarchyPropertiesDisplayOptions.searchText = "";
1095         this.displayClearSearch = false;
1096         this.advanceSearch.clearAll();
1097         this.searchQuery = '';
1098     };
1099
1100     clickOnClearSearch = () => {
1101         this.clearSearch();
1102         this.selectFirstInstanceByDefault();
1103         this.hierarchyNavTabs.triggerTabChange('Composition');
1104     };
1105
1106     private isInput = (instanceType:string):boolean =>{
1107         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
1108     }
1109
1110 }