Solved problems with autocomplete and form validation (reactive forms)

This commit is contained in:
Maria Teresa Paratore 2023-09-22 16:56:19 +02:00
parent 970b8b87d2
commit 7f4458cdc3
5 changed files with 96 additions and 120 deletions

View File

@ -1,5 +1,5 @@
<div class="d-flex flex-row py-4"> <div class="d-flex flex-row py-4">
<div id="btn-group" class="col-md-3 mt-2"> <div id="btn-group" class="col-md-3 mt-3">
<button mat-raised-button color="primary"><mat-icon>source</mat-icon> <span>File</span></button> <button mat-raised-button color="primary"><mat-icon>source</mat-icon> <span>File</span></button>
<button mat-raised-button color="primary"><mat-icon>edit</mat-icon> <span>Edit</span></button> <button mat-raised-button color="primary"><mat-icon>edit</mat-icon> <span>Edit</span></button>
<button mat-raised-button color="primary"><mat-icon>explore</mat-icon> <span>View</span></button> <button mat-raised-button color="primary"><mat-icon>explore</mat-icon> <span>View</span></button>
@ -9,17 +9,8 @@
<div class="col-md-9 mt-2"> <div class="col-md-9 mt-2">
<form [formGroup]="chooseContextForm"> <form [formGroup]="chooseContextForm">
<!--
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
-->
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label for="namefield" class="ml-4 flex-grow-1">Context name</mat-label> <mat-label for="namefield" class="ml-4 flex-grow-1">Context name</mat-label>
<input <input
matInput matInput
#contextInput #contextInput
@ -27,70 +18,53 @@
[matAutocomplete]="auto" [matAutocomplete]="auto"
placeholder="Name" placeholder="Name"
type="text" type="text"
[formControlName]="namefield" [formControl]="namefield"
required required
/> />
<mat-error>Please enter context!</mat-error>
<mat-autocomplete #auto="matAutocomplete"> <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option <mat-option *ngFor="let ctx of filteredContexts | async" [value]="ctx.name">
(onSelectionChange)="(contextInput.value != undefined)" <span>{{ ctx.name }}</span>
*ngFor="let ctx of filteredContexts | async" <small> | ID: {{ ctx.id }}</small>
[value]="ctx.name" </mat-option>
>{{ ctx.name }}</mat-option
>
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label for="uidfield">Context UUID</mat-label> <mat-label for="uidfield">Context UUID</mat-label>
<input matInput id="uidfield" type="text" placeholder="UUID" formControlName="uidfield" readonly /> <input matInput id="uidfield" type="text" placeholder="UUID" formControlName="uidfield" readonly />
</mat-form-field> </mat-form-field>
<button mat-button color="primary"><mat-icon class="icon-wide2">content_copy</mat-icon></button>
<!--
<div class="form-group">
<mat-form-field appearance="outline" class="form-field">
<mat-label class="ml-4 flex-grow-1">UUID</mat-label>
<input type="text" [formControl]="idCtrl" >
</mat-form-field>
</div>
-->
</form> </form>
<!-- </div>
<mat-form-field> </div>
<mat-label class="ml-4 flex-grow-1">UUID</mat-label> <div class="d-flex flex-row">
<input #uuidInput placeholder="UUID" [formControl]="contextCtrl" contenteditable="false" /> <div class="col-md-3">
</mat-form-field> <h3>Resource tree</h3>
--> <mat-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl" class="resources-tree">
<div class="row"> <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
<h3>Resource tree</h3> <button mat-button (click)="treeNode.emit(node)">
<div class="d-flex flex-row col-md-3"> {{ node.name }}
<mat-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl" class="resources-tree"> </button>
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle> </mat-tree-node>
<button mat-button (click)="treeNode.emit(node)"> <mat-nested-tree-node *matTreeNodeDef="let node; when: hasNestedChild">
{{ node.name }} <div class="mat-tree-node">
</button> <button mat-icon-button matTreeNodeToggle>
</mat-tree-node> <mat-icon class="mat-icon-rtl-mirror">
<mat-nested-tree-node *matTreeNodeDef="let node; when: hasNestedChild"> {{ nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
<div class="mat-tree-node"> </mat-icon>
<button mat-icon-button matTreeNodeToggle> </button>
<mat-icon class="mat-icon-rtl-mirror"> <!--<button mat-button (click)="loadTable(node)">-->
{{ nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }} <button mat-button (click)="treeNode.emit(node)">
</mat-icon> {{ node.name }}
</button> </button>
<!--<button mat-button (click)="loadTable(node)">--> </div>
<button mat-button (click)="treeNode.emit(node)"> <div [class.resources-tree-invisible]="!nestedTreeControl.isExpanded(node)" role="group">
{{ node.name }} <ng-container matTreeNodeOutlet></ng-container>
</button> </div>
</div> </mat-nested-tree-node>
<div [class.resources-tree-invisible]="!nestedTreeControl.isExpanded(node)" role="group"> </mat-tree>
<ng-container matTreeNodeOutlet></ng-container> </div>
</div> <div class="col-md-9">
</mat-nested-tree-node> <jhi-table-screen> </jhi-table-screen>
</mat-tree>
</div>
<div class="col-md-9">
<jhi-table-screen> </jhi-table-screen>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -47,3 +47,7 @@
font-size: 14px; font-size: 14px;
width: 40%; width: 40%;
} }
.icon-wide2 {
transform: scale(2);
}

