summaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/pages/properties-assignment
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-ui/src/app/ng2/pages/properties-assignment')
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts3
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html86
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less12
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts329
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts1
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts40
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts83
7 files changed, 418 insertions, 136 deletions
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
index 203c75dd11..907f7638bb 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
@@ -32,6 +32,7 @@ import {DataTypeService} from "../../services/data-type.service";
import {PropertiesAssignmentComponent} from "./properties-assignment.page.component";
import {HierarchyNavService} from "./services/hierarchy-nav.service";
import {PropertiesUtils} from "./services/properties.utils";
+import {InputsUtils} from "./services/inputs.utils";
import {ComponentModeService} from "../../services/component-services/component-mode.service";
@NgModule({
@@ -53,7 +54,7 @@ import {ComponentModeService} from "../../services/component-services/component-
exports: [
PropertiesAssignmentComponent
],
- providers: [PropertiesService, HierarchyNavService, PropertiesUtils, DataTypeService, ComponentModeService]
+ providers: [PropertiesService, HierarchyNavService, PropertiesUtils, InputsUtils, DataTypeService, ComponentModeService]
})
export class PropertiesAssignmentModule {
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
index beea3fe73f..03e9f1fa7e 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
@@ -1,43 +1,50 @@
<div class="properties-assignment-page">
<div class="main-content">
<div class="left-column">
- <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true">
- <tab tabTitle="Properties">
- <properties-table class="properties-table"
- [fePropertiesMap]="instanceFePropertiesMap"
- [feInstanceNamesMap]="componentInstanceNamesMap"
- [searchTerm]="searchQuery"
- [selectedPropertyId]="selectedFlatProperty.path"
- [propertyNameSearchText]="searchPropertyName"
- [readonly]="isReadonly"
- [isLoading]="loadingProperties"
- [hasDeclareOption]="true"
- (valueChanged)="propertyValueChanged($event)"
- (propertySelected)="propertySelected($event)"
- (selectPropertyRow)="selectPropertyRow($event)"
- (selectChildProperty)="selectChildProperty($event)"
- (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)"
- (selectInstanceRow)="selectInstanceRow($event)">
+ <div class="main-tabs-section">
+ <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true">
+ <tab tabTitle="Properties">
+ <properties-table class="properties-table"
+ [fePropertiesMap]="instanceFePropertiesMap"
+ [feInstanceNamesMap]="componentInstanceNamesMap"
+ [searchTerm]="searchQuery"
+ [selectedPropertyId]="selectedFlatProperty.path"
+ [propertyNameSearchText]="searchPropertyName"
+ [readonly]="isReadonly"
+ [isLoading]="loadingProperties || savingChangedData"
+ [hasDeclareOption]="true"
+ (propertyChanged)="dataChanged($event)"
+ (propertySelected)="propertySelected($event)"
+ (selectPropertyRow)="selectPropertyRow($event)"
+ (selectChildProperty)="selectChildProperty($event)"
+ (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)"
+ (selectInstanceRow)="selectInstanceRow($event)">
</properties-table>
- </tab>
- <tab tabTitle="Inputs">
- <inputs-table class="properties-table"
- [readonly]="isReadonly"
- [inputs]="inputs | searchFilter:'name':searchQuery"
- [instanceNamesMap]="componentInstanceNamesMap"
- [isLoading]="loadingInputs"
- (deleteInput)="deleteInput($event)"
- (inputValueChanged)="inputValueChanged($event)"></inputs-table>
- </tab>
- </tabs>
+ </tab>
+ <tab tabTitle="Inputs">
+ <inputs-table class="properties-table"
+ [readonly]="isReadonly"
+ [inputs]="inputs | searchFilter:'name':searchQuery"
+ [instanceNamesMap]="componentInstanceNamesMap"
+ [isLoading]="loadingInputs"
+ (deleteInput)="deleteInput($event)"
+ (inputChanged)="dataChanged($event)">
+ </inputs-table>
+ </tab>
+ </tabs>
+ <div class="main-tabs-section-buttons">
+ <button class="tlv-btn outline blue" [disabled]="!hasChangedData || savingChangedData" (click)="reverseChangedData()" data-tests-id="properties-reverse-button">Discard</button>
+ <button class="tlv-btn blue" [disabled]="!hasChangedData || !isValidChangedData || savingChangedData" (click)="doSaveChangedData()" data-tests-id="properties-save-button">{{savingChangedData ? 'Saving' : 'Save'}}</button>
+ </div>
+ </div>
<div class="header">
- <div class="search-filter-container" [class.without-filter]="isInpusTabSelected">
- <span *ngIf="displayClearSearch && !isInpusTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span>
+ <div class="search-filter-container" [class.without-filter]="isInputsTabSelected">
+ <span *ngIf="displayClearSearch && isPropertiesTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span>
<input type="text" class="search-box" placeholder="Search" [(ngModel)]="searchQuery" data-tests-id="search-box"/>
<span class="sprite search-icon" data-tests-id="search-button"></span>
- <filter-properties-assignment *ngIf="!isInpusTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment>
+ <filter-properties-assignment *ngIf="isPropertiesTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment>
</div>
- <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly" (click)="declareProperties()" data-tests-id="declare-button">Declare</button>
+ <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly || hasChangedData" (click)="declareProperties()" data-tests-id="declare-button">Declare</button>
</div>
</div>
<div class="right-column gray-border">
@@ -48,10 +55,10 @@
<div class="hierarchy-header white-sub-header">
<span tooltip="{{component.name}}">{{component.name}}</span>
</div>
- <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInpusTabSelected">No data to display</div>
+ <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInputsTabSelected">No data to display</div>
<hierarchy-navigation class="hierarchy-nav"
(updateSelected)="onInstanceSelectedUpdate($event)"
- [displayData]="isInpusTabSelected ? []: instancesNavigationData"
+ [displayData]="isInputsTabSelected ? []: instancesNavigationData"
[selectedItem]="selectedInstanceData.uniqueId"
[displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation>
</div>
@@ -59,12 +66,12 @@
<tab tabTitle="Property Structure">
<div class="hierarchy-nav-container">
<div class="hierarchy-header white-sub-header" [class.selected]="selectedFlatProperty.path == propertyStructureHeader">
- <span tooltip="{{!isInpusTabSelected ? propertyStructureHeader : ''}}">{{!isInpusTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span>
+ <span tooltip="{{isPropertiesTabSelected ? propertyStructureHeader : ''}}">{{isPropertiesTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span>
</div>
- <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInpusTabSelected">No data to display</div>
+ <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInputsTabSelected">No data to display</div>
<hierarchy-navigation class="hierarchy-nav"
(updateSelected)="onPropertySelectedUpdate($event)"
- [displayData]="isInpusTabSelected ? [] : propertiesNavigationData"
+ [displayData]="isInputsTabSelected ? [] : propertiesNavigationData"
[selectedItem]="selectedFlatProperty.path"
[displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation>
</div>
@@ -72,4 +79,9 @@
</tabs>
</div>
</div>
+ <template #saveChangedDataModalContentTemplate>
+ <loader [display]="savingChangedData" [size]="'medium'" [relative]="true"></loader>
+ Your changes{{isValidChangedData ? '' : ' (invalid)'}} have not been saved.<br>
+ Do you want to {{isValidChangedData ? 'save' : 'discard'}} them?
+ </template>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less
index 03974bf723..6de6dda7bb 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less
@@ -76,6 +76,7 @@
outline: none;
font-style: italic;
color:@ng2-med-dark-gray;
+ width: auto;
&::-moz-placeholder { color:@ng2-med-dark-gray;}
&::-webkit-input-placeholder{ color:@ng2-med-dark-gray;}
@@ -113,6 +114,17 @@
top: 0;
right: 0;
}
+
+ .main-tabs-section {
+ position: relative;
+
+ .main-tabs-section-buttons {
+ position: absolute;
+ top: 45px;
+ right: 0;
+ padding: 4px;
+ }
+ }
}
.right-column {
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
index 9603648bd8..40818bce78 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts
@@ -18,11 +18,11 @@
* ============LICENSE_END=========================================================
*/
-import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core";
+import * as _ from "lodash";
+import {Component, ViewChild, Inject, TemplateRef} from "@angular/core";
import { PropertiesService } from "../../services/properties.service";
-import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models";
+import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
import { ResourceType } from "app/utils";
-import property = require("lodash/property");
import {ComponentServiceNg2} from "../../services/component-services/component.service";
import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
import { InputBEModel, InputFEModel, ComponentInstance, PropertyBEModel, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models";
@@ -35,6 +35,9 @@ import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/
import {HierarchyNavService} from "./services/hierarchy-nav.service";
import {PropertiesUtils} from "./services/properties.utils";
import {ComponentModeService} from "../../services/component-services/component-mode.service";
+import {ModalService} from "../../services/modal.service";
+import {Tabs, Tab} from "../../components/ui/tabs/tabs.component";
+import {InputsUtils} from "./services/inputs.utils";
@Component({
templateUrl: './properties-assignment.page.component.html',
@@ -64,24 +67,36 @@ export class PropertiesAssignmentComponent {
hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name');
displayClearSearch = false;
searchPropertyName:string;
- isInpusTabSelected:boolean;
+ currentMainTab:Tab;
+ isInputsTabSelected:boolean;
+ isPropertiesTabSelected:boolean;
isReadonly:boolean;
loadingInstances:boolean = false;
loadingInputs:boolean = false;
loadingProperties:boolean = false;
-
- @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef;
- @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef;
+ changedData:Array<PropertyFEModel|InputFEModel>;
+ hasChangedData:boolean;
+ isValidChangedData:boolean;
+ savingChangedData:boolean;
+ stateChangeStartUnregister:Function;
+
+ @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
+ @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
@ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
+ @ViewChild('saveChangedDataModalContentTemplate') saveChangedDataModalContentTemplateRef: TemplateRef<void>;
constructor(private propertiesService: PropertiesService,
private hierarchyNavService: HierarchyNavService,
private propertiesUtils:PropertiesUtils,
+ private inputsUtils:InputsUtils,
private componentServiceNg2:ComponentServiceNg2,
private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
@Inject("$stateParams") _stateParams,
- private renderer: Renderer,
+ @Inject("$scope") private $scope:ng.IScope,
+ @Inject("$state") private $state:ng.ui.IStateService,
+ @Inject("Notification") private Notification:any,
private componentModeService:ComponentModeService,
+ private ModalService:ModalService,
private EventListenerService:EventListenerService) {
this.instanceFePropertiesMap = new InstanceFePropertiesMap();
@@ -91,6 +106,10 @@ export class PropertiesAssignmentComponent {
this.component = _stateParams.component;
this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout);
this.updateViewMode();
+
+ this.changedData = [];
+ this.updateHasChangedData();
+ this.isValidChangedData = true;
}
ngOnInit() {
@@ -102,7 +121,9 @@ export class PropertiesAssignmentComponent {
.getComponentInputs(this.component)
.subscribe(response => {
_.forEach(response.inputs, (input: InputBEModel) => {
- this.inputs.push(new InputFEModel(input)); //only push items that were declared via SDC
+ const newInput: InputFEModel = new InputFEModel(input);
+ this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
+ this.inputs.push(newInput); //only push items that were declared via SDC
});
this.loadingInputs = false;
@@ -123,10 +144,22 @@ export class PropertiesAssignmentComponent {
this.selectFirstInstanceByDefault();
}, error => {}); //ignore error
+ this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ event.preventDefault();
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.$state.go(toState, toParams);
+ }
+ });
+ }
+ });
};
ngOnDestroy() {
this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT);
+ this.stateChangeStartUnregister();
}
selectFirstInstanceByDefault = () => {
@@ -147,12 +180,23 @@ export class PropertiesAssignmentComponent {
onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => {
console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate");
+
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.onInstanceSelectedUpdate(resourceInstance);
+ }
+ });
+ return;
+ }
+
let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
this.selectedInstanceData = resourceInstance;
this.selectedInstanceType = resourceInstance.originType;
this.loadingProperties = true;
- if(this.isInput(resourceInstance.originType)) {
+ if (this.isInput(resourceInstance.originType)) {
this.componentInstanceServiceNg2
.getComponentInstanceInputs(this.component, resourceInstance)
.subscribe(response => {
@@ -160,7 +204,8 @@ export class PropertiesAssignmentComponent {
this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
this.loadingProperties = false;
- }, error => {}); //ignore error
+ }, error => {
+ }); //ignore error
} else {
this.componentInstanceServiceNg2
.getComponentInstanceProperties(this.component, resourceInstance.uniqueId)
@@ -168,14 +213,15 @@ export class PropertiesAssignmentComponent {
instanceBePropertiesMap[resourceInstance.uniqueId] = response;
this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
this.loadingProperties = false;
- }, error => {}); //ignore error
+ }, error => {
+ }); //ignore error
}
- if(resourceInstance.componentName === "vnfConfiguration") {
+ if (resourceInstance.componentName === "vnfConfiguration") {
this.isReadonly = true;
}
- if( this.searchPropertyName ){
+ if (this.searchPropertyName) {
this.clearSearch();
}
//clear selected property from the navigation
@@ -193,41 +239,31 @@ export class PropertiesAssignmentComponent {
/*** VALUE CHANGE EVENTS ***/
- propertyValueChanged = (event: PropertyFEModel) => {
- console.log("==>" + this.constructor.name + ": propertyValueChanged " + event);
- // Copying the actual value from the object ref into the value if it's from a complex type
- event.value = event.getJSONValue();
-
- if (this.isInput(this.selectedInstanceData.originType)) {
- console.log("I want to update input value on the resource instance");
- let inputToUpdate = new PropertyBEModel(event);
- this.componentInstanceServiceNg2
- .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate)
- .subscribe(response => {
- console.log("Update resource instance input response: ", response);
- }, error => {}); //ignore error
- }
- else {
- let propertyBe = new PropertyBEModel(event);
- this.componentInstanceServiceNg2
- .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe)
- .subscribe(response => {
- console.log("Update resource instance property response: ", response);
- }, error => {}); //ignore error
- console.log(event);
+ dataChanged = (item:PropertyFEModel|InputFEModel) => {
+ let itemHasChanged;
+ if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
+ itemHasChanged = item.hasValueObjChanged();
+ } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
+ itemHasChanged = item.hasDefaultValueChanged();
}
- };
-
- inputValueChanged = (event) => {
- console.log("==>" + this.constructor.name + ": inputValueChanged");
- let inputToUpdate = new PropertyBEModel(event);
+ const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
+ if (itemHasChanged) {
+ if (dataChangedIdx === -1) {
+ this.changedData.push(item);
+ }
+ } else {
+ if (dataChangedIdx !== -1) {
+ this.changedData.splice(dataChangedIdx, 1);
+ }
+ }
- this.componentServiceNg2
- .updateComponentInput(this.component, inputToUpdate)
- .subscribe(response => {
- console.log("updated the component input and got this response: ", response);
- }, error => {}); //ignore error
+ if (this.isPropertiesTabSelected) {
+ this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
+ } else if (this.isInputsTabSelected) {
+ this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
+ }
+ this.updateHasChangedData();
};
@@ -272,7 +308,7 @@ export class PropertiesAssignmentComponent {
// Set selected property in table
this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
- this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']);
+ this.hierarchyNavTabs.triggerTabChange('Property Structure');
};
@@ -280,13 +316,27 @@ export class PropertiesAssignmentComponent {
this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
return instance.name == $event;
});
- this.renderer.invokeElementMethod(
- this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
};
tabChanged = (event) => {
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
+ }
+ });
+
+ // return to show the current tab
+ this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
+ return;
+ }
+
console.log("==>" + this.constructor.name + ": tabChanged " + event);
- this.isInpusTabSelected = event.title === "Inputs";
+ this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
+ this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
+ this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
this.propertyStructureHeader = null;
this.searchQuery = '';
};
@@ -320,12 +370,180 @@ export class PropertiesAssignmentComponent {
this.checkedPropertiesCount = 0;
_.forEach(response, (input: InputBEModel) => {
let newInput: InputFEModel = new InputFEModel(input);
+ this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
this.inputs.push(newInput);
this.updatePropertyValueAfterDeclare(newInput);
});
}, error => {}); //ignore error
};
+ saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
+ return new Promise((resolve, reject) => {
+ if (!this.isValidChangedData) {
+ reject('Changed data is invalid - cannot save!');
+ return;
+ }
+ if (!this.changedData.length) {
+ resolve([]);
+ return;
+ }
+
+ // make request and its handlers
+ let request;
+ let handleSuccess, handleError;
+ if (this.isPropertiesTabSelected) {
+ const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
+ changedProp = <PropertyFEModel>changedProp;
+ const propBE = new PropertyBEModel(changedProp);
+ propBE.value = changedProp.getJSONValue();
+ return propBE;
+ });
+
+ if (this.isInput(this.selectedInstanceData.originType)) {
+ request = this.componentInstanceServiceNg2
+ .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties);
+ handleSuccess = (response) => {
+ // reset each changed property with new value and remove it from changed properties list
+ response.forEach((resInput) => {
+ const changedProp = <PropertyFEModel>this.changedData.shift();
+ this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
+ });
+ console.log('updated instance inputs:', response);
+ };
+ } else {
+ request = this.componentInstanceServiceNg2
+ .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties)
+ handleSuccess = (response) => {
+ // reset each changed property with new value and remove it from changed properties list
+ response.forEach((resProp) => {
+ const changedProp = <PropertyFEModel>this.changedData.shift();
+ this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
+ });
+ resolve(response);
+ console.log("updated instance properties: ", response);
+ };
+ }
+ } else if (this.isInputsTabSelected) {
+ const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
+ changedInput = <InputFEModel>changedInput;
+ const inputBE = new InputBEModel(changedInput);
+ inputBE.defaultValue = changedInput.getJSONDefaultValue();
+ return inputBE;
+ });
+ request = this.componentServiceNg2
+ .updateComponentInputs(this.component, changedInputs);
+ handleSuccess = (response) => {
+ // reset each changed property with new value and remove it from changed properties list
+ response.forEach((resInput) => {
+ const changedInput = <InputFEModel>this.changedData.shift();
+ this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
+ });
+ console.log("updated the component inputs and got this response: ", response);
+ }
+ }
+
+ this.savingChangedData = true;
+ request.subscribe(
+ (response) => {
+ this.savingChangedData = false;
+ handleSuccess && handleSuccess(response);
+ this.updateHasChangedData();
+ resolve(response);
+ },
+ (error) => {
+ this.savingChangedData = false;
+ handleError && handleError(error);
+ this.updateHasChangedData();
+ reject(error);
+ }
+ );
+ });
+ };
+
+ reverseChangedData = ():void => {
+ // make reverse item handler
+ let handleReverseItem;
+ if (this.isPropertiesTabSelected) {
+ handleReverseItem = (changedItem) => {
+ changedItem = <PropertyFEModel>changedItem;
+ this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
+ };
+ } else if (this.isInputsTabSelected) {
+ handleReverseItem = (changedItem) => {
+ changedItem = <InputFEModel>changedItem;
+ this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
+ };
+ }
+
+ this.changedData.forEach(handleReverseItem);
+ this.changedData = [];
+ this.updateHasChangedData();
+ };
+
+ updateHasChangedData = ():boolean => {
+ const curHasChangedData:boolean = (this.changedData.length > 0);
+ if (curHasChangedData !== this.hasChangedData) {
+ this.hasChangedData = curHasChangedData;
+ this.$scope.$emit('setWorkspaceTopBarActive', !this.hasChangedData);
+ }
+ return this.hasChangedData;
+ };
+
+ doSaveChangedData = ():void => {
+ this.saveChangedData().then(
+ () => {
+ this.Notification.success({
+ message: 'Successfully saved changes',
+ title: 'Saved'
+ });
+ },
+ () => {
+ this.Notification.error({
+ message: 'Failed to save changes!',
+ title: 'Failure'
+ });
+ }
+ );
+ };
+
+ openChangedDataModal = ():Promise<boolean> => {
+ let modalTitle;
+ if (this.isPropertiesTabSelected) {
+ modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
+ } else if (this.isInputsTabSelected) {
+ modalTitle = `Unsaved inputs for ${this.component.name}`;
+ }
+
+ return new Promise<boolean>((resolve) => {
+ const modal = this.ModalService.createCustomModal(new ModalModel(
+ 'sm',
+ modalTitle,
+ null,
+ [
+ new ButtonModel('Cancel', 'outline grey', () => {
+ modal.instance.close();
+ resolve(false);
+ }),
+ new ButtonModel('Discard', 'outline blue', () => {
+ this.reverseChangedData();
+ modal.instance.close();
+ resolve(true);
+ }),
+ new ButtonModel('Save', 'blue', () => {
+ this.saveChangedData().then(() => {
+ modal.instance.close();
+ resolve(true);
+ }, () => {
+ modal.instance.close();
+ resolve(false);
+ });
+ }, () => !this.isValidChangedData)
+ ]
+ ));
+ this.ModalService.addDynamicTemplateToModal(modal, this.saveChangedDataModalContentTemplateRef);
+ modal.instance.open();
+ });
+ };
updatePropertyValueAfterDeclare = (input: InputFEModel) => {
if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
@@ -346,12 +564,12 @@ export class PropertiesAssignmentComponent {
};
setInputTabIndication = (numInputs: number): void => {
- this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
+ this.propertyInputTabs.setTabIndication('Inputs', numInputs);
};
deleteInput = (input: InputFEModel) => {
console.log("==>" + this.constructor.name + ": deleteInput");
- let inputToDelete = new PropertyBEModel(input);
+ let inputToDelete = new InputBEModel(input);
this.componentServiceNg2
.deleteInput(this.component, inputToDelete)
@@ -389,7 +607,7 @@ export class PropertiesAssignmentComponent {
this.processInstancePropertiesResponse(response, false);
this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
this.searchPropertyName = filterData.propertyName;//mark in table
- this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
this.propertiesNavigationData = [];
this.displayClearSearch = true;
}, error => {}); //ignore error
@@ -408,12 +626,11 @@ export class PropertiesAssignmentComponent {
clickOnClearSearch = () => {
this.clearSearch();
this.selectFirstInstanceByDefault();
- this.renderer.invokeElementMethod(
- this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
};
private isInput = (instanceType:string):boolean =>{
- return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC;
+ return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
}
}
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
index 016b04788e..1a800baac7 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { SimpleFlatProperty, PropertyFEModel, DerivedFEProperty } from 'app/models';
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts
new file mode 100644
index 0000000000..408a00e7b4
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+import { Injectable } from '@angular/core';
+import { InputFEModel} from "app/models";
+
+@Injectable()
+export class InputsUtils {
+
+ constructor() {}
+
+ public initDefaultValueObject = (input: InputFEModel): void => {
+ input.resetDefaultValueObjValidation();
+ input.defaultValueObj = input.getDefaultValueObj();
+ input.updateDefaultValueObjOrig();
+ };
+
+ public resetInputDefaultValue = (input: InputFEModel, newDefaultValue: string): void => {
+ input.defaultValue = newDefaultValue;
+ this.initDefaultValueObject(input);
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts
index 8f46c6f603..e7b59b96ba 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, DerivedFEProperty, DerivedPropertyType, InputFEModel} from "app/models";
import { DataTypeService } from "app/ng2/services/data-type.service";
@@ -47,10 +48,13 @@ export class PropertiesUtils {
let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
- if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map.
- newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name);
- }
- if (newFEProp.getInputValues && newFEProp.getInputValues.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
+ this.initValueObjectRef(newFEProp); //initialize valueObj.
+ propertyFeArray.push(newFEProp);
+ newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
+ this.dataTypeService.checkForCustomBehavior(newFEProp);
+
+ //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
+ if (newFEProp.getInputValues && newFEProp.getInputValues.length) {
newFEProp.getInputValues.forEach(propInputDetail => {
let inputPath = propInputDetail.inputPath;
if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath
@@ -63,10 +67,6 @@ export class PropertiesUtils {
this.propertiesService.disableRelatedProperties(newFEProp, inputPath);
});
}
- this.initValueObjectRef(newFEProp); //initialize valueObj.
- propertyFeArray.push(newFEProp);
- newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
- this.dataTypeService.checkForCustomBehavior(newFEProp);
}
});
instanceFePropertiesMap[instanceId] = propertyFeArray;
@@ -103,33 +103,29 @@ export class PropertiesUtils {
* Note: This logic is different than assignflattenedchildrenvalues - here we merge values, there we pick either the parents value, props value, or default value - without merging.
*/
public initValueObjectRef = (property: PropertyFEModel): void => {
- if (property.derivedDataType == DerivedPropertyType.SIMPLE || property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component
- property.valueObj = property.value || property.defaultValue;
- if (property.isDeclared) {
- if(typeof property.valueObj == 'object'){
- property.valueObj = JSON.stringify(property.valueObj);
- }
- }else if(property.valueObj &&
- property.type !== PROPERTY_TYPES.STRING &&
- property.type !== PROPERTY_TYPES.JSON &&
- PROPERTY_DATA.SCALAR_TYPES.indexOf(property.type) == -1){
- property.valueObj = JSON.parse(property.valueObj);//The valueObj contains the real value ans not the value as string
+ property.resetValueObjValidation();
+ if (property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component
+ property.valueObj = property.value || property.defaultValue || null; // use null for empty value object
+ if (property.valueObj && typeof property.valueObj == 'object') {
+ property.valueObj = JSON.stringify(property.valueObj);
}
} else {
- if (property.derivedDataType == DerivedPropertyType.LIST) {
- property.valueObj = _.merge([], JSON.parse(property.defaultValue || '[]'), JSON.parse(property.value || '[]')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
- } else {
- property.valueObj = _.merge({}, JSON.parse(property.defaultValue || '{}'), JSON.parse(property.value || '{}')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
- }
- if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) && Object.keys(property.valueObj).length) {
+ property.valueObj = property.getValueObj();
+ if (property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) {
+ property.flattenedChildren = [];
Object.keys(property.valueObj).forEach((key) => {
property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key]))
});
- } else {
+ } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) {
+ property.flattenedChildren = this.createFlattenedChildren(property.type, property.name);
this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
+ property.flattenedChildren.forEach((childProp) => {
+ property.childPropUpdated(childProp);
+ });
}
}
- }
+ property.updateValueObjOrig();
+ };
/*
* Loops through flattened properties array and to assign values
@@ -142,22 +138,24 @@ export class PropertiesUtils {
derivedPropArray.forEach((prop, index) => {
let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name
- prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
+ prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue || null); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
+ prop.value = (prop.valueObj !== null && (typeof prop.valueObj) != 'string') ? JSON.stringify(prop.valueObj) : prop.valueObj;
- if ( prop.isDeclared && typeof prop.valueObj == 'object') { //Stringify objects of items that are declared
- prop.valueObj = JSON.stringify(prop.valueObj);
- } else if(typeof prop.valueObj == PROPERTY_TYPES.STRING
- && (prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN)){ //parse ints and non-string simple types
- prop.valueObj = JSON.parse(prop.valueObj);
+ if ((prop.isDeclared || prop.type == PROPERTY_TYPES.STRING || prop.type == PROPERTY_TYPES.JSON)) { //Stringify objects of items that are declared or from type string/json
+ prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == 'object') ? JSON.stringify(prop.valueObj) : prop.valueObj;
+ } else if(prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN){ //parse ints and non-string simple types
+ prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == PROPERTY_TYPES.STRING) ? JSON.parse(prop.valueObj) : prop.valueObj;
} else { //parse strings that should be objects
- if (prop.derivedDataType == DerivedPropertyType.COMPLEX && typeof prop.valueObj != 'object') {
- prop.valueObj = JSON.parse(prop.valueObj || '{}');
- } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') {
- prop.valueObj = JSON.parse(prop.valueObj || '[]');
- } else if (prop.derivedDataType == DerivedPropertyType.MAP && typeof prop.valueObj != 'object' && (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType)) { //dont parse values for children of map of simple
- prop.valueObj = JSON.parse(prop.valueObj || '{}');
+ if (prop.derivedDataType == DerivedPropertyType.COMPLEX) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
+ } else if (prop.derivedDataType == DerivedPropertyType.LIST) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '[]') : prop.valueObj;
+ } else if (prop.derivedDataType == DerivedPropertyType.MAP) {
+ if (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
+ }
}
- if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && Object.keys(prop.valueObj).length) {
+ if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && prop.valueObj !== null && Object.keys(prop.valueObj).length) {
let newProps: Array<DerivedFEProperty> = [];
Object.keys(prop.valueObj).forEach((key) => {
newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array
@@ -165,6 +163,8 @@ export class PropertiesUtils {
propsToPushMap[index + 1] = newProps;
}
}
+
+ prop.valueObj = PropertyFEModel.cleanValueObj(prop.valueObj);
});
//add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes.
@@ -178,11 +178,10 @@ export class PropertiesUtils {
if (nestedPath) {
let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath);
newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name);
+ property.updateValueObjOrig();
} else {
this.initValueObjectRef(property);
}
}
-
-
}