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 @@
-
- 1">
+ @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}}
-
-
+