Browse Source

Merge pull request #19887 from abpframework/sinan/update-forRoot-docs

Update deprecated forRoot method with the new provider functions in documentations
pull/19959/head
Masum ULU 2 years ago
committed by GitHub
parent
commit
d6c5b36546
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 112
      docs/en/Multi-Tenancy.md
  2. 13
      docs/en/Themes/LeptonXLite/Angular.md
  3. 19
      docs/en/UI/Angular/Account-Module.md
  4. 2
      docs/en/UI/Angular/Basic-Theme.md
  5. 117
      docs/en/UI/Angular/Component-Replacement.md
  6. 37
      docs/en/UI/Angular/Confirmation-Service.md
  7. 8
      docs/en/UI/Angular/Feature-Libraries.md
  8. 52
      docs/en/UI/Angular/Form-Validation.md
  9. 22
      docs/en/UI/Angular/HTTP-Error-Handling.md
  10. 183
      docs/en/UI/Angular/Localization.md
  11. 9
      docs/en/UI/Angular/OAuth-Module.md
  12. 27
      docs/zh-Hans/UI/Angular/Localization.md

112
docs/en/Multi-Tenancy.md

@ -1,6 +1,6 @@
# Multi-Tenancy
Multi-Tenancy is a widely used architecture to create **SaaS applications** where the hardware and software **resources are shared by the customers** (tenants). ABP Framework provides all the base functionalities to create **multi tenant applications**.
Multi-Tenancy is a widely used architecture to create **SaaS applications** where the hardware and software **resources are shared by the customers** (tenants). ABP Framework provides all the base functionalities to create **multi tenant applications**.
Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) the multi-tenancy as like that:
@ -10,8 +10,8 @@ Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) the multi-tenanc
There are two main side of a typical SaaS / Multi-tenant application:
* A **Tenant** is a customer of the SaaS application that pays money to use the service.
* **Host** is the company that owns the SaaS application and manages the system.
- A **Tenant** is a customer of the SaaS application that pays money to use the service.
- **Host** is the company that owns the SaaS application and manages the system.
The Host and the Tenant terms will be used for that purpose in the rest of the document.
@ -36,9 +36,9 @@ Configure<AbpMultiTenancyOptions>(options =>
ABP Framework supports all the following approaches to store the tenant data in the database;
* **Single Database**: All tenants are stored in a single database.
* **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant.
* **Hybrid**: Some tenants share a single databases while some tenants may have their own databases.
- **Single Database**: All tenants are stored in a single database.
- **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant.
- **Hybrid**: Some tenants share a single database while some tenants may have their own databases.
[Tenant management module](Modules/Tenant-Management.md) (which comes pre-installed with the startup projects) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches.
@ -48,11 +48,11 @@ Multi-tenancy system is designed to **work seamlessly** and make your applicatio
### IMultiTenant
You should implement the `IMultiTenant` interface for your [entities](Entities.md) to make them **multi-tenancy ready**.
You should implement the `IMultiTenant` interface for your [entities](Entities.md) to make them **multi-tenancy ready**.
**Example: A multi-tenant *Product* entity**
**Example: A multi-tenant _Product_ entity**
````csharp
```csharp
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
@ -68,7 +68,7 @@ namespace MultiTenancyDemo.Products
public float Price { get; set; }
}
}
````
```
`IMultiTenant` interface just defines a `TenantId` property. When you implement this interface, ABP Framework **automatically** [filters](Data-Filtering.md) entities for the current tenant when you query from database. So, you don't need to manually add `TenantId` condition while performing queries. A tenant can not access to data of another tenant by default.
@ -96,9 +96,9 @@ If you set the `TenantId` value for a specific entity object, it will override t
`ICurrentTenant` defines the following properties;
* `Id` (`Guid`): Id of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request.
* `Name` (`string`): Name of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request.
* `IsAvailable` (`bool`): Returns `true` if the `Id` is not `null`.
- `Id` (`Guid`): Id of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request.
- `Name` (`string`): Name of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request.
- `IsAvailable` (`bool`): Returns `true` if the `Id` is not `null`.
#### Change the Current Tenant
@ -108,7 +108,7 @@ ABP Framework automatically filters the resources (database, cache...) based on
**Example: Get product count of a specific tenant**
````csharp
```csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
@ -134,11 +134,11 @@ namespace MultiTenancyDemo.Products
}
}
}
````
```
* `Change` method can be used in a **nested way**. It restores the `CurrentTenant.Id` to the previous value after the `using` statement.
* When you use `CurrentTenant.Id` inside the `Change` scope, you get the `tenantId` provided to the `Change` method. So, the repository also get this `tenantId` and can filter the database query accordingly.
* Use `CurrentTenant.Change(null)` to change scope to the host context.
- `Change` method can be used in a **nested way**. It restores the `CurrentTenant.Id` to the previous value after the `using` statement.
- When you use `CurrentTenant.Id` inside the `Change` scope, you get the `tenantId` provided to the `Change` method. So, the repository also get this `tenantId` and can filter the database query accordingly.
- Use `CurrentTenant.Change(null)` to change scope to the host context.
> Always use the `Change` method with a `using` statement like done in this example.
@ -148,7 +148,7 @@ As mentioned before, ABP Framework handles data isolation between tenants using
**Example: Get count of products in the database, including all the products of all the tenants.**
````csharp
```csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
@ -181,7 +181,7 @@ namespace MultiTenancyDemo.Products
}
}
````
```
See the [Data Filtering document](Data-Filtering.md) for more.
@ -201,15 +201,15 @@ ABP Framework provides an extensible **Tenant Resolving** system for that purpos
The following resolvers are provided and configured by default;
* `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**.
* `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default.
* `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route.
* `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default.
* `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default.
- `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**.
- `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default.
- `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route.
- `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default.
- `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default.
###### Problems with the NGINX
You may have problems with the `__tenant` in the HTTP Headers if you're using the [nginx](https://www.nginx.com/) as the reverse proxy server. Because it doesn't allow to use underscore and some other special characters in the HTTP headers and you may need to manually configure it. See the following documents please:
You may have problems with the `__tenant` in the HTTP Headers if you're using the [nginx](https://www.nginx.com/) as the reverse proxy server. Because it doesn't allow to use underscore and some other special characters in the HTTP headers and you may need to manually configure it. See the following documents please:
http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers
http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
@ -219,22 +219,24 @@ http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
**Example:**
````csharp
```csharp
services.Configure<AbpAspNetCoreMultiTenancyOptions>(options =>
{
options.TenantKey = "MyTenantKey";
});
````
```
If you change the `TenantKey`, make sure to pass it to `CoreModule` in the Angular client as follows:
If you change the `TenantKey`, make sure to pass it to `provideAbpCore` via `withOptions` method in the Angular client as follows:
```js
@NgModule({
imports: [
CoreModule.forRoot({
// ...
tenantKey: 'MyTenantKey'
}),
providers: [
provideAbpCore(
withOptions({
// ...
tenantKey: "MyTenantKey",
})
),
],
// ...
})
@ -249,7 +251,7 @@ import { TENANT_KEY } from '@abp/ng.core';
class SomeComponent {
constructor(@Inject(TENANT_KEY) private tenantKey: string) {}
}
}
```
> However, we don't suggest to change this value since some clients may assume the the `__tenant` as the parameter name and they might need to manually configure then.
@ -277,30 +279,30 @@ In a real application, most of times you will want to determine the current tena
**Example: Add a subdomain resolver**
````csharp
```csharp
Configure<AbpTenantResolveOptions>(options =>
{
options.AddDomainTenantResolver("{0}.mydomain.com");
});
````
```
* `{0}` is the placeholder to determine the current tenant's unique name.
* Add this code to the `ConfigureServices` method of your [module](Module-Development-Basics.md).
* This should be done in the *Web/API Layer* since the URL is a web related stuff.
- `{0}` is the placeholder to determine the current tenant's unique name.
- Add this code to the `ConfigureServices` method of your [module](Module-Development-Basics.md).
- This should be done in the _Web/API Layer_ since the URL is a web related stuff.
Openiddict is the default Auth Server in ABP (since v6.0). When you use OpenIddict, you must add this code to the `PreConfigure` method as well.
```csharp
// using Volo.Abp.OpenIddict.WildcardDomains
PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
{
options.EnableWildcardDomainSupport = true;
options.WildcardDomainsFormat.Add("https://{0}.mydomain.com");
});
```
You must add this code to the `Configure` method as well.
You must add this code to the `Configure` method as well.
```csharp
// using Volo.Abp.MultiTenancy;
@ -312,7 +314,7 @@ Configure<AbpTenantResolveOptions>(options =>
```
> There is an [example](https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver) that uses the subdomain to determine the current tenant.
> There is an [example](https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver) that uses the subdomain to determine the current tenant.
If you use a sepereted Auth server, you must install `[Owl.TokenWildcardIssuerValidator](https://www.nuget.org/packages/Owl.TokenWildcardIssuerValidator)` on the `HTTPApi.Host` project
@ -332,7 +334,7 @@ context.Services
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = "ExampleProjectName";
// start of added block
options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
options.TokenValidationParameters.ValidIssuers = new[]
@ -348,16 +350,16 @@ context.Services
You can add implement your custom tenant resolver and configure the `AbpTenantResolveOptions` in your module's `ConfigureServices` method as like below:
````csharp
```csharp
Configure<AbpTenantResolveOptions>(options =>
{
options.TenantResolvers.Add(new MyCustomTenantResolveContributor());
});
````
```
`MyCustomTenantResolveContributor` must inherit from the `TenantResolveContributorBase` (or implement the `ITenantResolveContributor`) as shown below:
````csharp
```csharp
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
@ -373,10 +375,10 @@ namespace MultiTenancyDemo.Web
}
}
}
````
```
* A tenant resolver should set `context.TenantIdOrName` if it can determine it. If not, just leave it as is to allow the next resolver to determine it.
* `context.ServiceProvider` can be used if you need to additional services to resolve from the [dependency injection](Dependency-Injection.md) system.
- A tenant resolver should set `context.TenantIdOrName` if it can determine it. If not, just leave it as is to allow the next resolver to determine it.
- `context.ServiceProvider` can be used if you need to additional services to resolve from the [dependency injection](Dependency-Injection.md) system.
#### Multi-Tenancy Middleware
@ -384,9 +386,9 @@ Multi-Tenancy middleware is an ASP.NET Core request pipeline [middleware](https:
Multi-Tenancy middleware is typically placed just under the [authentication](https://docs.microsoft.com/en-us/aspnet/core/security/authentication) middleware (`app.UseAuthentication()`):
````csharp
```csharp
app.UseMultiTenancy();
````
```
> This middleware is already configured in the startup templates, so you normally don't need to manually add it.
@ -406,7 +408,7 @@ The [tenant management module](Modules/Tenant-Management) is **included in the s
**Example: Define tenants in appsettings.json**
````json
```json
"Tenants": [
{
"Id": "446a5211-3d72-4339-9adc-845151f8ada0",
@ -422,7 +424,7 @@ The [tenant management module](Modules/Tenant-Management) is **included in the s
}
}
]
````
```
> It is recommended to **use the Tenant Management module**, which is already pre-configured when you create a new application with the ABP startup templates.
@ -440,4 +442,4 @@ The [Tenant Management module](Modules/Tenant-Management.md) provides a basic UI
## See Also
* [Features](Features.md)
- [Features](Features.md)

13
docs/en/Themes/LeptonXLite/Angular.md

@ -31,20 +31,21 @@ yarn add bootstrap-icons
Note: You should remove the old theme styles from "angular.json" if you are switching from "ThemeBasic" or "Lepton."
Look at the [Theme Configurations](../../UI/Angular/Theme-Configurations) list of styles. Depending on your theme, you can alter your styles in angular.json.
- Finally, remove `ThemeBasicModule` from `app.module.ts`, and import the related modules in `app.module.ts`
- Finally, remove `ThemeBasicModule`, `provideThemeBasicConfig` from `app.module.ts`, and import the related modules in `app.module.ts`
```js
import { ThemeLeptonXModule } from "@abp/ng.theme.lepton-x";
import { SideMenuLayoutModule } from "@abp/ng.theme.lepton-x/layouts";
@NgModule({
imports: [
// ...
// do not forget to remove ThemeBasicModule or other old theme module
// ThemeBasicModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
// ThemeBasicModule
ThemeLeptonXModule.forRoot()
],
providers: [
// do not forget to remove provideThemeBasicConfig or other old theme providers
// provideThemeBasicConfig
],
// ...
})

19
docs/en/UI/Angular/Account-Module.md

@ -7,7 +7,6 @@ If you add the account module to your project;
- "My account" link in the current user dropdown on the top bar will redirect the user to a page in the account module.
- You can switch the authentication flow to the resource owner password flow.
### Account Module Implementation
Install the `@abp/ng.account` NPM package by running the below command:
@ -18,18 +17,18 @@ npm install @abp/ng.account
> Make sure v4.3 or higher version is installed.
Open the `app.module.ts` and add `AccountConfigModule.forRoot()` to the imports array as shown below:
Open the `app.module.ts` and add `provideAccountConfig()` to the providers array as shown below:
```js
// app.module.ts
import { AccountConfigModule } from '@abp/ng.account/config';
import { provideAccountConfig } from "@abp/ng.account/config";
//...
@NgModule({
imports: [
providers: [
//...
AccountConfigModule.forRoot()
provideAccountConfig(),
],
//...
})
@ -57,19 +56,19 @@ The pro startup template comes with `@volo/abp.ng.account` package. You should u
```bash
npm install @volo/abp.ng.account
```
> Make sure v4.3 or higher version is installed.
Open the `app.module.ts` and add `AccountPublicConfigModule.forRoot()` to the imports array as shown below:
> Ensure that the `Account Layout Module` has been added if you are using the Lepton X theme. If you miss the step, you will get an error message that says `Account layout not found. Please check your configuration. If you are using LeptonX, please make sure you have added "AccountLayoutModule.forRoot()" to your app.module configuration.` when you try to access the account pages. Otherwise, you can skip adding the `AccountLayoutModule` step.
```js
// app.module.ts
import { AccountPublicConfigModule } from '@volo/abp.ng.account/public/config';
import { AccountPublicConfigModule } from "@volo/abp.ng.account/public/config";
// if you are using or want to use Lepton X, you should add AccountLayoutModule
// import { AccountLayoutModule } from '@volosoft/abp.ng.theme.lepton-x/account'
// import { AccountLayoutModule } from '@volosoft/abp.ng.theme.lepton-x/account'
//...
@ -104,9 +103,9 @@ Before v4.3, the "My account" link in the current user dropdown on the top bar r
### Personal Info Page Confirm Message
When the user changes their own data on the personal settings tab in My Account, The data can not update the CurrentUser key of Application-Configuration. The information of the user is stored in claims. The only way to apply this information to the CurrentUser of Application-Configuration is user should log out and log in. When the Refresh-Token feature is implemented, it will be fixed. So We've added a confirmation alert.
When the user changes their own data on the personal settings tab in My Account, The data can not update the CurrentUser key of Application-Configuration. The information of the user is stored in claims. The only way to apply this information to the CurrentUser of Application-Configuration is user should log out and log in. When the Refresh-Token feature is implemented, it will be fixed. So We've added a confirmation alert.
If you want to disable these warning, You should set `isPersonalSettingsChangedConfirmationActive` false
If you want to disable these warning, You should set `isPersonalSettingsChangedConfirmationActive` false
```js
// app-routing.module.ts

2
docs/en/UI/Angular/Basic-Theme.md

@ -11,7 +11,7 @@ The Basic Theme is a theme implementation for the Angular UI. It is a minimalist
If you need to manually this theme, follow the steps below:
* Install the [@abp/ng.theme.basic](https://www.npmjs.com/package/@abp/ng.theme.basic) NPM package to your Angular project.
* Open the `src/app/app.module.ts` file, import `ThemeBasicModule` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule.forRoot()` to the `imports` array.
* Open the `src/app/app.module.ts` file, import `ThemeBasicModule`,`provideThemeBasicConfig` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule` to the `imports` array and provide `provideThemeBasicConfig()` to the providers array.
* Open the `src/app/shared/shared.module` file, import `ThemeBasicModule` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule` to the `imports` and `exports` array.
The `ThemeBasicModule` is registered own layouts (`ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`) to a service which is exposed by `@abp/ng.core` package on application initialization.

117
docs/en/UI/Angular/Component-Replacement.md

@ -30,7 +30,6 @@ export class AppComponent {
![Example Usage](./images/component-replacement.gif)
## How to Replace a Layout
Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced the same way.
@ -88,11 +87,13 @@ This component should have a 'router-outlet' for dynamic content loading. You ca
```bash
ng generate component new-layout
```
This command will create a new component named `new-layout`. Now, open the new-layout.component.html file and add a `router-outlet` to it:
```html
<router-outlet></router-outlet>
<router-outlet></router-outlet>
```
This 'router-outlet' will act as a placeholder that Angular dynamically fills based on the current router state.
note: (don't forget: you should add the app in the app.module.ts file)
@ -103,10 +104,11 @@ Although this step is optional, it can be useful if you're going to use the layo
```javascript
export const eCustomLayout = {
key: 'CustomLayout',
component: 'CustomLayoutComponent',
key: "CustomLayout",
component: "CustomLayoutComponent",
};
```
In this variable, `key` is a unique identifier for the layout component, and `component` is the name of the layout component.
You can use this variable when you need to refer to the layout component.
@ -120,19 +122,24 @@ Here's how you can do it:
```javascript
export const CUSTOM_LAYOUT_PROVIDERS = [
{ provide: APP_INITIALIZER, useFactory: configureLayoutFn, deps: [ReplaceableComponentsService], multi: true },
{
provide: APP_INITIALIZER,
useFactory: configureLayoutFn,
deps: [ReplaceableComponentsService],
multi: true,
},
];
function configureLayoutFn() {
const service= inject( ReplaceableComponentsService)
return () =>{
service.add({
key: eCustomLayout.component,
component: CustomLayoutComponent,
})
}
const service = inject(ReplaceableComponentsService);
return () => {
service.add({
key: eCustomLayout.component,
component: CustomLayoutComponent,
});
};
}
```
In this code, `configureLayoutFn` is a factory function that adds the new layout component to the `ReplaceableComponentsService`. The `APP_INITIALIZER` provider runs this function when the application starts.
note: (don't forget: you should add the CUSTOM_LAYOUT_PROVIDERS in the app.module.ts file)
@ -149,34 +156,31 @@ export const myDynamicLayouts = new Map<string, string>([...DEFAULT_DYNAMIC_LAYO
#### Step 5: Pass the Dynamic Layouts to the CoreModule
The final step is to pass the dynamic layouts to the `CoreModule` using the `forRoot` method. This method allows you to configure the module with a static method.
The final step is to pass the dynamic layouts to the `provideAbpCore` using the `withOptions` method. This method allows you to configure the module with a static method.
Here's how you can do it:
```javascript
```ts
@NgModule({
declarations: [AppComponent],
imports: [
// other imports...
CoreModule.forRoot({
dynamicLayouts: myDynamicLayouts,
environment,
registerLocaleFn: registerLocale(),
}),
// other imports...
NewLayoutComponent
providers: [
// ...
provideAbpCore(
withOptions({
dynamicLayouts: myDynamicLayouts,
environment,
registerLocaleFn: registerLocale(),
}),
),
],
providers: [APP_ROUTE_PROVIDER, CUSTOM_LAYOUT_PROVIDERS],
bootstrap: [AppComponent],
})
export class AppModule {}
```
In this code, `myDynamicLayouts` is the map of dynamic layouts you defined earlier. We pass this map to the `CoreModule` using the `forRoot` method.
In this code, `myDynamicLayouts` is the map of dynamic layouts you defined earlier. We pass this map to the `provideAbpCore` using the `withOptions` method.
Now that you have defined the new layout, you can use it in the router definition. You do this by adding a new route that uses the new layout.
Here's how you can do it:
Here's how you can do it:
```javascript
// route.provider.ts
@ -221,14 +225,13 @@ Run the following command in `angular` folder to create a new component called `
yarn ng generate component logo --inlineTemplate --inlineStyle
```
Open the generated `logo.component.ts` in `src/app/logo` folder and replace its content with the following:
```js
import { Component } from '@angular/core';
import { Component } from "@angular/core";
@Component({
selector: 'app-logo',
selector: "app-logo",
template: `
<a class="navbar-brand" routerLink="/">
<!-- Change the img src -->
@ -284,14 +287,14 @@ yarn ng generate component routes
Open the generated `routes.component.ts` in `src/app/routes` folder and replace its content with the following:
```js
import { Component, HostBinding } from '@angular/core';
import { Component, HostBinding } from "@angular/core";
@Component({
selector: 'app-routes',
templateUrl: 'routes.component.html',
selector: "app-routes",
templateUrl: "routes.component.html",
})
export class RoutesComponent {
@HostBinding('class.mx-auto')
@HostBinding("class.mx-auto")
marginAuto = true;
get smallScreen() {
@ -321,11 +324,14 @@ Open the generated `routes.component.html` in `src/app/routes` folder and replac
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/"
><i class="fas fa-home"></i> {%{{{ '::Menu:Home' | abpLocalization }}}%}</a
><i class="fas fa-home"></i> {%{{{ '::Menu:Home' | abpLocalization
}}}%}</a
>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/my-page"><i class="fas fa-newspaper mr-1"></i>My Page</a>
<a class="nav-link" routerLink="/my-page"
><i class="fas fa-newspaper mr-1"></i>My Page</a
>
</li>
<li
#navbarRootDropdown
@ -338,7 +344,11 @@ Open the generated `routes.component.html` in `src/app/routes` folder and replac
: (navbarRootDropdown.expand = true)
"
>
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
href="javascript:void(0)"
>
<i class="fas fa-wrench"></i>
{%{{{ 'AbpUiNavigation::Menu:Administration' | abpLocalization }}}%}
</a>
@ -401,7 +411,8 @@ Open the generated `routes.component.html` in `src/app/routes` folder and replac
class="btn d-block text-start dropdown-toggle"
>
<i class="fa fa-users"></i>
{%{{{ 'AbpTenantManagement::Menu:TenantManagement' | abpLocalization }}}%}
{%{{{ 'AbpTenantManagement::Menu:TenantManagement' | abpLocalization
}}}%}
</a>
</div>
<div
@ -409,7 +420,10 @@ Open the generated `routes.component.html` in `src/app/routes` folder and replac
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div class="dropdown-submenu" *abpPermission="'AbpTenantManagement.Tenants'">
<div
class="dropdown-submenu"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<a class="dropdown-item" routerLink="/tenant-management/tenants">
{%{{{ 'AbpTenantManagement::Tenants' | abpLocalization }}}%}</a
>
@ -554,7 +568,11 @@ Open the generated `nav-items.component.html` in `src/app/nav-items` folder and
```html
<ul class="navbar-nav">
<input type="search" placeholder="Search" class="bg-transparent border-0 text-white" />
<input
type="search"
placeholder="Search"
class="bg-transparent border-0 text-white"
/>
<li class="nav-item d-flex align-items-center">
<div
*ngIf="(dropdownLanguages$ | async)?.length > 0"
@ -592,9 +610,9 @@ Open the generated `nav-items.component.html` in `src/app/nav-items` folder and
</li>
<li class="nav-item d-flex align-items-center">
<ng-template #loginBtn>
<a role="button" class="nav-link pointer" (click)="navigateToLogin()">{%{{{
'AbpAccount::Login' | abpLocalization
}}}%}</a>
<a role="button" class="nav-link pointer" (click)="navigateToLogin()"
>{%{{{ 'AbpAccount::Login' | abpLocalization }}}%}</a
>
</ng-template>
<div
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn"
@ -614,8 +632,7 @@ Open the generated `nav-items.component.html` in `src/app/nav-items` folder and
aria-expanded="false"
>
<small *ngIf="(selectedTenant$ | async)?.name as tenantName"
><i>{%{{{ tenantName }}}%}</i
>\</small
><i>{%{{{ tenantName }}}%}</i>\</small
>
<strong>{%{{{ (currentUser$ | async)?.userName }}}%}</strong>
</a>
@ -625,10 +642,12 @@ Open the generated `nav-items.component.html` in `src/app/nav-items` folder and
[class.d-block]="smallScreen && currentUserDropdown.isOpen()"
>
<a class="dropdown-item pointer" (click)="navigateToManageProfile()"
><i class="fa fa-cog mr-1"></i>{%{{{ 'AbpAccount::MyAccount' | abpLocalization }}}%}</a
><i class="fa fa-cog mr-1"></i>{%{{{ 'AbpAccount::MyAccount' |
abpLocalization }}}%}</a
>
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()"
><i class="fa fa-power-off mr-1"></i>{%{{{ 'AbpUi::Logout' | abpLocalization }}}%}</a
><i class="fa fa-power-off mr-1"></i>{%{{{ 'AbpUi::Logout' |
abpLocalization }}}%}</a
>
</div>
</div>

37
docs/en/UI/Angular/Confirmation-Service.md

@ -71,10 +71,10 @@ const options: Partial<Confirmation.Options> = {
yesText: "Confirm",
messageLocalizationParams: ["Demo"],
titleLocalizationParams: [],
// You can customize icon
// You can customize icon
// icon: 'fa fa-exclamation-triangle', // or
// iconTemplate : '<img src="custom-image-path.jpg" alt=""/>'
}
};
this.confirmation.warn(
"AbpIdentity::RoleDeletionConfirmationMessage",
@ -129,23 +129,26 @@ this.confirmation.clear();
### How to Change Icons of The Confirmation Popup
You can change icons with the token of "confirmationIcons" in ThemeSharedModule in the app.module.ts. The changes will affect all confirmation popup in the project.
```js
...
ThemeSharedModule.forRoot({
confirmationIcons: {
info: 'fa fa-info-circle',
success: 'fa fa-check-circle',
warning: 'fa fa-exclamation-triangle',
error: 'fa fa-times-circle',
default: 'fa fa-question-circle',
},
}),
...
You can change icons with the `withConfirmationIcon()` method of `provideAbpThemeShared` function in the app.module.ts. The changes will affect all confirmation popup in the project.
```ts
import { provideAbpThemeShared, withConfirmationIcon } from '@abp/ng.theme.shared';
@NgModule({
providers: [
// ...
provideAbpThemeShared(withConfirmationIcon({
info: 'fa fa-info-circle',
success: 'fa fa-check-circle',
warning: 'fa fa-exclamation-triangle',
error: 'fa fa-times-circle',
default: 'fa fa-question-circle',
})),
],
})
export class AppModule {}
```
## API
### success

8
docs/en/UI/Angular/Feature-Libraries.md

@ -41,13 +41,13 @@ yarn add @abp/ng.identity
As of ABP v3.0, every lazy-loaded module has a config module available via a secondary entry point on the same package. Importing them in your root module looks like this:
```js
import { IdentityConfigModule } from "@abp/ng.identity/config";
```ts
import { provideIdentityConfig } from "@abp/ng.identity/config";
@NgModule({
imports: [
providers: [
// other imports
IdentityConfigModule.forRoot(),
provideIdentityConfig(),
],
// providers, declarations, and bootstrap
})

52
docs/en/UI/Angular/Form-Validation.md

@ -6,28 +6,20 @@ Reactive forms in ABP Angular UI are validated by [ngx-validate](https://www.npm
## How to Add New Error Messages
You can add a new error message by passing validation options to the `ThemeSharedModule` in your root module.
You can add a new error message by passing validation options to the `withValidationBluePrint` method of `provideAbpThemeShared` function in your root module.
```js
import { VALIDATION_BLUEPRINTS } from "@ngx-validate/core";
import { DEFAULT_VALIDATION_BLUEPRINTS } from "@abp/ng.theme.shared";
```ts
import { provideAbpThemeShared, withValidationBluePrint } from '@abp/ng.theme.shared';
@NgModule({
imports: [
ThemeSharedModule.forRoot({
validation: {
blueprints: {
uniqueUsername: "::AlreadyExists[{%{{{ username }}}%}]",
},
},
// rest of theme shared config
}),
// other imports
providers: [
provideAbpThemeShared(
withValidationBluePrint({
uniqueUsername: "::AlreadyExists[{%{{{ username }}}%}]"
})
),
...
],
// rest of the module metadata
})
export class AppModule {}
```
@ -77,26 +69,16 @@ You can overwrite an existing error message by passing validation options to the
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";
import { DEFAULT_VALIDATION_BLUEPRINTS } from "@abp/ng.theme.shared";
```ts
import { provideAbpThemeShared, withValidationBluePrint } from '@abp/ng.theme.shared';
@NgModule({
imports: [
ThemeSharedModule.forRoot({
validation: {
blueprints: {
required: "::RequiredInput",
},
},
// rest of theme shared config
}),
// other imports
providers: [
provideAbpThemeShared(withValidationBluePrint({
required: "::RequiredInput",
})),
...
],
// rest of the module metadata
})
export class AppModule {}
```

22
docs/en/UI/Angular/HTTP-Error-Handling.md

@ -5,22 +5,20 @@
ABP offers a configurations for errors handling like below
```ts
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { provideAbpThemeShared, withHttpErrorConfig } from '@abp/ng.theme.shared';
import { MyCustomRouteErrorComponent } from './my-custom-route.component';
@NgModule({
imports: [
ThemeSharedModule.forRoot({
httpErrorConfig: {
skipHandledErrorCodes: [403],
errorScreen: {
forWhichErrors: [404],
component: CustomErrorComponent,
hideCloseIcon: false
}
providers: [
// ...
provideAbpThemeShared(withHttpErrorConfig({
skipHandledErrorCodes: [403],
errorScreen: {
forWhichErrors: [404],
component: MyCustomRouteErrorComponent,
hideCloseIcon: false
}
}),
...
})),
],
})
export class AppModule {}

183
docs/en/UI/Angular/Localization.md

@ -11,7 +11,7 @@ The Localization key format consists of 2 sections which are **Resource Name** a
const environment = {
//...
localization: {
defaultResourceName: 'MyProjectName',
defaultResourceName: "MyProjectName",
},
};
```
@ -38,7 +38,8 @@ You can also specify a default value as shown below:
```html
<h1>
{%{{{ { key: 'Resource::Key', defaultValue: 'Default Value' } | abpLocalization }}}%}
{%{{{ { key: 'Resource::Key', defaultValue: 'Default Value' } |
abpLocalization }}}%}
</h1>
```
@ -82,17 +83,17 @@ After that, you are able to use localization service.
```js
this.localizationService.instant(
'AbpIdentity::UserDeletionConfirmation',
'John'
"AbpIdentity::UserDeletionConfirmation",
"John"
);
// with fallback value
this.localizationService.instant(
{
key: 'AbpIdentity::UserDeletionConfirmation',
defaultValue: 'Default Value',
key: "AbpIdentity::UserDeletionConfirmation",
defaultValue: "Default Value",
},
'John'
"John"
);
// Output
@ -102,12 +103,12 @@ this.localizationService.instant(
To get a localized text as [_Observable_](https://rxjs.dev/guide/observable) use `get` method instead of `instant`:
```js
this.localizationService.get('Resource::Key');
this.localizationService.get("Resource::Key");
// with fallback value
this.localizationService.get({
key: 'Resource::Key',
defaultValue: 'Default Value',
key: "Resource::Key",
defaultValue: "Default Value",
});
```
@ -118,41 +119,46 @@ Localizations can be determined on backend side. Angular UI gets the localizatio
See an example:
```ts
// app.module.ts
import { provideAbpCore, withOptions } from '@abp/ng.core';
@NgModule({
imports: [
//...other imports
CoreModule.forRoot({
localizations: [
{
culture: 'en',
resources: [
{
resourceName: 'MyProjectName',
texts: {
Administration: 'Administration',
HomePage: 'Home',
providers: [
// ...
provideAbpCore(
withOptions({
...,
localizations: [
{
culture: 'en',
resources: [
{
resourceName: 'MyProjectName',
texts: {
Administration: 'Administration',
HomePage: 'Home',
},
},
},
],
},
{
culture: 'de',
resources: [
{
resourceName: 'MyProjectName',
texts: {
Administration: 'Verwaltung',
HomePage: 'Startseite',
],
},
{
culture: 'de',
resources: [
{
resourceName: 'MyProjectName',
texts: {
Administration: 'Verwaltung',
HomePage: 'Startseite',
},
},
},
],
},
],
}),
]
],
},
],
}),
),
...
],
})
export class AppModule {}
```
...or, you can determine the localizations in a feature module:
@ -256,10 +262,10 @@ Find [styles configuration in angular.json](https://angular.io/guide/workspace-c
If you have created and injected chunks for Fontawesome as seen above, you no longer need the lazy loading in the `AppComponent` which was implemented before v2.9. Simply remove them. The `AppComponent` in the template of the new version looks like this:
```js
import { Component } from '@angular/core';
import { Component } from "@angular/core";
@Component({
selector: 'app-root',
selector: "app-root",
template: `
<abp-loader-bar></abp-loader-bar>
<router-outlet></router-outlet>
@ -274,33 +280,32 @@ Since ABP has more than one language, Angular locale files loads lazily using [W
### registerLocaleFn
`registerLocale` function that exported from `@abp/ng.core/locale` package is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack `import` function. A `registerLocale` function must be passed to the `forRoot` of the `CoreModule` as shown below:
```js
// app.module.ts
`registerLocale` function that exported from `@abp/ng.core/locale` package is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack `import` function. A `registerLocale` function must be passed to the `withOptions` of the `provideAbpCore` as shown below:
```ts
import { provideAbpCore, withOptions } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
// if you have commercial license and the language management module, add the below import
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale(
// you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally
{
cultureNameLocaleFileMap: { 'pt-BR': 'pt' },
errorHandlerFn: ({ resolve, reject, locale, error }) => {
// the error can be handled here
providers: [
provideAbpCore(
withOptions({
...,
registerLocaleFn: registerLocale(
// you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally
{
cultureNameLocaleFileMap: { 'pt-BR': 'pt' },
errorHandlerFn: ({ resolve, reject, locale, error }) => {
// the error can be handled here
},
},
},
)
}),
//...
]
),
}),
),
...
],
})
export class AppModule {}
```
### Mapping of Culture Name to Angular Locale File Name
@ -320,20 +325,23 @@ import { registerLocale } from '@abp/ng.core/locale';
@NgModule({
imports: [
providers: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale(
{
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
provideAbpCore(
withOptions({
...,
registerLocaleFn: registerLocale(
{
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
},
},
},
)
}),
//...
)
}),
),
]
})
```
See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales).
@ -345,12 +353,12 @@ Add the below code to the `app.module.ts` by replacing `your-locale` placeholder
```js
//app.module.ts
import { storeLocaleData } from '@abp/ng.core/locale';
import { storeLocaleData } from "@abp/ng.core/locale";
import(
/* webpackChunkName: "_locale-your-locale-js"*/
/* webpackMode: "eager" */
'@angular/common/locales/your-locale.js'
).then((m) => storeLocaleData(m.default, 'your-locale'));
"@angular/common/locales/your-locale.js"
).then((m) => storeLocaleData(m.default, "your-locale"));
```
...or a custom `registerLocale` function can be passed to the `CoreModule`:
@ -373,14 +381,17 @@ export function registerLocale(locale: string) {
import { registerLocale } from './register-locale';
@NgModule({
imports: [
providers: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale
}),
provideAbpCore(
withOptions({
...,
registerLocaleFn: registerLocale,
}),
),
//...
]
})
```
After this custom `registerLocale` function, since the en and fr added to the `webpackInclude`, only en and fr locale files will be created as chunks:
@ -391,5 +402,5 @@ Which locale files you add to `webpackInclude` magic comment, they will be inclu
## See Also
* [Localization in ASP.NET Core](../../Localization.md)
* [Video tutorial](https://abp.io/video-courses/essentials/localization)
- [Localization in ASP.NET Core](../../Localization.md)
- [Video tutorial](https://abp.io/video-courses/essentials/localization)

9
docs/en/UI/Angular/OAuth-Module.md

@ -1,9 +1,12 @@
# ABP OAuth Package
The authentication functionality has been moved from @abp/ng.core to @abp/ng.ouath since v7.0.
If your app is version 7.0 or higher, you should include "AbpOAuthModule.forRoot()" in your app.module.ts as an import after "CoreModule.forRoot(...)".
If your app is version 8.3 or higher, you should include "provideAbpOAuth()" in your app.module.ts as an providers after "provideAbpCore()
".
Those abstractions can be found in the @abp/ng-core packages.
- `AuthService` (the class that implements the IAuthService interface).
- `NAVIGATE_TO_MANAGE_PROFILE` Inject token.
- `ApiInterceptor` (the class that implements the IApiInterceptor interface).
@ -11,9 +14,9 @@ Those abstractions can be found in the @abp/ng-core packages.
Those base classes are overridden by the "AbpOAuthModule" for oAuth. There are also three functions provided with AbpOAuthModule.
- `PIPE_TO_LOGIN_FN_KEY` a provide that calls a function when the user is not authenticated. The function should be PipeToLoginFn type.
- `SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY` a provide that calls a function when the user is authenticated. The function should be SetTokenResponseToStorageFn type.
- `SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY` a provide that calls a function when the user is authenticated. The function should be SetTokenResponseToStorageFn type.
- `CHECK_AUTHENTICATION_STATE_FN_KEY` a provide that calls a function when the user is authenticated and stores the auth state. The function should be CheckAuthenticationStateFn type.
The tokens and interfaces are in the `@abp/ng.core` package but the implementation of these interfaces is in the `@abp/ng.oauth` package.
The tokens and interfaces are in the `@abp/ng.core` package but the implementation of these interfaces is in the `@abp/ng.oauth` package.
If you want to make your own authentication system, you must also change these 'abstract' classes.

27
docs/zh-Hans/UI/Angular/Localization.md

@ -205,16 +205,23 @@ export class AppComponent {}
// app.module.ts
@NgModule({
imports: [
// other imports
CoreModule.forRoot({
// other options
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
}
})
//...
providers: [
// ...
provideAbpCore(
withOptions({
...,
registerLocaleFn: registerLocale(
{
cultureNameLocaleFileMap: {
"DotnetCultureName": "AngularLocaleFileName",
"pt-BR": "pt" // example
},
},
)
}),
),
]
})
```
查看 [Angular中所有的语言环境文件](https://github.com/angular/angular/tree/master/packages/common/locales).

Loading…
Cancel
Save