diff --git a/docs/en/UI/Angular/Component-Replacement.md b/docs/en/UI/Angular/Component-Replacement.md index cf0041ab72..6df847d6d0 100644 --- a/docs/en/UI/Angular/Component-Replacement.md +++ b/docs/en/UI/Angular/Component-Replacement.md @@ -547,7 +547,3 @@ The final UI looks like below: ## See Also - [How to Replace PermissionManagementComponent](./Permission-Management-Component-Replacement.md) - -## What's Next? - -- [Custom Setting Page](./Custom-Setting-Page.md) diff --git a/docs/en/UI/Angular/Config-State.md b/docs/en/UI/Angular/Config-State.md index b1a882d43a..8014cd9b14 100644 --- a/docs/en/UI/Angular/Config-State.md +++ b/docs/en/UI/Angular/Config-State.md @@ -190,7 +190,3 @@ Please refer to `Config.Environment` type for all the properties you can pass to - [Settings](./Settings.md) - [Features](./Features.md) - -## What's Next? - -- [HTTP Requests](./Http-Requests) diff --git a/docs/en/UI/Angular/Confirmation-Service.md b/docs/en/UI/Angular/Confirmation-Service.md index 16dfa9fd94..ef48332580 100644 --- a/docs/en/UI/Angular/Confirmation-Service.md +++ b/docs/en/UI/Angular/Confirmation-Service.md @@ -180,8 +180,3 @@ clear( ``` - `status` parameter is the value of the confirmation closing event. - - -## What's Next? - -- [Toast Overlay](./Toaster-Service.md) diff --git a/docs/en/UI/Angular/Content-Projection-Service.md b/docs/en/UI/Angular/Content-Projection-Service.md index 7e03776835..ee64f57e20 100644 --- a/docs/en/UI/Angular/Content-Projection-Service.md +++ b/docs/en/UI/Angular/Content-Projection-Service.md @@ -71,8 +71,3 @@ projectContent | TemplateRef>( - `projectionStrategy` parameter is the primary focus here and is explained above. - `injector` parameter is the `Injector` instance you can pass to the projected content. It is not used in `TemplateProjectionStrategy`. - - -## What's Next? - -- [Confirmation Popup](./Confirmation-Service.md) diff --git a/docs/en/UI/Angular/Cross-Origin-Strategy.md b/docs/en/UI/Angular/Cross-Origin-Strategy.md index e626bf5011..5edcbed9e6 100644 --- a/docs/en/UI/Angular/Cross-Origin-Strategy.md +++ b/docs/en/UI/Angular/Cross-Origin-Strategy.md @@ -51,10 +51,3 @@ CROSS_ORIGIN_STRATEGY.UseCredentials(integrity?: string) ``` `crossorigin` will be set as `"use-credentials"` and `integrity` is optional. - - - - -## What's Next? - -- [LoadingStrategy](./Loading-Strategy.md) diff --git a/docs/en/UI/Angular/Dom-Insertion-Service.md b/docs/en/UI/Angular/Dom-Insertion-Service.md index d56e4f8b0e..78e01c59fb 100644 --- a/docs/en/UI/Angular/Dom-Insertion-Service.md +++ b/docs/en/UI/Angular/Dom-Insertion-Service.md @@ -134,7 +134,3 @@ has(content: string): boolean The `has` method returns a boolean value that indicates the given content has already been added to the DOM or not. - `content` parameter is the content of the inserted `HTMLScriptElement` or `HTMLStyleElement` element. - -## What's Next? - -- [Lazy Loading Scripts & Styles](./Lazy-Load-Service.md) diff --git a/docs/en/UI/Angular/Environment.md b/docs/en/UI/Angular/Environment.md index 1b78a11cc6..04bf8f98c4 100644 --- a/docs/en/UI/Angular/Environment.md +++ b/docs/en/UI/Angular/Environment.md @@ -101,8 +101,3 @@ export interface RemoteEnv { * `customMergeFn`: You can also provide your own merge function as shown in the example. It will take two parameters, `localEnv: Partial` and `remoteEnv` and it needs to return a `Config.Environment` object. * `method`: HTTP method to be used when retrieving environment config. Default: `GET` * `headers`: If extra headers are needed for the request, it can be set through this field. - - -## What's Next? - -- [About Feature Libraries](./Feature-Libraries.md) diff --git a/docs/en/UI/Angular/Feature-Libraries.md b/docs/en/UI/Angular/Feature-Libraries.md index 937bae7245..bdfff93371 100644 --- a/docs/en/UI/Angular/Feature-Libraries.md +++ b/docs/en/UI/Angular/Feature-Libraries.md @@ -90,9 +90,3 @@ Depending on the library, the `.forLazy` static method may also receive some opt --- 1 _Libraries expect to work at a predefined path. Please check [how to patch a navigation element](./Modifying-the-Menu.md#how-to-patch-or-remove-a-navigation-element), if you want to use a different path from the default one (e.g. '/identity')._ [↩](#a-modify-route) - ---- - -## What's Next? - -- [Service Proxies](./Service-Proxies.md) diff --git a/docs/en/UI/Angular/Features.md b/docs/en/UI/Angular/Features.md index 930fc2e527..3278bfd988 100644 --- a/docs/en/UI/Angular/Features.md +++ b/docs/en/UI/Angular/Features.md @@ -31,7 +31,3 @@ const defaultLang = this.config.getFeature("Identity.TwoFactor"); ``` You can then check the value of the feature to perform your logic. Please note that **feature keys are case-sensitive**. - -## What's Next? - -- [Permission Management](./Permission-Management.md) diff --git a/docs/en/UI/Angular/Form-Validation.md b/docs/en/UI/Angular/Form-Validation.md new file mode 100644 index 0000000000..38e7969eea --- /dev/null +++ b/docs/en/UI/Angular/Form-Validation.md @@ -0,0 +1,140 @@ +# Form Validation + +Reactive forms in ABP Angular UI are validated by [ngx-validate](https://www.npmjs.com/package/@ngx-validate/core) and helper texts are shown automatically based on validation rules and error blueprints. You do not have to add any elements or components to your templates. The library handles that for you. Here is how the experience is: + +The ngx-validate library validates an Angular reactive form and an error text appears under each wrong input based on the validation rule and the error blueprint. + +## How to Add New Error Messages + +You can add a new error message by providing the `VALIDATION_BLUEPRINTS` injection token from your root module. + +```js +import { VALIDATION_BLUEPRINTS } from "@ngx-validate/core"; + +@NgModule({ + // rest of the module metadata + + providers: [ + // other providers + { + provide: VALIDATION_BLUEPRINTS, + useValue: { + uniqueUsername: "::AlreadyExists[{%{{{ username }}}%}]", + }, + }, + ], +}) +export class AppModule {} +``` + +When a [validator](https://angular.io/guide/form-validation#defining-custom-validators) or an [async validator](https://angular.io/guide/form-validation#creating-asynchronous-validators) returns an error with the key given to the error blueprints (`uniqueUsername` here), the validation library will be able to display an error message after localizing according to the given key and interpolation params. The result will look like this: + +An already taken username is entered while creating new user and a custom error message appears under the input after validation. + +In this example; + +- Localization key is `::AlreadyExists`. +- The interpolation param is `username`. +- Localization resource is defined as `"AlreadyExists": "Sorry, “{0}” already exists."`. +- And the validator should return `{ uniqueUsername: { username: "admin" } }` as the error object. + +## How to Change Existing Error Messages + +You can overwrite an existing error message by providing `VALIDATION_BLUEPRINTS` injection token from your root module. Let's imagine you have a custom localization resource for required inputs. + +```json +"RequiredInput": "Oops! We need this input." +``` + +To use this instead of the built-in required input message, all you need to do is the following. + +```js +import { VALIDATION_BLUEPRINTS } from "@ngx-validate/core"; + +@NgModule({ + // rest of the module metadata + + providers: [ + // other providers + { + provide: VALIDATION_BLUEPRINTS, + useValue: { + required: "::RequiredInput", + }, + }, + ], +}) +export class AppModule {} +``` + +The error message will look like this: + +A required field is cleared and the custom error message appears under the input. + +## How to Disable Validation on a Form + +If you want to validate a form manually, you can always disable automatic validation on it. All you need to do is place `skipValidation` on the form element. + +```html +
+ +
+``` + +## How to Disable Validation on a Specific Field + +Validation works on any element or component with a `formControl` or `formControlName` directive. You can disable automatic validation on a specific field by placing `skipValidation` on the input element or component. + +```html + +``` + +## How to Use a Custom Error Component + +First, build a custom error component. Extending the existing `ValidationErrorComponent` would make it easier. + +```js +import { ValidationErrorComponent } from "@abp/ng.theme.basic"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; + +@Component({ + selector: "app-validation-error", + template: ` +
+ {%{{{ error.message | abpLocalization: error.interpoliteParams }}}%} +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ErrorComponent extends ValidationErrorComponent {} +``` + +Then, declare and provide it in your root module. + +```js +import { VALIDATION_ERROR_TEMPLATE } from "@ngx-validate/core"; + +@NgModule({ + // rest of the module metadata + + declarations: [ + // other declarables + ErrorComponent, + ], + providers: [ + // other providers + { + provide: VALIDATION_ERROR_TEMPLATE, + useValue: ErrorComponent, + }, + ], +}) +export class AppModule {} +``` + +The error message will be bold and italic now: + +A required field is cleared and a bold and italic error message appears. diff --git a/docs/en/UI/Angular/HTTP-Requests.md b/docs/en/UI/Angular/HTTP-Requests.md index 2ac5aa1b49..ab1ac04097 100644 --- a/docs/en/UI/Angular/HTTP-Requests.md +++ b/docs/en/UI/Angular/HTTP-Requests.md @@ -203,7 +203,3 @@ getSomeCustomHeaderValue() { You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10). - -## What's Next? - -* [Localization](./Localization.md) diff --git a/docs/en/UI/Angular/Lazy-Load-Service.md b/docs/en/UI/Angular/Lazy-Load-Service.md index 344b446107..0e1f7928c7 100644 --- a/docs/en/UI/Angular/Lazy-Load-Service.md +++ b/docs/en/UI/Angular/Lazy-Load-Service.md @@ -204,10 +204,3 @@ load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Obser - `strategy` parameter is the primary focus here and is explained above. - `retryTimes` defines how many times the loading will be tried again before fail (_default: 2_). - `retryDelay` defines how much delay there will be between retries (_default: 1000_). - - - - -## What's Next? - -- [Projecting Angular Content](./Content-Projection-Service.md) diff --git a/docs/en/UI/Angular/List-Service.md b/docs/en/UI/Angular/List-Service.md index d534558363..7cc31a46fd 100644 --- a/docs/en/UI/Angular/List-Service.md +++ b/docs/en/UI/Angular/List-Service.md @@ -177,8 +177,3 @@ As of v3.0, with ngx-datatable, the `page` property has to be set as `0` for ini ``` **Important Note:** The `abp-table` is not removed, but is deprecated and will be removed in the future. Please consider switching to ngx-datatable. - - -## What's Next? - -- [Easy *ngFor trackBy](./Track-By-Service.md) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index d4af491b8d..0c43317728 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -319,7 +319,3 @@ Which locale files you add to `webpackInclude` magic comment, they will be inclu ## See Also * [Localization in ASP.NET Core](../../Localization.md) - -## What's Next? - -* [Settings](./Settings.md) diff --git a/docs/en/UI/Angular/Migration-Guide-v3.md b/docs/en/UI/Angular/Migration-Guide-v3.md index 6a7b86efcb..f13b0c966f 100644 --- a/docs/en/UI/Angular/Migration-Guide-v3.md +++ b/docs/en/UI/Angular/Migration-Guide-v3.md @@ -470,8 +470,3 @@ Some interfaces have long been marked as deprecated and now they are removed. - Please check if you are still using [anything listed in this issue](https://github.com/abpframework/abp/issues/4281) - -## What's Next? - -* [Quick Start](./Quick-Start.md) - diff --git a/docs/en/UI/Angular/Modifying-the-Menu.md b/docs/en/UI/Angular/Modifying-the-Menu.md index a3c356891e..b0a3b760fa 100644 --- a/docs/en/UI/Angular/Modifying-the-Menu.md +++ b/docs/en/UI/Angular/Modifying-the-Menu.md @@ -271,8 +271,3 @@ export class AppComponent { * Patched the languages dropdown element with new `requiredPolicy` and new `order`. * Removed the current user dropdown element. - - -## What's Next - -* [Component Replacement](./Component-Replacement.md) diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md index c3e54c5f83..e7e284bb0d 100644 --- a/docs/en/UI/Angular/Multi-Tenancy.md +++ b/docs/en/UI/Angular/Multi-Tenancy.md @@ -126,7 +126,3 @@ The app sends the `__tenant` header that contains the current tenant id on each ## See Also - [Multi Tenancy in ABP](../../Multi-Tenancy.md) - -## What's Next? - -- [Managing RxJS Subscriptions](./Subscription-Service.md) diff --git a/docs/en/UI/Angular/PWA-Configuration.md b/docs/en/UI/Angular/PWA-Configuration.md index a6aee244ba..918aa4970d 100644 --- a/docs/en/UI/Angular/PWA-Configuration.md +++ b/docs/en/UI/Angular/PWA-Configuration.md @@ -340,7 +340,3 @@ In case you want to cache other static files, please refer to the [service worke ### 3.2 Set Data Groups This part is unique to your project. We recommend being very careful about which endpoints to cache. Please refer to [service worker configuration document](https://angular.io/guide/service-worker-config#datagroups) on Angular.io for details. - -## What's Next? - -- [Config State](./Config-State.md) diff --git a/docs/en/UI/Angular/Permission-Management.md b/docs/en/UI/Angular/Permission-Management.md index a6d7d13c14..adcbafdd99 100644 --- a/docs/en/UI/Angular/Permission-Management.md +++ b/docs/en/UI/Angular/Permission-Management.md @@ -57,8 +57,4 @@ const routes: Routes = [ ]; ``` -Granted Policies are stored in the `auth` property of `ConfigState`. - -## What's Next? - -* [Multi Tenancy](./Multi-Tenancy.md) +Granted Policies are stored in the `auth` property of `ConfigState`. \ No newline at end of file diff --git a/docs/en/UI/Angular/Quick-Start.md b/docs/en/UI/Angular/Quick-Start.md index a58671156c..c17f069c5f 100644 --- a/docs/en/UI/Angular/Quick-Start.md +++ b/docs/en/UI/Angular/Quick-Start.md @@ -204,9 +204,3 @@ In addition, you can [deploy your application to certain targets using the Angul --- 1 _The compiled output will be placed under `/dist` in a folder by the project name._ [↩](#a-dist-folder-name) - ---- - -## What's Next? - -- [Environment Variables](./Environment.md) diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md index 92c4b01871..8f6b1c4d60 100644 --- a/docs/en/UI/Angular/Service-Proxies.md +++ b/docs/en/UI/Angular/Service-Proxies.md @@ -137,7 +137,3 @@ export class BookComponent implements OnInit { ``` > Please [see this article](https://github.com/abpframework/abp/blob/dev/docs/en/Blog-Posts/2020-09-07%20Angular-Service-Proxies/POST.md) to learn more about service proxies. - -## What's Next? - -- [PWA Configuration](./PWA-Configuration.md) diff --git a/docs/en/UI/Angular/Settings.md b/docs/en/UI/Angular/Settings.md index f3a5775838..0dd29025e5 100644 --- a/docs/en/UI/Angular/Settings.md +++ b/docs/en/UI/Angular/Settings.md @@ -53,7 +53,3 @@ const localizationSettings = this.config.getSettings("Localization"); ``` Beware though, **settings search is case-sensitive**. - -## What's Next? - -- [Features](./Features.md) diff --git a/docs/en/UI/Angular/Subscription-Service.md b/docs/en/UI/Angular/Subscription-Service.md index dee77cbf1e..e080f8f3fa 100644 --- a/docs/en/UI/Angular/Subscription-Service.md +++ b/docs/en/UI/Angular/Subscription-Service.md @@ -197,7 +197,3 @@ class DemoComponent implements OnInit { } } ``` - -## What's Next? - -- [Working with Lists](./List-Service.md) diff --git a/docs/en/UI/Angular/Toaster-Service.md b/docs/en/UI/Angular/Toaster-Service.md index 4d94b8cf6d..059fd720a8 100644 --- a/docs/en/UI/Angular/Toaster-Service.md +++ b/docs/en/UI/Angular/Toaster-Service.md @@ -249,7 +249,3 @@ Removes all open toasts. ## See Also - [Confirmation Popup](./Confirmation-Service.md) - -## What's Next? - -- [Modifying the Menu](./Modifying-the-Menu.md) diff --git a/docs/en/UI/Angular/Track-By-Service.md b/docs/en/UI/Angular/Track-By-Service.md index 7883be4754..a7df7b80b6 100644 --- a/docs/en/UI/Angular/Track-By-Service.md +++ b/docs/en/UI/Angular/Track-By-Service.md @@ -111,9 +111,3 @@ class DemoComponent { trackByTenantAccountId = trackByDeep('tenant', 'account', 'id'); } ``` - - - -## What's Next? - -- [Inserting Scripts & Styles to DOM](./Dom-Insertion-Service.md) diff --git a/docs/en/UI/Angular/images/form-validation---custom-error-template.gif b/docs/en/UI/Angular/images/form-validation---custom-error-template.gif new file mode 100644 index 0000000000..6d9f492ce4 Binary files /dev/null and b/docs/en/UI/Angular/images/form-validation---custom-error-template.gif differ diff --git a/docs/en/UI/Angular/images/form-validation---error-display-user-experience.gif b/docs/en/UI/Angular/images/form-validation---error-display-user-experience.gif new file mode 100644 index 0000000000..9070a0f730 Binary files /dev/null and b/docs/en/UI/Angular/images/form-validation---error-display-user-experience.gif differ diff --git a/docs/en/UI/Angular/images/form-validation---new-error-message.gif b/docs/en/UI/Angular/images/form-validation---new-error-message.gif new file mode 100644 index 0000000000..ae61215f61 Binary files /dev/null and b/docs/en/UI/Angular/images/form-validation---new-error-message.gif differ diff --git a/docs/en/UI/Angular/images/form-validation---overwrite-error-message.gif b/docs/en/UI/Angular/images/form-validation---overwrite-error-message.gif new file mode 100644 index 0000000000..b6af5a68b7 Binary files /dev/null and b/docs/en/UI/Angular/images/form-validation---overwrite-error-message.gif differ diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index e6bbf6a3b0..217badae8e 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -636,6 +636,10 @@ "text": "Localization", "path": "UI/Angular/Localization.md" }, + { + "text": "Form Validation", + "path": "UI/Angular/Form-Validation.md" + }, { "text": "Settings", "path": "UI/Angular/Settings.md"