View File

@ -5,9 +5,9 @@ import { NestedTreeControl } from '@angular/cdk/tree';
import { ResourcesLoaderService } from 'app/services/resources-loader.service'; import { ResourcesLoaderService } from 'app/services/resources-loader.service';
import { IResource } from 'app/services/i-resource'; import { IResource } from 'app/services/i-resource';
import { ContextsLoaderService } from 'app/services/contexts-loader.service'; import { ContextsLoaderService } from 'app/services/contexts-loader.service';
import { IContextNode } from 'app/services/i-context-node'; import { ContextNode, IContextNode } from 'app/services/i-context-node';
import { Observable, map, startWith } from 'rxjs'; import { Observable, map, startWith } from 'rxjs';
import { FormControl, FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
@Component({ @Component({
selector: 'jhi-rsc-tree', selector: 'jhi-rsc-tree',
@ -20,58 +20,63 @@ export class RscTreeComponent implements OnInit {
nestedDataSource = new MatTreeNestedDataSource<IResource>(); nestedDataSource = new MatTreeNestedDataSource<IResource>();
@Output() treeNode = new EventEmitter<IResource>(); //emitting event to parent @Output() treeNode = new EventEmitter<IResource>(); //emitting event to parent
allContexts: IContextNode[]; contexts: ContextNode[];
filteredContexts: Observable<IContextNode[]> | any; filteredContexts: Observable<ContextNode[]> | any;
chooseContextForm: FormGroup | any;
chooseContextForm = new FormGroup({ constructor(private fb: FormBuilder, private resLoader: ResourcesLoaderService, private ctxLoader: ContextsLoaderService) {
namefield: new FormControl(''), this.contexts = [];
uidfield: new FormControl(''),
});
constructor(private resLoader: ResourcesLoaderService, private ctxLoader: ContextsLoaderService, private formBuilder: FormBuilder) {
this.allContexts = {} as IContextNode[];
} }
//MT: per chiamare le direttive formControlName
get namefield(): any { get namefield(): any {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return this.chooseContextForm.get('namefield'); return this.chooseContextForm.get('namefield');
} }
get uidfield(): any { get uidfield(): any {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return this.chooseContextForm.get('uidfield'); return this.chooseContextForm.get('uidfield');
} }
/*
onSubmit(): void {
console.log(this.chooseContextForm.value);
}*/
ngOnInit(): void { ngOnInit(): void {
/*
this.myForm = this.formBuilder.group({
context: [],});
*/
this.resLoader.getResourcesByContext().subscribe(res => { this.resLoader.getResourcesByContext().subscribe(res => {
this.nestedDataSource.data = res; this.nestedDataSource.data = res;
}); });
this.chooseContextForm = this.fb.group({
namefield: null,
uidfield: null,
});
// per la form dei contesti // per la form dei contesti
this.ctxLoader.getData().subscribe(res => { this.ctxLoader.getData().subscribe(res => {
this.allContexts = res; this.contexts = res;
console.log('*******numero contesti...', res.length); console.log('*******numero contesti...', res.length);
}); });
this.filteredContexts = this.chooseContextForm.get('namefield')?.valueChanges.pipe(
this.filteredContexts = this.chooseContextForm.get('namefield').valueChanges.pipe(
startWith(''), startWith(''),
map(name => (name ? this.doFilter(name) : this.allContexts.slice())) map(ctx => (ctx ? this.filterContexts(ctx) : this.contexts.slice()))
); );
} //fine oninit
/* //mettere ANY come tipo dell'argomento per evitare errore TypeScript nella map!!
this.myForm = this.formBuilder.group({ filterContexts(name: any): IContextNode[] {
context: [], return this.contexts.filter(ctx => ctx.name.toLowerCase().includes(name.toLowerCase()));
});
*/
} }
displayFn(name: string): string {
return name ? name : '';
}
/*
//mettendo ID come value da passare
displayFn(name: string): string {
if (!id) return '';
let index = this.states.findIndex(state => state.id === id);
return this.states[index].name;
}
*/
hasNestedChild(_: number, node: IResource): boolean { hasNestedChild(_: number, node: IResource): boolean {
if (node.children == null) { if (node.children == null) {
return false; return false;
@ -79,24 +84,4 @@ export class RscTreeComponent implements OnInit {
return node.children.length > 0; return node.children.length > 0;
} }
} }
private doFilter(name: string): IContextNode[] {
//return this.allContexts.filter(ctx => ctx.name.toLowerCase().includes(name.toLowerCase()));
return this.allContexts.filter(ctx => ctx.name.toLowerCase().includes(name.toLowerCase()));
}
/*
onEnter(evt: any){
if (evt.source.selected) {
alert("hello ");
}
}*/
/*
//TODO: modificare per gestire eventuali eventi sulla onselect
private onSelectedContext(uid:string):void {
this.resLoader.getResourcesByContext().subscribe(res => {
this.nestedDataSource.data = res;
});
}*/
} }

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable, map, tap } from 'rxjs';
import { appProperties } from 'app/config/app-properties'; import { appProperties } from 'app/config/app-properties';
import { IContextNode } from './i-context-node'; import { IContextNode } from './i-context-node';
@ -16,10 +16,9 @@ export class ContextsLoaderService {
getData(): Observable<IContextNode[]> { getData(): Observable<IContextNode[]> {
//TODO: pipe per gestione errori //TODO: pipe per gestione errori
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
// return this.http.get<IContextNode[]>(appProperties.BASEURL_API + 'is/allcontext');
return this.http.get<IContextNode[]>(appProperties.MOCK_BASEURL_API + '/allcontext'); return this.http.get<IContextNode[]>(appProperties.MOCK_BASEURL_API + '/allcontext');
} }
getRawData(): Observable<string> { getRawData(): Observable<string> {
//TODO: pipe per gestione errori //TODO: pipe per gestione errori
return this.http.get<string>(appProperties.BASEURL_API + 'is/allcontext'); return this.http.get<string>(appProperties.BASEURL_API + 'is/allcontext');

View File

@ -4,3 +4,17 @@ export interface IContextNode {
id: string; id: string;
children?: IContextNode[]; children?: IContextNode[];
} }
export class ContextNode implements IContextNode {
parent: string;
name: string;
id: string;
children?: IContextNode[];
constructor(id: string, name: string, parent: string, children: ContextNode[]) {
this.name = name;
this.parent = id;
this.id = id;
this.children = children;
}
}