我不确定我的问题标题是否足够清楚,但我会尽力提供更多细节。我尝试使用角度形式创建文件夹层次结构形式。表单可以无限嵌套。我的问题是,现在我可以在某个级别添加 2 个具有相同名称的文件夹,但这应该是不可能的,并且应该警告用户。这是合乎逻辑的,因为在普通文件系统中,2 个文件夹不能具有相同的名称
为了清楚起见,我在这里提供一个简化版本。阅读起来还是有点长,但是这里有可重现的演示在 stackblitz 中具有相同的代码
表单组件
@Component({
selector: 'my-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css'],
})
export class FormComponent implements OnInit {
myForm!: FormGroup;
isHierarchyVisible: boolean = false;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.myForm = this.formBuilder.group({
folderHierarchy: this.formBuilder.array([]),
});
if (this.folderHierarchy.length === 0) this.isHierarchyVisible = false;
}
removeFolder(index: number): void {
this.folderHierarchy.removeAt(index);
if (this.folderHierarchy.length === 0) this.isHierarchyVisible = false;
}
addFolder(): void {
this.folderHierarchy.push(
this.formBuilder.group({
name: [null, [Validators.required]],
subFolders: this.formBuilder.array([]),
level: 0,
})
);
this.isHierarchyVisible = true;
}
getForm(control: AbstractControl): FormGroup {
return control as FormGroup;
}
get folderHierarchy(): FormArray {
return this.myForm.get('folderHierarchy') as FormArray;
}
}
<p>folder form. type in form name and press enter</p>
<form [formGroup]="myForm">
<div formArrayName="folderHierarchy">
<label for="folderHierarchy">create folder</label>
<div>
<button type="button" class="btn btn-custom rounded-corners btn-circle mb-2" (click)="addFolder()" [disabled]="!folderHierarchy.valid">
Add
</button>
<span class="pl-1">new folder</span>
</div>
<div>
<div *ngIf="!folderHierarchy.valid" class="folder-hierarchy-error">invalid folder hierarchy</div>
<div class="folderContainer">
<div>
<div *ngFor="let folder of folderHierarchy.controls; let i = index" [formGroupName]="i">
<folder-hierarchy (remove)="removeFolder(i)" [folder]="getForm(folder)" [index]="i"></folder-hierarchy>
</div>
</div>
</div>
</div>
</div>
</form>
文件夹层次结构组件
@Component({
selector: 'folder-hierarchy',
templateUrl: './folder-hierarchy.component.html',
styleUrls: ['./folder-hierarchy.component.css'],
})
export class FolderHierarchyComponent implements OnInit {
constructor(private formBuilder: FormBuilder) {}
@Output() remove = new EventEmitter();
@Input() folder!: FormGroup;
@Input() index!: number;
tempName: string = '';
ngOnInit() {}
addSubFolder(folder: FormGroup): void {
(folder.get('subFolders') as FormArray).push(
this.formBuilder.group({
name: [null, [Validators.required]],
subFolders: this.formBuilder.array([]),
level: folder.value.level + 1,
})
);
}
getControls(folder: FormGroup): FormGroup[] {
return (folder.get('subFolders') as FormArray).controls as FormGroup[];
}
removeSubFolder(folder: FormGroup, index: number): void {
(folder.get('subFolders') as FormArray).removeAt(index);
}
removeFolder(folder: { value: { subFolders: string | any[] } }): void {
this.remove.emit(folder);
}
disableAdd(folder: { invalid: any }): void {
return this.folder.invalid || folder.invalid;
}
onKeyup(event: KeyboardEvent): void {
this.tempName = (event.target as HTMLInputElement).value;
}
updateName(folder: FormGroup, name: string): void {
folder.get('name')?.setValue(name);
if (this.isInvalid(folder)) {
folder.get('name')?.updateValueAndValidity();
return;
}
}
isInvalid(folder: FormGroup): boolean {
return !folder.get('name')?.valid;
}
}
<div *ngIf="folder" #folderRow class="folder-row">
<div class="folder-header">
<div class="folder-name-container">
<label for="folderName" class="folder-name-label">Name:</label>
<input #folderName id="folderName" [ngClass]="isInvalid(folder) ? 'invalid-input' : ''" class="folder-name-input" placeholder="Folder Name" type="text" (keyup)="onKeyup($event)" maxlength="50" (keyup.enter)="updateName(folder, $any($event.target).value)" [value]="folder.value.name" autocomplete="off" />
</div>
<button type="button" class="btn-remove-folder" (click)="removeFolder(folder)">Remove</button>
<button type="button" class="btn-add-subfolder" [disabled]="disableAdd(folder)" (click)="addSubFolder(folder)">Add Subfolder</button>
</div>
<div *ngIf="folder && folder.value.subFolders.length > 0" class="subfolder-container">
<div *ngFor="let subFolder of getControls(folder); let i = index" class="subfolder-item">
<folder-hierarchy (remove)="removeSubFolder(folder, i)" [folder]="subFolder"></folder-hierarchy>
</div>
</div>
</div>
由于文件夹组件中已有
onKeyup
监听器,因此可以获取父表单,并检查是否存在同名元素,例如:
onKeyup(event: KeyboardEvent): void {
this.tempName = (event.target as HTMLInputElement).value;
const hasDuplicateName = this.folder.parent.value.filter((el:any, i:number)=> el.name.toLowerCase() === this.tempName.toLowerCase() && i !== this.index);
if(hasDuplicateName.length > 0) {
console.log('hasDuplicateName', hasDuplicateName);
this.folder.setErrors({'duplicateName': true});
}
}