Merge pull request #2146 from abpframework/refactor/forms

refactor: form buttons and validations
pull/2165/head
Yasin Aydın 5 years ago committed by GitHub
commit 97d9d1b1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,20 +19,20 @@
"@abp/ng.tenant-management.config": "^1.0.2",
"@abp/ng.theme.shared": "^1.0.2",
"@angular-builders/jest": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.15",
"@angular-devkit/build-ng-packagr": "~0.803.15",
"@angular/animations": "~8.2.12",
"@angular-devkit/build-angular": "~0.803.18",
"@angular-devkit/build-ng-packagr": "~0.803.18",
"@angular/animations": "~8.2.13",
"@angular/cdk": "^8.2.3",
"@angular/cli": "~8.3.15",
"@angular/common": "~8.2.12",
"@angular/compiler": "~8.2.12",
"@angular/compiler-cli": "~8.2.12",
"@angular/core": "~8.2.12",
"@angular/forms": "~8.2.12",
"@angular/language-service": "~8.2.12",
"@angular/platform-browser": "~8.2.12",
"@angular/platform-browser-dynamic": "~8.2.12",
"@angular/router": "~8.2.12",
"@angular/cli": "~8.3.18",
"@angular/common": "~8.2.13",
"@angular/compiler": "~8.2.13",
"@angular/compiler-cli": "~8.2.13",
"@angular/core": "~8.2.13",
"@angular/forms": "~8.2.13",
"@angular/language-service": "~8.2.13",
"@angular/platform-browser": "~8.2.13",
"@angular/platform-browser-dynamic": "~8.2.13",
"@angular/router": "~8.2.13",
"@fortawesome/fontawesome-free": "^5.11.2",
"@ng-bootstrap/ng-bootstrap": "^5.1.0",
"@ngneat/spectator": "^4.5.0",

