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