diff --git a/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/component.ts b/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/component.ts index 631e6511f2..a1e25019fb 100644 --- a/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/component.ts +++ b/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/component.ts @@ -367,7 +367,7 @@ export class VMwareCloudDirectorBasicNodeDataComponent const networkControl = this.form.get(Controls.Network); const additionalNetworksControl = this.form.get(Controls.AdditionalNetworks); - additionalNetworksControl.setValue(additionalNetworksControl.value.filter(n => this.networks.includes(n))); + additionalNetworksControl.setValue(additionalNetworksControl.value?.filter(n => this.networks.includes(n))); if (networkControl.value && this.networks.includes(networkControl.value)) { return; diff --git a/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/template.html b/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/template.html index 6381487e2b..a08696f92d 100644 --- a/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/template.html +++ b/modules/web/src/app/node-data/basic/provider/vmware-cloud-director/template.html @@ -98,8 +98,8 @@ - - + @if (networks.length > 1) { + Additional Networks {{network}} + } (); + @Input() patternError = 'Invalid pattern'; + @Input() pattern: string; + @Output() onChange = new EventEmitter(); @ViewChild('tagInput') tagInput: ElementRef; private readonly _debounceTime = 250; private _unsubscribe = new Subject(); @@ -96,8 +99,11 @@ export class ChipAutocompleteComponent implements OnChanges, OnInit, OnDestroy, if (changes?.disabled && this.form) { if (this.disabled) { this.form.get(Controls.Tags).disable(); + this.form.get(Controls.Tags).clearValidators(); } else if (this.form.get(Controls.Tags).disabled) { this.form.get(Controls.Tags).enable(); + this.form.get(Controls.Tags).setValidators(this._validators()); + this.form.get(Controls.Tags).updateValueAndValidity(); } } } @@ -122,7 +128,11 @@ export class ChipAutocompleteComponent implements OnChanges, OnInit, OnDestroy, } removeTag(tag: string): void { - this.selectedTags = this.selectedTags.filter(selectedTag => selectedTag !== tag); + const index = this.selectedTags.indexOf(tag); + + if (index >= 0) { + this.selectedTags.splice(index, 1); + } this._valueUpdated(); } @@ -151,10 +161,7 @@ export class ChipAutocompleteComponent implements OnChanges, OnInit, OnDestroy, private _initForm() { this.form = this._builder.group({ [Controls.Filter]: this._builder.control(''), - [Controls.Tags]: this._builder.control( - [], - this.required ? [Validators.required, KmValidators.unique()] : [KmValidators.unique()] - ), + [Controls.Tags]: this._builder.control([], this._validators()), }); if (this.disabled) { @@ -180,7 +187,7 @@ export class ChipAutocompleteComponent implements OnChanges, OnInit, OnDestroy, private _valueUpdated() { this._patchValue(); this.filteredTags = this._removeSelectedTagsFromList(); - this.change.emit(this.selectedTags); + this.onChange.emit(this.selectedTags); } private _patchValue() { @@ -196,10 +203,23 @@ export class ChipAutocompleteComponent implements OnChanges, OnInit, OnDestroy, } private _removeSelectedTagsFromFilterList(tags: string[]): string[] { - return tags?.filter(tag => !this.selectedTags.includes(tag)) || []; + return tags?.filter(tag => !this.selectedTags?.includes(tag)) || []; } private _removeSelectedTagsFromList(): string[] { - return this.tags?.filter(tag => !this.selectedTags.includes(tag)) || []; + return this.tags?.filter(tag => !this.selectedTags?.includes(tag)) || []; + } + + private _validators(): ValidatorFn[] { + const validators = [KmValidators.unique()]; + + if (this.required) { + validators.push(Validators.required); + } + + if (this.pattern) { + validators.push(KmValidators.chipPattern(this.pattern)); + } + return validators; } } diff --git a/modules/web/src/app/shared/components/chip-autocomplete/template.html b/modules/web/src/app/shared/components/chip-autocomplete/template.html index 526a8a6013..b1c8ee778c 100644 --- a/modules/web/src/app/shared/components/chip-autocomplete/template.html +++ b/modules/web/src/app/shared/components/chip-autocomplete/template.html @@ -40,6 +40,7 @@ [placeholder]="placeholder" [matAutocomplete]="auto" [matChipInputFor]="tagList" + [matChipInputAddOnBlur]="true" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" (matChipInputTokenEnd)="addTag($event)" /> @@ -62,5 +63,13 @@ Values must be unique. } + + @if (form.get(Controls.Tags).hasError('pattern')) { + + + + } + + diff --git a/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/component.ts b/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/component.ts index 77c9a40e95..ac31fbb4bd 100644 --- a/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/component.ts +++ b/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/component.ts @@ -36,7 +36,18 @@ import {KUBERNETES_RESOURCE_NAME_PATTERN} from '@app/shared/validators/others'; import {AutocompleteControls, AutocompleteInitialState} from '@shared/components/autocomplete/component'; import _ from 'lodash'; import {EMPTY, forkJoin, merge, Observable, of, onErrorResumeNext} from 'rxjs'; -import {catchError, debounceTime, distinctUntilChanged, filter, map, switchMap, takeUntil, tap} from 'rxjs/operators'; +import { + catchError, + debounceTime, + distinctUntilChanged, + filter, + map, + switchMap, + take, + takeUntil, + tap, +} from 'rxjs/operators'; +import {NodeDataService} from '@app/core/services/node-data/service'; enum Controls { Networks = 'networks', @@ -107,6 +118,7 @@ export class VSphereProviderExtendedComponent extends BaseFormValidator implemen folderLabel = FolderState.Empty; networkLabel = NetworkState.Empty; tagCategories: VSphereTagCategory[] = []; + predefinedTagList: string[] = []; tagCategoryLabel = TagCategoryState.Empty; selectedTagCategory = ''; tags: string[] = []; @@ -123,7 +135,8 @@ export class VSphereProviderExtendedComponent extends BaseFormValidator implemen private readonly _builder: FormBuilder, private readonly _presets: PresetsService, private readonly _clusterSpecService: ClusterSpecService, - private readonly _cdr: ChangeDetectorRef + private readonly _cdr: ChangeDetectorRef, + private readonly _nodeDataService: NodeDataService ) { super('VSphere Provider Extended'); } @@ -245,9 +258,17 @@ export class VSphereProviderExtendedComponent extends BaseFormValidator implemen onTagCategoryChange(tagCategory: string): void { this.selectedTagCategory = tagCategory; + this.predefinedTagList = []; if (tagCategory) { this.form.get(Controls.Tags).enable(); this.onTagValuesChange(this.tags); + this._nodeDataService.vsphere + .categoryTags(tagCategory) + .pipe(take(1)) + .subscribe(tags => { + this.predefinedTagList = tags.map(t => t.name); + this._cdr.detectChanges(); + }); } else { this.form.get(Controls.Tags).reset(); this.form.get(Controls.Tags).disable(); diff --git a/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/template.html b/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/template.html index 5680518a47..75d4eabc14 100644 --- a/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/template.html +++ b/modules/web/src/app/wizard/step/provider-settings/provider/extended/vsphere/template.html @@ -109,15 +109,13 @@ filterBy="name">
{{category.name}}
- - +