@ -1,4 +1,4 @@
<form [formGroup]="form" (ngSubmit)="onSubmit()" [mapErrorsFn]="mapErrorsFn">
<form [formGroup]="form" (ngSubmit)="onSubmit()" [mapErrorsFn]="mapErrorsFn" validateOnSubmit>
<div class="form-group">
<label for="current-password">{{ 'AbpIdentity::DisplayName:CurrentPassword' | abpLocalization }}</label
><span> * </span
@ -18,6 +18,7 @@
buttonClass="btn btn-primary color-white"
buttonType="submit"
[loading]="inProgress"
[disabled]="form?.invalid"
>{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
>
</form>

@ -5,7 +5,7 @@
{{ 'AbpAccount::AreYouANewUser' | abpLocalization }}
<a class="text-decoration-none" routerLink="/account/register">{{ 'AbpAccount::Register' | abpLocalization }}</a>
</strong>
<form [formGroup]="form" (ngSubmit)="onSubmit()" novalidate class="mt-4">
<form [formGroup]="form" (ngSubmit)="onSubmit()" validateOnSubmit class="mt-4">
<div class="form-group">
<label for="login-input-user-name-or-email-address">{{
'AbpAccount::UserNameOrEmailAddress' | abpLocalization

@ -1,4 +1,4 @@
<form novalidate *ngIf="form" [formGroup]="form" (ngSubmit)="submit()">
<form validateOnSubmit *ngIf="form" [formGroup]="form" (ngSubmit)="submit()">
<div class="form-group">
<label for="username">{{ 'AbpIdentity::DisplayName:UserName' | abpLocalization }}</label
><span> * </span
@ -38,6 +38,7 @@
iconClass="fa fa-check"
buttonClass="btn btn-primary color-white"
[loading]="inProgress"
[disabled]="form?.invalid || form?.pristine"
>
{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
>

@ -5,7 +5,7 @@
{{ 'AbpAccount::AlreadyRegistered' | abpLocalization }}
<a class="text-decoration-none" routerLink="/account/login">{{ 'AbpAccount::Login' | abpLocalization }}</a>
</strong>
<form [formGroup]="form" (ngSubmit)="onSubmit()" novalidate class="mt-4">
<form [formGroup]="form" (ngSubmit)="onSubmit()" validateOnSubmit class="mt-4">
<div class="form-group">
<label for="input-user-name">{{ 'AbpAccount::UserName' | abpLocalization }}</label
><span> * </span

@ -25,7 +25,7 @@
</div>
</div>
<abp-modal [(visible)]="isModalVisible" size="md">
<abp-modal size="md" [(visible)]="isModalVisible" [busy]="inProgress">
<ng-template #abpHeader>
<h5>Switch Tenant</h5>
</ng-template>
@ -44,8 +44,8 @@
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="save()">
<abp-button buttonType="button" buttonClass="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpTenantManagement::Save' | abpLocalization }}</span>
</button>
</abp-button>
</ng-template>
</abp-modal>

@ -3,31 +3,27 @@ import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngxs/store';
import { throwError } from 'rxjs';
import { catchError, take } from 'rxjs/operators';
import { catchError, take, finalize } from 'rxjs/operators';
import snq from 'snq';
import { AccountService } from '../../services/account.service';
@Component({
selector: 'abp-tenant-box',
templateUrl: './tenant-box.component.html'
templateUrl: './tenant-box.component.html',
})
export class TenantBoxComponent implements OnInit {
constructor(
private store: Store,
private toasterService: ToasterService,
private accountService: AccountService
) {}
tenant = {} as ABP.BasicItem;
tenantName: string;
isModalVisible: boolean;
inProgress: boolean;
constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {}
ngOnInit() {
this.tenant =
this.store.selectSnapshot(SessionState.getTenant) ||
({} as ABP.BasicItem);
this.tenant = this.store.selectSnapshot(SessionState.getTenant) || ({} as ABP.BasicItem);
this.tenantName = this.tenant.name || '';
}
@ -36,38 +32,33 @@ export class TenantBoxComponent implements OnInit {
}
save() {
if (this.tenant.name) {
if (this.tenant.name && !this.inProgress) {
this.inProgress = true;
this.accountService
.findTenant(this.tenant.name)
.pipe(
finalize(() => (this.inProgress = false)),
take(1),
catchError(err => {
this.toasterService.error(
snq(
() => err.error.error_description,
'AbpUi::DefaultErrorMessage'
),
'AbpUi::Error'
snq(() => err.error.error_description, 'AbpUi::DefaultErrorMessage'),
'AbpUi::Error',
);
return throwError(err);
})
}),
)
.subscribe(({ success, tenantId }) => {
if (success) {
this.tenant = {
id: tenantId,
name: this.tenant.name
name: this.tenant.name,
};
this.tenantName = this.tenant.name;
this.isModalVisible = false;
} else {
this.toasterService.error(
'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
'AbpUi::Error',
{
messageLocalizationParams: [this.tenant.name]
}
);
this.toasterService.error('AbpUiMultiTenancy::GivenTenantIsNotAvailable', 'AbpUi::Error', {
messageLocalizationParams: [this.tenant.name],
});
this.tenant = {} as ABP.BasicItem;
}
this.store.dispatch(new SetTenant(success ? this.tenant : null));

@ -2,19 +2,19 @@
"name": "@abp/ng.core",
"version": "1.0.2",
"dependencies": {
"@ngxs/router-plugin": "^3.5.0",
"@ngxs/storage-plugin": "^3.5.0",
"@ngxs/store": "^3.5.0",
"angular-oauth2-oidc": "^8.0.1",
"@ngxs/router-plugin": "^3.5.1",
"@ngxs/storage-plugin": "^3.5.1",
"@ngxs/store": "^3.5.1",
"angular-oauth2-oidc": "^8.0.4",
"just-compare": "^1.3.0",
"just-clone": "3.1.0",
"snq": "^1.0.3"
},
"peerDependencies": {
"@angular/common": "~8.1.2",
"@angular/core": "~8.1.2",
"@angular/forms": "~8.1.2",
"@angular/router": "~8.1.2",
"@angular/common": ">=8.0.0 <9.0.0",
"@angular/core": ">=8.0.0 <9.0.0",
"@angular/forms": ">=8.0.0 <9.0.0",
"@angular/router": ">=8.0.0 <9.0.0",
"rxjs": "~6.4.0"
},
"publishConfig": {

@ -7,7 +7,7 @@ import {
OnDestroy,
OnInit,
Output,
Self
Self,
} from '@angular/core';
import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms';
import { fromEvent } from 'rxjs';
@ -18,7 +18,7 @@ type Controls = { [key: string]: FormControl } | FormGroup[];
@Directive({
// tslint:disable-next-line: directive-selector
selector: 'form[ngSubmit][formGroup]'
selector: 'form[ngSubmit][formGroup]',
})
export class FormSubmitDirective implements OnInit, OnDestroy {
@Input()
@ -31,7 +31,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
constructor(
@Self() private formGroupDirective: FormGroupDirective,
private host: ElementRef<HTMLFormElement>,
private cdRef: ChangeDetectorRef
private cdRef: ChangeDetectorRef,
) {}
ngOnInit() {
@ -44,7 +44,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
.pipe(
debounceTime(200),
filter((key: KeyboardEvent) => key && key.key === 'Enter'),
takeUntilDestroy(this)
takeUntilDestroy(this),
)
.subscribe(() => {
if (!this.executedNgSubmit) {
@ -53,17 +53,6 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
this.executedNgSubmit = false;
});
fromEvent(this.host.nativeElement, 'submit')
.pipe(
takeUntilDestroy(this),
filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string')
)
.subscribe(() => {
if (!this.executedNgSubmit) {
this.markAsDirty();
}
});
}
ngOnDestroy(): void {}

@ -4,7 +4,7 @@
</ng-template>
<ng-template #abpBody>
<form *ngIf="form" (ngSubmit)="save()" [formGroup]="form">
<form *ngIf="form" (ngSubmit)="save()" [formGroup]="form" validateOnSubmit>
<div
class="row my-3"
*ngFor="let feature of features$ | async; let i = index"
@ -25,7 +25,7 @@
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="save()">
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid || modalBusy" (click)="save()">
{{ 'AbpFeatureManagement::Save' | abpLocalization }}
</abp-button>
</ng-template>

@ -76,6 +76,8 @@ export class FeatureManagementComponent {
}
save() {
if (this.modalBusy) return;
this.modalBusy = true;
let features = this.store.selectSnapshot(FeatureManagementState.getFeatures);

@ -11,7 +11,7 @@ import { FeatureManagementService } from '../services/feature-management.service
export class FeatureManagementState {
@Selector()
static getFeatures({ features }: FeatureManagement.State) {
return features;
return features || [];
}
constructor(private featureManagementService: FeatureManagementService) {}

@ -106,7 +106,7 @@
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" (ngSubmit)="save()">
<form #formRef [formGroup]="form" (ngSubmit)="save()" validateOnSubmit>
<div class="form-group">
<label for="role-name">{{ 'AbpIdentity::RoleName' | abpLocalization }}</label
><span> * </span>
@ -133,7 +133,9 @@
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="save()">{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="onClickSaveButton()">{{
'AbpIdentity::Save' | abpLocalization
}}</abp-button>
</ng-template>
</abp-modal>

@ -1,6 +1,6 @@
import { ABP } from '@abp/ng.core';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
import { Component, TemplateRef, ViewChild, OnInit } from '@angular/core';
import { Component, TemplateRef, ViewChild, OnInit, ContentChild, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
@ -40,8 +40,8 @@ export class RolesComponent implements OnInit {
sortKey = '';
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@ViewChild('formRef', { static: false, read: ElementRef })
formRef: ElementRef<HTMLFormElement>;
constructor(private confirmationService: ConfirmationService, private fb: FormBuilder, private store: Store) {}
@ -122,4 +122,8 @@ export class RolesComponent implements OnInit {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
onClickSaveButton() {
this.formRef.nativeElement.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
}
}

@ -129,12 +129,14 @@
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="save()">{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button>
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="isDisabledSaveButton">{{
'AbpIdentity::Save' | abpLocalization
}}</abp-button>
</ng-template>
</abp-modal>
<ng-template #tenantModalTemplate>
<form [formGroup]="tenantForm" (ngSubmit)="save()">
<form [formGroup]="tenantForm" (ngSubmit)="save()" validateOnSubmit>
<div class="mt-2">
<div class="form-group">
<label for="name">{{ 'AbpTenantManagement::TenantName' | abpLocalization }}</label>
@ -145,7 +147,7 @@
</ng-template>
<ng-template #connectionStringModalTemplate>
<form [formGroup]="defaultConnectionStringForm" (ngSubmit)="save()">
<form [formGroup]="defaultConnectionStringForm" (ngSubmit)="save()" validateOnSubmit>
<label class="mt-2">
<div class="form-group">
<div class="custom-checkbox custom-control mb-2">
@ -155,6 +157,7 @@
class="custom-control-input"
formControlName="useSharedDatabase"
autofocus
(ngModelChange)="onSharedDatabaseChange($event)"
/>
<label for="useSharedDatabase" class="custom-control-label">{{
'AbpTenantManagement::DisplayName:UseSharedDatabase' | abpLocalization

@ -1,6 +1,6 @@
import { ABP } from '@abp/ng.core';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Component, OnInit, TemplateRef, ViewChild, ChangeDetectorRef } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
@ -16,7 +16,7 @@ import { TenantManagementService } from '../../services/tenant-management.servic
import { TenantManagementState } from '../../states/tenant-management.state';
interface SelectedModalContent {
type: string;
type: 'saveConnStr' | 'saveTenant';
title: string;
template: TemplateRef<any>;
}
@ -74,6 +74,18 @@ export class TenantsComponent implements OnInit {
@ViewChild('connectionStringModalTemplate', { static: false })
connectionStringModalTemplate: TemplateRef<any>;
get isDisabledSaveButton(): boolean {
if (!this.selectedModalContent) return false;
if (this.selectedModalContent.type === 'saveConnStr' && this.defaultConnectionStringForm.invalid) {
return true;
} else if (this.selectedModalContent.type === 'saveTenant' && this.tenantForm.invalid) {
return true;
} else {
return false;
}
}
constructor(
private confirmationService: ConfirmationService,
private tenantService: TenantManagementService,
@ -103,7 +115,7 @@ export class TenantsComponent implements OnInit {
});
}
openModal(title: string, template: TemplateRef<any>, type: string) {
openModal(title: string, template: TemplateRef<any>, type: 'saveConnStr' | 'saveTenant') {
this.selectedModalContent = {
title,
template,
@ -224,4 +236,15 @@ export class TenantsComponent implements OnInit {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
onSharedDatabaseChange(value: boolean) {
if (!value) {
setTimeout(() => {
const defaultConnectionString = document.getElementById('defaultConnectionString') as HTMLInputElement;
if (defaultConnectionString) {
defaultConnectionString.focus();
}
}, 0);
}
}
}

@ -5,10 +5,10 @@
"@abp/ng.core": "^1.0.2",
"@angular/cdk": "^8.2.3",
"@fortawesome/fontawesome-free": "^5.11.2",
"@ng-bootstrap/ng-bootstrap": "^5.1.2",
"@ng-bootstrap/ng-bootstrap": "^5.1.4",
"@ngx-validate/core": "^0.0.7",
"bootstrap": "^4.3.1",
"chart.js": "^2.8.0",
"chart.js": "^2.9.2",
"font-awesome": "^4.7.0",
"ngx-perfect-scrollbar": "^8.0.0",
"primeicons": "^2.0.0",

@ -7,6 +7,7 @@ import { ABP } from '@abp/ng.core';
template: `
<button
#button
[id]="buttonId"
[attr.type]="buttonType"
[ngClass]="buttonClass"
[disabled]="loading || disabled"
@ -19,6 +20,9 @@ import { ABP } from '@abp/ng.core';
`,
})
export class ButtonComponent implements OnInit {
@Input()
buttonId = '';
@Input()
buttonClass = 'btn btn-primary';

Loading…
Cancel
Save