validation

This commit is contained in:
Yubing325 2020-06-17 10:57:57 -05:00
parent 7ffcc93769
commit 1cd6e16ce4
15 changed files with 187 additions and 62 deletions

View File

@ -4,11 +4,13 @@ import { ModalModule } from 'ngx-bootstrap/modal';
import { CommandModalDirective } from './directives/command-modal.directive'; import { CommandModalDirective } from './directives/command-modal.directive';
import { CommandModalComponent } from './command-modal/command-modal.component'; import { CommandModalComponent } from './command-modal/command-modal.component';
import { InputValidatorDirective } from './directives/input-validator.directive'; import { InputValidatorDirective } from './directives/input-validator.directive';
import { FormsModule } from '@angular/forms';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
ModalModule.forRoot() ModalModule.forRoot(),
FormsModule
], ],
declarations: [CommandModalDirective, CommandModalComponent, InputValidatorDirective], declarations: [CommandModalDirective, CommandModalComponent, InputValidatorDirective],
exports: [CommandModalDirective] exports: [CommandModalDirective]

View File

@ -7,13 +7,12 @@
<div class="modal-body"> <div class="modal-body">
<ng-container [ngTemplateOutlet]="template" <ng-container [ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{ $implicit: commandModel, loading: loading }"></ng-container> [ngTemplateOutletContext]="{ $implicit: commandModel, loading: loading }"></ng-container>
<div *ngIf="validationMessage" class="alert alert-danger mt-2" style="white-space: pre-wrap">{{validationMessage}}</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-light" (click)="modalRef.hide()" <button type="button" class="btn btn-light" (click)="modalRef.hide()"
[attr.disabled]="loading">{{ cancelText }}</button> [disabled]="loading">{{ cancelText }}</button>
<button type="button" class="btn btn-primary" (click)="attemptSave()" <button type="button" class="btn btn-primary" [disabled]="loading" (click)="onSubmit()">{{ commandText }}</button>
[attr.disabled]="loading">{{ commandText }}</button>
<br> <br>
<div class="progress" style="width: 100%" *ngIf="loading"> <div class="progress" style="width: 100%" *ngIf="loading">
@ -22,3 +21,4 @@
</div> </div>
</div> </div>

View File

@ -0,0 +1,3 @@
.field-error{
border: 1px solid red;
}

View File

@ -3,6 +3,7 @@ import { IDataSource } from '@poweredsoft/data';
import { BsModalRef } from 'ngx-bootstrap/modal'; import { BsModalRef } from 'ngx-bootstrap/modal';
import { finalize} from 'rxjs/operators'; import { finalize} from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { NgForm } from '@angular/forms';
@Component({ @Component({
selector: 'psbx-command-modal', selector: 'psbx-command-modal',
@ -20,6 +21,8 @@ export class CommandModalComponent implements OnInit, OnDestroy {
loading: boolean; loading: boolean;
commandText: string; commandText: string;
cancelText: string; cancelText: string;
form:NgForm;
validationMessage:string ;
private _notifyMessage: Subscription; private _notifyMessage: Subscription;
private _validationError: Subscription; private _validationError: Subscription;
@ -33,16 +36,26 @@ export class CommandModalComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this._notifyMessage = this.dataSource.notifyMessage$.subscribe(message => { this._notifyMessage = this.dataSource.notifyMessage$.subscribe(message => {
if (message.type != 'info')
this.validationMessage = message.message;
}); });
this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => { this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => {
console.log(validatorErrors); let validationSummary = '';
Object.getOwnPropertyNames(validatorErrors.errors).forEach(property => {
const errors = validatorErrors.errors[property].join('\n');
validationSummary += errors + '\n';
});
this.validationMessage = validationSummary.trim();
}); });
} }
attemptSave() { onSubmit(){
this.loading = true; this.loading = true;
this.validationMessage = null;
this.dataSource.executeCommandByName(this.command, this.commandModel) this.dataSource.executeCommandByName(this.command, this.commandModel)
.pipe( .pipe(
finalize(() => { finalize(() => {
@ -59,4 +72,8 @@ export class CommandModalComponent implements OnInit, OnDestroy {
}); });
} }
attemptSave() {
}
} }

View File

@ -11,17 +11,15 @@
<ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{ $implicit: modelForm, loading: loading }"> <ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{ $implicit: modelForm, loading: loading }">
</ng-container> </ng-container>
<div *ngIf="errorMessage" class="alert alert-danger mt-2"> <div *ngIf="errorMessage" class="alert alert-danger mt-2" style="white-space: pre-wrap">{{ errorMessage }}</div>
{{ errorMessage }}
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-light" (click)="modalRef.hide()" <button type="button" class="btn btn-light" (click)="modalRef.hide()"
[attr.disabled]="loading">{{ cancelText }}</button> [disabled]="loading">{{ cancelText }}</button>
<button type="button" class="btn btn-primary" (click)="attemptSave()" [attr.disabled]="loading">{{commandText}}</button> <button type="button" class="btn btn-primary" (click)="attemptSave()" [disabled]="loading">{{commandText}}</button>
<br> <br>
<div class="progress" style="width: 100%" *ngIf="loading"> <div class="progress" style="width: 100%" *ngIf="loading">

View File

@ -24,16 +24,15 @@ export class FormGroupCommandModalComponent implements OnInit {
cancelText: string; cancelText: string;
errorMessage: string; errorMessage: string;
commandModel:any; commandModel:any;
private _notifyMessage: Subscription; private _notifyMessage: Subscription;
private _validationError: Subscription; private _validationError: Subscription;
constructor(public modalRef: BsModalRef) { } constructor(public modalRef: BsModalRef) { }
ngOnDestroy(): void { ngOnDestroy(): void {
/*
this._notifyMessage.unsubscribe(); this._notifyMessage.unsubscribe();
this._validationError.unsubscribe(); this._validationError.unsubscribe();
*/
} }
ngOnInit(): void { ngOnInit(): void {
@ -42,18 +41,23 @@ export class FormGroupCommandModalComponent implements OnInit {
// }); // });
// this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => { this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => {
// console.log(validatorErrors); let validationSummary = '';
// }); Object.getOwnPropertyNames(validatorErrors.errors).forEach(property => {
const errors = validatorErrors.errors[property].join('\n');
validationSummary += errors + '\n';
});
this.errorMessage = validationSummary.trim();
});
} }
attemptSave() { attemptSave() {
this.errorMessage = null; // this.errorMessage = null;
if (!this.modelForm.valid) // if (!this.modelForm.valid)
{ // {
this.errorMessage = 'Form is not valid, please enter all required fields'; // this.errorMessage = 'Form is not valid, please enter all required fields';
return; // return;
} // }
const finalModel = this.modelForm.value; const finalModel = this.modelForm.value;
if(this.commandModel.id) if(this.commandModel.id)

View File

@ -1,3 +1,10 @@
<div class="spinner-border" role="status"> <div class="center">
<span class="sr-only">Loading...</span> <div class="spinner-grow text-primary" role="status">
</div>
<div class="spinner-grow text-secondary" role="status">
</div>
<div class="spinner-grow text-primary" role="status">
</div>
<div class="spinner-grow text-secondary" role="status">
</div>
</div> </div>

View File

@ -0,0 +1,6 @@
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

View File

@ -13,12 +13,18 @@ import { DataGridLoaderDirective } from './directives/data-grid-loader.directive
@NgModule({ @NgModule({
declarations: [DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,DataGridCellDirective, DataGridFooterDirective, DataGridHeaderDirective, DataGridLoaderDirective, declarations: [
DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,
DataGridCellDirective, DataGridFooterDirective, DataGridHeaderDirective,
DataGridLoaderDirective,
], ],
imports: [ imports: [
CommonModule CommonModule
], ],
exports: [DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,DataGridCellDirective,DataGridFooterDirective, DataGridHeaderDirective,DataGridLoaderDirective] exports: [
DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,
DataGridCellDirective,DataGridFooterDirective, DataGridHeaderDirective,
DataGridLoaderDirective]
}) })
export class DataGridModule { } export class DataGridModule { }

View File

@ -1,4 +1,6 @@
<table [ngClass]="tableClasses"> <ng-container *ngIf="loading" [ngTemplateOutlet]="loadingTemplate"></ng-container>
<table [ngClass]="tableClasses" style="min-height: 300px;">
<thead> <thead>
<tr> <tr>
<th *ngFor="let header of gridHeaders" [attr.colspan]="columns.length"> <th *ngFor="let header of gridHeaders" [attr.colspan]="columns.length">
@ -15,12 +17,7 @@
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody *ngIf="loading"> <tbody *ngIf="!noData else noResultTemplate">
<tr>
<td [attr.colspan]="columns.length"><ng-content select="psDataGridLoader"></ng-content></td>
</tr>
</tbody>
<tbody *ngIf="latestResult&&!loading">
<tr *ngFor="let rowModel of latestResult.data; let i = index"> <tr *ngFor="let rowModel of latestResult.data; let i = index">
<td *ngFor="let column of columns"> <td *ngFor="let column of columns">
<ng-container *ngIf="hasCellTemplate(column)"> <ng-container *ngIf="hasCellTemplate(column)">
@ -44,3 +41,18 @@
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<ng-template #loadingTemplate>
<ng-container *ngFor="let loader of loaders">
<ng-container [ngTemplateOutlet]="loader.template"></ng-container>
</ng-container>
</ng-template>
<ng-template #noResultTemplate>
<tbody>
<tr>
<td [attr.colspan]="columns.length">
<p style="text-align: center;">{{ noRecordsDisplayText }}</p>
</td>
</tr>
</tbody>
</ng-template>

View File

@ -1,27 +1,34 @@
import { Component, OnInit, ContentChildren, QueryList, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, ContentChildren, QueryList, Input, Output, EventEmitter, ContentChild, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { IQueryExecutionResult, IQueryExecutionGroupResult, IDataSource } from '@poweredsoft/data'; import { IQueryExecutionResult, IQueryExecutionGroupResult, IDataSource } from '@poweredsoft/data';
import { DataGridColDirective } from '../directives/data-grid-col.directive'; import { DataGridColDirective } from '../directives/data-grid-col.directive';
import { DataGridHeaderDirective } from '../directives/data-grid-header.directive'; import { DataGridHeaderDirective } from '../directives/data-grid-header.directive';
import { DataGridFooterDirective } from '../directives/data-grid-footer.directive'; import { DataGridFooterDirective } from '../directives/data-grid-footer.directive';
import { DataGridLoaderDirective } from '../directives/data-grid-loader.directive';
import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'ps-data-grid', selector: 'ps-data-grid',
templateUrl: './data-grid.component.html', templateUrl: './data-grid.component.html',
styleUrls: ['./data-grid.component.scss'] styleUrls: ['./data-grid.component.scss']
}) })
export class DataGridComponent implements OnInit { export class DataGridComponent implements OnInit, OnDestroy {
latestResult: IQueryExecutionResult<any> & IQueryExecutionGroupResult<any>; latestResult: IQueryExecutionResult<any> & IQueryExecutionGroupResult<any>;
loading:boolean;
@ContentChildren(DataGridColDirective) columnDefinitions: QueryList<DataGridColDirective>; @ContentChildren(DataGridColDirective) columnDefinitions: QueryList<DataGridColDirective>;
@ContentChildren(DataGridHeaderDirective) gridHeaders: QueryList<DataGridHeaderDirective>; @ContentChildren(DataGridHeaderDirective) gridHeaders: QueryList<DataGridHeaderDirective>;
@ContentChildren(DataGridFooterDirective) gridFooters: QueryList<DataGridFooterDirective>; @ContentChildren(DataGridFooterDirective) gridFooters: QueryList<DataGridFooterDirective>;
@ContentChildren(DataGridLoaderDirective) loaders: QueryList<DataGridLoaderDirective>;
@Input() dataSource: IDataSource<any>; @Input() dataSource: IDataSource<any>;
@Input() tableClasses: any; @Input() tableClasses: any;
@Input() noRecordsText: string;
private _columns: string[]; private _columns: string[];
loading:boolean; private _dataSubscription: Subscription;
private _loadingSubscription: Subscription;
@Input() set columns(value: string[]) { @Input() set columns(value: string[]) {
this._columns = value; this._columns = value;
this.columnsChange.emit(value); this.columnsChange.emit(value);
@ -32,19 +39,45 @@ export class DataGridComponent implements OnInit {
} }
@Output() columnsChange:EventEmitter<string []> = new EventEmitter<string []>(); @Output() columnsChange:EventEmitter<string []> = new EventEmitter<string []>();
constructor() { }
get noData() {
return !this.latestResult || this.latestResult.totalRecords == 0;
}
get noRecordsDisplayText() {
return this.noRecordsText || 'No records';
}
constructor(private cdr: ChangeDetectorRef) {
}
ngOnDestroy(): void {
this._dataSubscription.unsubscribe();
this._loadingSubscription.unsubscribe();
}
ngOnInit(): void { ngOnInit(): void {
this.loading = true;
console.log(this.columnDefinitions); this._dataSubscription = this.dataSource.data$.subscribe(newData => {
this.dataSource.data$.subscribe(newData => {
this.loading=false;
this.latestResult = newData; this.latestResult = newData;
}); });
this._loadingSubscription = this.dataSource.loading$.subscribe(isLoading => {
this.loading = isLoading;
this.cdr.detectChanges();
});
console.log(this.loaders);
} }
getColumn(columnName: string) { getColumn(columnName: string) {
if (!this.columnDefinitions)
return null;
const ret = this.columnDefinitions.find(t => const ret = this.columnDefinitions.find(t =>
{ {
return t.columnName == columnName; return t.columnName == columnName;

View File

@ -1,10 +1,10 @@
import { Directive } from '@angular/core'; import { Directive, TemplateRef } from '@angular/core';
@Directive({ @Directive({
selector: '[psDataGridLoader]' selector: '[psDataGridLoader]'
}) })
export class DataGridLoaderDirective { export class DataGridLoaderDirective {
constructor() { } constructor(public template: TemplateRef<any>) { }
} }

View File

@ -46,8 +46,11 @@
</ng-template> </ng-template>
<ng-template #theModal let-command let-loading="loading"> <ng-template #theModal let-command let-loading="loading">
<form ngNativeValidate >
Name Name
<input type="text" [attr.disabled]="loading" [(ngModel)]="command.name" placeholder="Enter a merchant name" class="form-control"> <input type="text" required [disabled]="loading" name="name" [(ngModel)]="command.name" placeholder="Enter a merchant name" class="form-control" >
Address Address
<input type="text" required [attr.disabled]="loading" [(ngModel)]="command.address" placeholder="Enter the merchant's address" class="form-control"> <input type="text" required [disabled]="loading" name="address" [(ngModel)]="command.address" placeholder="Enter the merchant's address" class="form-control" >
</form>
</ng-template> </ng-template>

View File

@ -1,7 +1,13 @@
<psbx-spinner psDataGridLoader></psbx-spinner>
<ps-data-grid [dataSource]="merchantDataSource" [(columns)]="columns" <ps-data-grid [dataSource]="merchantDataSource" [(columns)]="columns"
tableClasses="table table-sm table-dark table-striped table-bordered"> tableClasses="table table-sm table-dark table-striped table-bordered">
<psbx-spinner *psDataGridLoader></psbx-spinner>
<ng-container *psDataGridHeader> <ng-container *psDataGridHeader>
<button class="btn-warning btn" psbxCommandModal commandTitle="Adding a new merchant" commandText="Add" <button class="btn-warning btn" psbxCommandModal commandTitle="Adding a new merchant" commandText="Add"
[dataSource]="merchantDataSource" command="addMerchant" [template]="theModal">Create a new record</button> [dataSource]="merchantDataSource" command="addMerchant" [template]="theModal">Create a new record</button>
@ -10,6 +16,18 @@
<ng-container psDataGridCol="id"> <ng-container psDataGridCol="id">
<div *psDataGridColHeader>ID</div> <div *psDataGridColHeader>ID</div>
<div *psDataGridCell="let model">{{model.id}}</div> <div *psDataGridCell="let model">{{model.id}}</div>
<!-- <psfa-ds-sort-icon *psDataGridColSort [sortPath]="id" [dataSource]="merchantDataSource">
</psfa-ds-sort-icon> -->
<!-- <psbx-ds-text-filter *psDataGridCellFilter [dataSource]="merchantDataSource"></psbx-ds-text-filter>
<psdb-ds-date-filter *psDataGridCellFilter></psdb-ds-date-filter>
<psdb-ds-date-range-filter *psDataGridCellFilter></psdb-ds-date-range-filter>
<psbx-ds-number-filter *psDataGridCellFilter></psbx-ds-number-filter>
<psbx-ds-multi-select-filter [dataSource]="merchantDataSource" [selectDataSource]="selectDataSource" [valueField]="id">
<ng-container *psSelectOption="let option">{{ option.name }}</ng-container>
</psbx-ds-multi-select-filter> -->
</ng-container> </ng-container>
<ng-container psDataGridCol="name"> <ng-container psDataGridCol="name">
@ -27,7 +45,6 @@
<!-- <button class="btn-warning btn" psbxCommandModal [commandTitle]="'Change ' + model.name + ' name'" commandText="Update" <!-- <button class="btn-warning btn" psbxCommandModal [commandTitle]="'Change ' + model.name + ' name'" commandText="Update"
[dataSource]="merchantDataSource" command="changeMerchant" [model]="model" [template]="changeName">Change</button> --> [dataSource]="merchantDataSource" command="changeMerchant" [model]="model" [template]="changeName">Change</button> -->
<button class="btn-danger btn" (click)="removeMerchant(model.id)">Remove</button> <button class="btn-danger btn" (click)="removeMerchant(model.id)">Remove</button>
</ng-container> </ng-container>
</ng-container> </ng-container>
@ -49,8 +66,22 @@
<ng-template #theModal let-command let-loading="loading"> <ng-template #theModal let-command let-loading="loading">
Name <label for="name">Name</label>
<input type="text" [attr.disabled]="loading" [(ngModel)]="command.name" placeholder="Enter a merchant name" class="form-control"> <input type="text" required [attr.disabled]="loading" [(ngModel)]="command.name" placeholder="Enter a merchant name"
Address class="form-control" >
<input type="text" [attr.disabled]="loading" [(ngModel)]="command.address" placeholder="Enter the merchant's address" class="form-control"> <label for="address">Address</label>
<input type="text" required [attr.disabled]="loading" [(ngModel)]="command.address" placeholder="Enter the merchant's address"
class="form-control" >
</ng-template> </ng-template>
<!-- <ng-template #theModal let-command let-form let-loading="loading">
<label for="name">Name</label>
<input type="text" required [attr.disabled]="loading" [(ngModel)]="command.name" name="name" placeholder="Enter a merchant name"
class="form-control" #nameField="ngModel" [class.field-error]="form.submitted && form.invalid">
<label for="address">Address</label>
<input type="text" required [attr.disabled]="loading" [(ngModel)]="command.address" name="address" placeholder="Enter the merchant's address"
class="form-control" #addressField="ngModel" [class.field-error]="form.submitted && form.invalid">
</ng-template> -->

View File

@ -0,0 +1,3 @@
.ng-invalid:not(form).ng-touched{
border: 1px solid red;
}