Support list of map properties in composition
[sdc.git] / catalog-ui / src / app / directives / property-types / type-map / type-map-directive.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 /**
22  * Created by rcohen on 9/15/2016.
23  */
24 'use strict';
25 import {ValidationUtils, PROPERTY_TYPES} from "app/utils";
26 import {DataTypesService} from "app/services";
27 import {SchemaProperty} from "app/models";
28
29 export interface ITypeMapScope extends ng.IScope {
30     parentFormObj:ng.IFormController;
31     schemaProperty:SchemaProperty;
32     isMapKeysUnique:boolean;
33     isSchemaTypeDataType:boolean;
34     valueObjRef:any;
35     mapKeys:Array<string>;//array of map keys
36     mapKeysStatic:Array<string>;
37     MapKeyValidationPattern:RegExp;
38     fieldsPrefixName:string;
39     readOnly:boolean;
40     mapDefaultValue:any;
41     maxLength:number;
42     constraints:string[];
43     showAddBtn: boolean;
44
45     getValidationPattern(type:string):RegExp;
46     validateIntRange(value:string):boolean;
47     changeKeyOfMap(newKey:string, index:number, fieldName:string):void;
48     deleteMapItem(index:number):void;
49     addMapItemFields():void;
50     parseToCorrectType(objectOfValues:any, locationInObj:string, type:string):void;
51     getNumber(num:number):Array<any>;
52 }
53
54
55 export class TypeMapDirective implements ng.IDirective {
56
57     constructor(private DataTypesService:DataTypesService,
58                 private MapKeyValidationPattern:RegExp,
59                 private ValidationUtils:ValidationUtils,
60                 private $timeout:ng.ITimeoutService) {
61     }
62
63     scope = {
64         valueObjRef: '=',//ref to map object in the parent value object
65         schemaProperty: '=',//get the schema.property object
66         parentFormObj: '=',//ref to parent form (get angular form object)
67         fieldsPrefixName: '=',//prefix for form fields names
68         readOnly: '=',//is form read only
69         defaultValue: '@',//this map default value
70         maxLength: '=',
71         constraints: '=',
72         showAddBtn: '=?'
73     };
74
75     restrict = 'E';
76     replace = true;
77     template = (): string => {
78         return require('./type-map-directive.html');
79     };
80
81     link = (scope:ITypeMapScope, element:any, $attr:any) => {
82         scope.showAddBtn = angular.isDefined(scope.showAddBtn) ? scope.showAddBtn : true;
83         scope.MapKeyValidationPattern = this.MapKeyValidationPattern;
84         scope.isMapKeysUnique = true;
85
86         //reset valueObjRef and mapKeys when schema type is changed
87         scope.$watchCollection('schemaProperty.type', (newData:any):void => {
88             scope.isSchemaTypeDataType = this.DataTypesService.isDataTypeForSchemaType(scope.schemaProperty);
89             if (scope.valueObjRef) {
90                 scope.mapKeys = Object.keys(scope.valueObjRef);
91                 //keeping another copy of the keys, as the mapKeys gets overridden sometimes
92                 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
93             }
94         });
95
96         scope.$watchCollection('valueObjRef', (newData: any): void => {
97             scope.mapKeys = Object.keys(scope.valueObjRef);
98             scope.mapKeysStatic = Object.keys(scope.valueObjRef);
99         });
100
101         //when user brows between properties in "edit property form"
102         scope.$watchCollection('fieldsPrefixName', (newData:any):void => {
103             if (!scope.valueObjRef) {
104                 scope.valueObjRef = {};
105             }
106             scope.mapKeys = Object.keys(scope.valueObjRef);
107             //keeping another copy of the keys, as the mapKeys gets overridden sometimes
108             scope.mapKeysStatic = Object.keys(scope.valueObjRef);
109
110             if ($attr.defaultValue) {
111                 scope.mapDefaultValue = JSON.parse($attr.defaultValue);
112             }
113         });
114
115         //return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
116         scope.getNumber = (num:number):Array<any> => {
117             return new Array(num);
118         };
119
120         scope.getValidationPattern = (type:string):RegExp => {
121             return this.ValidationUtils.getValidationPattern(type);
122         };
123
124         scope.validateIntRange = (value:string):boolean => {
125             return !value || this.ValidationUtils.validateIntRange(value);
126         };
127
128         scope.changeKeyOfMap = (newKey:string, index:number, fieldName:string):void => {
129             const currentKeySet = Object.keys(scope.valueObjRef);
130             const currentKey = currentKeySet[index];
131             const existingKeyIndex = currentKeySet.indexOf(newKey);
132             if (existingKeyIndex > -1 && existingKeyIndex != index) {
133                 scope.parentFormObj[fieldName].$setValidity('keyExist', false);
134                 scope.isMapKeysUnique = false;
135                 return;
136             }
137
138             scope.parentFormObj[fieldName].$setValidity('keyExist', true);
139             scope.isMapKeysUnique = true;
140             if (!scope.parentFormObj[fieldName].$invalid) {
141                 //To preserve the order of the keys, delete each one and recreate
142                 let newObj = {};
143                 angular.copy(scope.valueObjRef, newObj);
144                 angular.forEach(newObj, function (value: any, key: string) {
145                     delete scope.valueObjRef[key];
146                     if (key == currentKey) {
147                         scope.valueObjRef[newKey] = value;
148                     } else {
149                         scope.valueObjRef[key] = value;
150                     }
151                 });
152             }
153         };
154
155         scope.deleteMapItem = (index:number):void=> {
156             delete scope.valueObjRef[scope.mapKeys[index]];
157             scope.mapKeys.splice(index, 1);
158             if (!scope.mapKeys.length) {//only when user removes all pairs of key-value fields - put the default
159                 if (scope.mapDefaultValue) {
160                     angular.copy(scope.mapDefaultValue, scope.valueObjRef);
161                     scope.mapKeys = Object.keys(scope.valueObjRef);
162                 }
163             }
164         };
165
166         scope.addMapItemFields = ():void => {
167             scope.valueObjRef[''] = null;
168             scope.mapKeys = Object.keys(scope.valueObjRef);
169         };
170
171         scope.parseToCorrectType = (objectOfValues:any, locationInObj:string, type:string):void => {
172             if (objectOfValues[locationInObj] && type != PROPERTY_TYPES.STRING) {
173                 objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
174             }
175         }
176     };
177
178     public static factory = (DataTypesService:DataTypesService,
179                              MapKeyValidationPattern:RegExp,
180                              ValidationUtils:ValidationUtils,
181                              $timeout:ng.ITimeoutService)=> {
182         return new TypeMapDirective(DataTypesService, MapKeyValidationPattern, ValidationUtils, $timeout);
183     };
184 }
185
186 TypeMapDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'MapKeyValidationPattern', 'ValidationUtils', '$timeout'];