ds-command

This commit is contained in:
David Lebee
2021-08-18 16:38:26 -04:00
parent 11cdfd131a
commit 222e0be33b
20 changed files with 493 additions and 5 deletions
@@ -0,0 +1,10 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[psDsCommandContent]'
})
export class DsCommandContentDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,10 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[psDsCommandError]'
})
export class DsCommandErrorDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,10 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[psDsCommandFooter]'
})
export class DsCommandFooterDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,10 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[psDsCommandNoCommand]'
})
export class DsCommandNoCommandDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,11 @@
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[psDsCommandSubmit]'
})
export class DsCommandSubmitDirective {
constructor(public element: ElementRef) { }
}
@@ -0,0 +1,10 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[psDsCommandValidation]'
})
export class DsCommandValidationDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,30 @@
<ng-container *ngIf="command; else noCommand">
<ng-container *ngIf="hasContent" [ngTemplateOutlet]="contentTemplate"
[ngTemplateOutletContext]="{ $implicit: command, loading: isLoading }">
</ng-container>
</ng-container>
<ng-container *ngIf="lastValidationResult">
<ng-container *ngIf="hasValidationTemplate" [ngTemplateOutlet]="validationTemplate"
[ngTemplateOutletContext]="{ $implicit: lastValidationResult }">
</ng-container>
</ng-container>
<ng-container *ngIf="lastErrorMessage && errorContent && errorContent.template">
<ng-container [ngTemplateOutlet]="errorContent.template"
[ngTemplateOutletContext]="{ $implicit: lastErrorMessage }">
</ng-container>
</ng-container>
<ng-container *ngIf="hasFooterTemplate" [ngTemplateOutlet]="footerTemplate"
[ngTemplateOutletContext]="{ $implicit: command, loading: isLoading }">
</ng-container>
<ng-template #noCommand>
<ng-container *ngIf="noCommandContent && noCommandContent.template">
<ng-container [ngTemplateOutlet]="noCommandContent.template">
</ng-container>
</ng-container>
</ng-template>
@@ -0,0 +1,182 @@
import { Component, ContentChild, ContentChildren, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, QueryList } from '@angular/core';
import { IDataSource, IDataSourceValidationError } from '@poweredsoft/data';
import { Subscription } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { DsCommandContentDirective } from './directives/ds-command-content.directive';
import { DsCommandErrorDirective } from './directives/ds-command-error.directive';
import { DsCommandFooterDirective } from './directives/ds-command-footer.directive';
import { DsCommandNoCommandDirective } from './directives/ds-command-no-command.directive';
import { DsCommandSubmitDirective } from './directives/ds-command-submit.directive';
import { DsCommandValidationDirective } from './directives/ds-command-validation.directive';
export interface DsCommandPropertyError
{
name: string,
errors: string[];
}
@Component({
selector: 'ps-ds-command',
templateUrl: './ds-command.component.html',
styleUrls: ['./ds-command.component.scss']
})
export class DsCommandComponent implements OnInit, OnDestroy {
@Input() dataSource: IDataSource<any>;
@Input() name: string;
@Input() model: any;
@Input() refreshOnSuccess: boolean;
@Input() resolveCommand: boolean;
@Output() success = new EventEmitter<any>();
@Output() loading = new EventEmitter<boolean>();
@Output() commandChange = new EventEmitter<any>();
@ContentChild(DsCommandContentDirective) commandContent: DsCommandContentDirective;
@ContentChild(DsCommandFooterDirective) footerContent: DsCommandFooterDirective;
@ContentChild(DsCommandNoCommandDirective) noCommandContent: DsCommandNoCommandDirective;
@ContentChild(DsCommandErrorDirective) errorContent: DsCommandErrorDirective;
@ContentChild(DsCommandValidationDirective) validationDirective: DsCommandValidationDirective;
@ContentChildren(DsCommandSubmitDirective) submitDirectives: QueryList<DsCommandSubmitDirective>;
lastErrorMessage: string;
lastValidationResult: DsCommandPropertyError[];
protected _command: any = null;
private _loading = false;
private _validationErrorSubscription: Subscription;
private _notifyMessageSubscription: Subscription;
@Input() set command(value: any) {
if (this._command != value) {
this._command = value;
this.commandChange.emit(value);
}
}
get noCommand() {
return this._command ? true : false;
}
get command() {
return this._command;
}
get hasContent() {
return this.commandContent != null && this.commandContent.template != null;
}
get footerTemplate() {
return this.footerContent.template;
}
get hasFooterTemplate() {
return this.footerContent?.template != null;
}
get hasValidationTemplate() {
return this.validationDirective != null && this.validationDirective.template != null;
}
get validationTemplate() {
return this.validationDirective.template;
}
get contentTemplate() {
return this.commandContent.template;
}
constructor() {
}
ngOnDestroy() {
this._validationErrorSubscription?.unsubscribe();
this._notifyMessageSubscription?.unsubscribe();
}
ngOnInit(): void {
this._validationErrorSubscription = this.dataSource
.validationError$.subscribe(validationResult => {
this.lastValidationResult = Object.keys(validationResult.errors).reduce<DsCommandPropertyError[]>((prev, attr) => {
prev.push({
name: attr,
errors: validationResult.errors[attr]
})
return prev;
}, []);
});
this._notifyMessageSubscription = this.dataSource.notifyMessage$.subscribe(message => {
if (message.type != 'info')
this.lastErrorMessage = message.message;
});
const shouldResolve = this.resolveCommand === undefined ? true : this.resolveCommand;
if (shouldResolve)
this.resolveModel();
}
resolveModel() {
this.dataSource.resolveCommandModelByName({
model: this.model,
command: this.name
}).subscribe(
commandModel => {
this.command = commandModel;
},
_ => { }
);
}
private changeLoadingState(loading: boolean) {
if (loading != this._loading) {
this._loading = loading;
this.loading.emit(loading);
}
}
@HostListener("click", ["$event"])
childClicked($event: MouseEvent) {
const element = $event.target;
const found = this.submitDirectives
.toArray()
.find(t => t.element.nativeElement == element);
if (found != null)
this.execute()
}
execute() {
// secure from double send.
if (this._loading)
return;
this.changeLoadingState(true);
this.lastValidationResult = null;
this.lastErrorMessage = null;
this.dataSource.executeCommandByName(this.name, this._command)
.pipe(
finalize(() => this.changeLoadingState(false))
)
.subscribe(
commandResult => {
this.success.emit(commandResult);
if (this.refreshOnSuccess)
this.dataSource.refresh();
},
_ => {
// we just catch the error, this way its not uncatched its handled via the other
// subscriptions.
}
);
}
get isLoading() {
return this._loading;
}
}
@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DsCommandComponent } from './ds-command.component';
import { DsCommandContentDirective } from './directives/ds-command-content.directive';
import { DsCommandSubmitDirective } from './directives/ds-command-submit.directive';
import { DsCommandValidationDirective } from './directives/ds-command-validation.directive';
import { DsCommandFooterDirective } from './directives/ds-command-footer.directive';
import { DsCommandErrorDirective } from './directives/ds-command-error.directive';
import { DsCommandNoCommandDirective } from './directives/ds-command-no-command.directive';
@NgModule({
declarations: [DsCommandComponent, DsCommandContentDirective, DsCommandSubmitDirective, DsCommandValidationDirective, DsCommandFooterDirective, DsCommandErrorDirective, DsCommandNoCommandDirective],
imports: [
CommonModule
],
exports: [DsCommandComponent, DsCommandContentDirective, DsCommandSubmitDirective, DsCommandValidationDirective, DsCommandFooterDirective, DsCommandErrorDirective, DsCommandNoCommandDirective]
})
export class DsCommandModule { }
@@ -35,4 +35,14 @@ export * from './lib/view/view.module';
export * from './lib/view/view.component';
export * from './lib/view/directives/view-content.directive';
export * from './lib/view/directives/view-loading.directive';
export * from './lib/view/directives/view-no-records.directive';
export * from './lib/view/directives/view-no-records.directive';
// ds-command
export * from './lib/ds-command/ds-command.module';
export * from './lib/ds-command/ds-command.component';
export * from './lib/ds-command/directives/ds-command-content.directive';
export * from './lib/ds-command/directives/ds-command-submit.directive';
export * from './lib/ds-command/directives/ds-command-validation.directive';
export * from './lib/ds-command/directives/ds-command-footer.directive';
export * from './lib/ds-command/directives/ds-command-no-command.directive';
export * from './lib/ds-command/directives/ds-command-error.directive';