You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id to the backend as `__tenant` header on each request.
## Domain Tenant Resolver
Angular UI can get the tenant name from the app running URL. You can determine the current tenant by subdomain (like mytenant1.mydomain.com) or by the whole domain (like mytenant.com). To do this, you need to set the `application.baseUrl` property in the environment:
@ -32,10 +31,10 @@ Subdomain resolver:
export const environment = {
//...
application: {
baseUrl: 'https://{0}.mydomain.com/'
baseUrl: "https://{0}.mydomain.com/",
},
//...
}
};
```
**{0}** is the placeholder to determine current tenant's unique name.
@ -44,7 +43,6 @@ After the configuration above, if your app runs on the `mytenant1.mydomain.com`,
> **Important Note:** If you define the `baseUrl` with the placeholder (**{0}**), the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page) will be hidden.
Domain resolver:
```js
@ -53,10 +51,10 @@ Domain resolver:
export const environment = {
//...
application: {
baseUrl: 'https://{0}.com/'
baseUrl: "https://{0}.com/",
},
//...
}
};
```
After the configuration above, if your app runs on the `mytenant.com`, the app will get the tenant name as **mytenant**.
@ -71,29 +69,28 @@ The **{0}** placeholder can be put to the API URLs in the environment to determi
export const environment = {
//...
application: {
baseUrl: 'https://{0}.mydomain.com/',
baseUrl: "https://{0}.mydomain.com/",
//...
},
oAuthConfig: {
issuer: 'https://{0}.ids.mydomain.com',
issuer: "https://{0}.ids.mydomain.com",
//...
},
apis: {
default: {
url: 'https://{0}.api.mydomain.com',
url: "https://{0}.api.mydomain.com",
},
AbpIdentity: {
url: 'https://{0}.identity.mydomain.com',
url: "https://{0}.identity.mydomain.com",
},
},
}
};
```
> **Important Note:** The `application.baseUrl` and the `{0}` placeholder in the value of the `baseUrl` property are required to be able to get tenant from running URL. Other placeholders in API URLs are optional.
After the configuration above, if your app runs on the `mytenant1.mydomain.com`, the app will get tenant name as **mytenant1** and replace the environment object in `ConfigState` on app initialization as follows:
```js
// environment object in ConfigState
@ -128,8 +125,8 @@ The app sends the `__tenant` header that contains the current tenant id on each
It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on the server side) and **model objects** (matches to [DTOs](../../Data-Transfer-Objects) in the server side).
Calling a REST endpoint from Angular applications is common. We usually create **services** matching server-side controllers and **interfaces** matching [DTOs](../../Data-Transfer-Objects) to interact with the server. This often results in manually transforming C# code into TypeScript equivalents and that is unfortunate, if not intolerable.
In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced:
To avoid manual effort, we might use a tool like [NSWAG](https://github.com/RicoSuter/NSwag) that generates service proxies. However, NSWAG has some disadvantages:
- It generates a **big, single** .ts file which has some problems;
- It get **too large** when your application grows.
- It doesn't fit into the **[modular](../../Module-Development-Basics) approach** of the ABP framework.
- It creates a bit **ugly code**. We want to have a clean code (just like if we write manually).
- It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies.
- It generates **a single .ts file** which gets **too large** as your application grows. Also, this single file does not fit the **[modular](../../Module-Development-Basics) approach** of ABP.
- To be honest, the generated code is a bit **ugly**. We would like to produce code that looks as if someone wrote it.
- Since swagger.json **does not reflect the exact method signature** of backend services, NSWAG cannot reflect them on the client-side as well.
ABP introduces an endpoint that exposes server-side method contracts. When the `generate-proxy` command is run, ABP CLI makes an HTTP request to this endpoint and generates better-aligned client proxies in TypeScript. It organizes folders according to namespaces, adds barrel exports, and reflects method signatures in Angular services.
ABP CLI changes that via the `generate-proxy` command. It automatically generates the client proxies in TypeScript. by creating folders which separated by module names in the `src/app` folder.
Run the following command in the **root folder** of the angular application:
```bash
@ -19,13 +18,13 @@ abp generate-proxy
The command without any parameters creates proxies only for your own application's services and places them in your default Angular application. There are several parameters you may use to modify this behavior. See the [CLI documentation](../../CLI) for details.
The generated files will be placed in a folder called `proxy` at the root of target project.
The generated files will be placed in a folder called `proxy` at the root of the target project.
Each folder will have models, enums, and services defined at related namespace accompanied by a barrel export, i.e. an `index.ts` file for easier imports.
> The cammand is able to find application/library roots by reading`angular.json` file. Make sure you have either defined your target project as the `defaultProject` or pass the `--target` parameter to the command. This also means that you may have a monorepo workspace.
> The command can find application/library roots by reading the`angular.json` file. Make sure you have either defined your target project as the `defaultProject` or pass the `--target` parameter to the command. This also means that you may have a monorepo workspace.
The `generate-proxy` command generates one service per back-end controller and a method (property with a function value actually) for each action in the controller. These methods call backend APIs via [RestService](./Http-Requests#restservice).
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's `RemoteServiceName`. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting them into constructor as shown below:
The `providedIn` property of the services is defined as `'root'`. Therefore there is no need to provide them in a module. You can use them directly by injecting them into the constructor as shown below:
```js
import { BookService } from '@proxy/books';
@ -114,7 +113,7 @@ export class BookComponent implements OnInit {
### Enums
Enums have always been difficult to populate in the frontend. The `generate-proxy` command genarates enums in a separate file and exports a ready-to-use options constant from the same file. So you can import them as follows:
Enums have always been difficult to populate in the frontend. The `generate-proxy` command generates enums in a separate file and exports a ready-to-use "options constant" from the same file. So you can import them as follows:
```js
import { bookGenreOptions } from "@proxy/books";
@ -132,7 +131,7 @@ export class BookComponent implements OnInit {
<selectformControlName="genre">
<option[ngValue]="null">Select a genre</option>
<option*ngFor="let genre of genres"[ngValue]="genre.value">
{%{{{ genre.key }}}%}
{%{{{ genre.key }}}%}
</option>
</select>
```
@ -141,4 +140,4 @@ export class BookComponent implements OnInit {
`SubscriptionService` is a utility service to provide an easy unsubscription from RxJS observables in Angular components and directives. Please see [why you should unsubscribe from observables on instance destruction](https://angular.io/guide/lifecycle-hooks#cleaning-up-on-instance-destruction).
@ -200,4 +200,4 @@ class DemoComponent implements OnInit {
`TrackByService` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose.