diff options
author | Michael Lando <ml636r@att.com> | 2018-03-04 14:53:33 +0200 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2018-03-07 13:19:05 +0000 |
commit | a5445100050e49e83f73424198d73cd72d672a4d (patch) | |
tree | cacf4df817df31be23e4e790d1dda857bdae061e /catalog-ui/src/app/ng2/pages/properties-assignment | |
parent | 51157f92c21976cba4914c378aaa3cba49826931 (diff) |
Sync Integ to Master
Change-Id: I71e3acc26fa612127756ac04073a522b9cc6cd74
Issue-ID: SDC-977
Signed-off-by: Gitelman, Tal (tg851x) <tg851x@intl.att.com>
Diffstat (limited to 'catalog-ui/src/app/ng2/pages/properties-assignment')
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); } } - - } |