Browse Source

Initial blog post

pull/17580/head
Masum ULU 2 years ago
parent
commit
dbde879c4d
  1. 450
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/README.md
  2. 16
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.editorconfig
  3. 50
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.eslintrc.json
  4. 50
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.gitignore
  5. 5
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.prettierrc
  6. 27
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/README.md
  7. 186
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/angular.json
  8. 44
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/karma.conf.js
  9. 62
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/package.json
  10. 44
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app-routing.module.ts
  11. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app.component.ts
  12. 57
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app.module.ts
  13. 40
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/contributors/form-prop.contributors.ts
  14. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/contributors/index.ts
  15. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/index.ts
  16. 36
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books-routing.module.ts
  17. 34
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.component.html
  18. 19
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.component.ts
  19. 47
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.module.ts
  20. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/index.ts
  21. 30
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/rent-book/rent-book.component.html
  22. 91
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/rent-book/rent-book.component.ts
  23. 55
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/defaults/default-books-form-props.ts
  24. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/defaults/index.ts
  25. 4
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/enums/components.ts
  26. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/enums/index.ts
  27. 37
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/guards/extensions.guard.ts
  28. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/guards/index.ts
  29. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/index.ts
  30. 11
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/models/config-options.ts
  31. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/models/index.ts
  32. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/providers/index.ts
  33. 22
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/providers/route.provider.ts
  34. 12
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/author.service.ts
  35. 41
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/books.service.ts
  36. 3
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/index.ts
  37. 11
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/models.ts
  38. 20
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/tokens/extensions.token.ts
  39. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/tokens/index.ts
  40. 6
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/utils/common.ts
  41. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/utils/index.ts
  42. 11
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home-routing.module.ts
  43. 380
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.component.html
  44. 15
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.component.ts
  45. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.module.ts
  46. 20
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/route.provider.ts
  47. 23
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/shared/shared.module.ts
  48. 0
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/.gitkeep
  49. BIN
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/images/logo/logo-light-thumbnail.png
  50. BIN
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/images/logo/logo-light.png
  51. 26
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/environments/environment.prod.ts
  52. 26
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/environments/environment.ts
  53. BIN
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/favicon.ico
  54. 16
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/index.html
  55. 13
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/main.ts
  56. 54
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/polyfills.ts
  57. 30
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/styles.scss
  58. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/test.ts
  59. 2
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/start.ps1
  60. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.app.json
  61. 26
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.json
  62. 10
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.spec.json
  63. 1
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.gitattributes
  64. 265
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.gitignore
  65. 5
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.prettierrc
  66. 5
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/NuGet.Config
  67. 123
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/Volo.BookStore.sln
  68. 23
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/Volo.BookStore.sln.DotSettings
  69. 19
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/common.props
  70. 28
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/BookStoreApplicationContractsModule.cs
  71. 28
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/BookStoreDtoExtensions.cs
  72. 20
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Permissions/BookStorePermissionDefinitionProvider.cs
  73. 9
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Permissions/BookStorePermissions.cs
  74. 25
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Volo.BookStore.Application.Contracts.csproj
  75. 17
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreAppService.cs
  76. 13
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs
  77. 31
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreApplicationModule.cs
  78. 2
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/Properties/AssemblyInfo.cs
  79. 25
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/Volo.BookStore.Application.csproj
  80. 14
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/BookStoreDbMigratorModule.cs
  81. 51
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/DbMigratorHostedService.cs
  82. 41
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/Program.cs
  83. 45
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/Volo.BookStore.DbMigrator.csproj
  84. 30
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/appsettings.json
  85. 6
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreDomainErrorCodes.cs
  86. 58
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreDomainSharedModule.cs
  87. 22
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreGlobalFeatureConfigurator.cs
  88. 73
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreModuleExtensionConfigurator.cs
  89. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/ar.json
  90. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/cs.json
  91. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/de.json
  92. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/en-GB.json
  93. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/en.json
  94. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/es.json
  95. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/fi.json
  96. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/fr.json
  97. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hi.json
  98. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hr.json
  99. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hu.json
  100. 8
      docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/is.json

450
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/README.md

@ -0,0 +1,450 @@
# Cascading Option Loading with Extensions System in ABP Angular
In this article we'll see how to loading cascading options with extensions system in ABP Angular. For this example we'll simulate renting a book process. Beside our default form properties we'll contribute `Name` property to our `Rent Form Modal` in Books module. This property will be loaded after `Genre` selected.
> Before starting this article, I suggest you to read the [ABP Angular Dynamic Form Extensions](https://docs.abp.io/en/abp/latest/UI/Angular/Dynamic-Form-Extensions)
### Environment
- **ABP Framework Version:** ~7.3.0 (`~` means that use the latest patch version of the specified release)
- **DB Provider:** MongoDB
- **Angular Version:** ~16.0.0
### Project structure
Books module is not a library, for this demo it'll placed in application itself
![Folder structure](./assets/img/folder-structure.png)
- **books folder:** Contains default form properties, tokens, models, etc. It's smilar abp module structure.
- Also I've used **standalone** and **signals** feature in this demo.
- **books-extended folder:** Contains only `Name` property for the contribute `Rent Form Modal` inside Books module.
- **For more readibility, I've used TS path aliases in this demo. Don't forget to export files in `index.ts` file 🙂**
![tsconfig.json file](./assets/img/ts-config-file.png)
### First look at the demo
![Cascading Loading Demo](assets/gif/cascading-loading-demo.gif)
### What is extensions system?
![Extensions System Document](./assets/img/extensions-system-document.png)
# Reviewing the code step by step
**1.Create default form properties for `Rent Form` in the `Books` module**
- `getInjected` function is the key point of the cascading loading
- We can reach and track any value from `Service` or `Component`
- In that way we can load options according to the selected value
```ts
// ~/books/defaults/default-books-form.props.ts
import { Validators } from "@angular/forms";
import { map, of } from "rxjs";
import { ePropType, FormProp } from "@abp/ng.theme.shared/extensions";
import { BookDto, AuthorService, BooksService } from "../proxy";
import { RentBookComponent } from "../components";
import { DefaultOption } from "../utils";
const { required } = Validators;
export const DEFAULT_RENT_FORM_PROPS = FormProp.createMany<BookDto>([
{
type: ePropType.String,
id: "authorId",
name: "authorId",
displayName: "BookStore::Author",
defaultValue: null,
validators: () => [required],
options: (data) => {
const { authors } = data.getInjected(AuthorService);
return of([
DefaultOption,
...authors().map((author) => ({ value: author.id, key: author.name })),
]);
},
},
{
type: ePropType.String,
id: "genreId",
name: "genreId",
displayName: "BookStore::Genre",
defaultValue: null,
validators: () => [required],
options: (data) => {
const rentBookComponent = data.getInjected(RentBookComponent);
const { genres } = data.getInjected(BooksService);
const genreOptions = genres().map(({ id, name }) => ({
value: id,
key: name,
}));
return rentBookComponent.form.controls.authorId.valueChanges.pipe(
map((value: string | undefined) =>
value ? [DefaultOption, ...genreOptions] : [DefaultOption]
)
);
},
},
{
type: ePropType.Date,
id: "returnDate",
name: "returnDate",
displayName: "BookStore::ReturnDate",
defaultValue: null,
validators: () => [required],
},
]);
```
**2.Configure tokens and config options**
This steps explained in documentation, that's why I won't explain it again. If document or samples not enough please let me know 🙂
**Extensions Token**
```ts
// ~/books/tokens/extensions.token.ts
import { CreateFormPropContributorCallback } from "@abp/ng.theme.shared/extensions";
import { InjectionToken } from "@angular/core";
import { BookDto } from "../proxy";
import { eBooksComponents } from "../enums";
import { DEFAULT_RENT_FORM_PROPS } from "../defaults";
export const DEFAULT_BOOK_STORE_CREATE_FORM_PROPS = {
[eBooksComponents.RentBook]: DEFAULT_RENT_FORM_PROPS,
};
export const BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS =
new InjectionToken<CreateFormPropContributors>(
"BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS"
);
type CreateFormPropContributors = Partial<{
[eBooksComponents.RentBook]: CreateFormPropContributorCallback<BookDto>[];
/**
* Other creation form prop contributors...
*/
// [eBooksComponents.CreateBook]: CreateFormPropContributorCallback<BookDto>[];
}>;
```
**Extensions Config Option**
```ts
// ~/books/models/config-options.ts
import { CreateFormPropContributorCallback } from "@abp/ng.theme.shared/extensions";
import { BookDto } from "../proxy";
import { eBooksComponents } from "../enums";
export type BookStoreRentFormPropContributors = Partial<{
[eBooksComponents.RentBook]: CreateFormPropContributorCallback<BookDto>[];
}>;
export interface BooksConfigOptions {
rentFormPropContributors?: BookStoreRentFormPropContributors;
}
```
**3.Extensions Guard**
It'll to collect all contributors from [ExtensionsService](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/theme-shared/extensions/src/lib/services/extensions.service.ts)
```ts
// ~/books/guards/extensions.guard.ts
import { Injectable, inject } from "@angular/core";
import { Observable, map, tap } from "rxjs";
import { ConfigStateService, IAbpGuard } from "@abp/ng.core";
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
mapEntitiesToContributors,
mergeWithDefaultProps,
} from "@abp/ng.theme.shared/extensions";
import {
BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS,
DEFAULT_BOOK_STORE_CREATE_FORM_PROPS,
} from "../tokens";
@Injectable()
export class BooksExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
const createFormContributors =
inject(BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS, { optional: true }) || {};
return getObjectExtensionEntitiesFromStore(
this.configState,
"BookStore"
).pipe(
mapEntitiesToContributors(this.configState, "BookStore"),
tap((objectExtensionContributors) => {
mergeWithDefaultProps(
this.extensions.createFormProps,
DEFAULT_BOOK_STORE_CREATE_FORM_PROPS,
objectExtensionContributors.createForm,
createFormContributors
);
}),
map(() => true)
);
}
}
```
Yes I'm still using class based guard 🙂 much flexible...
**4.RentBookComponent**
- Our trackable variable defined here `(form:FormGroup)`, which means We'll track this variable in `options` property at defaults || contributors files.
- Providing `AuthorService`, also `EXTENSIONS_IDENTIFIER` for the reach dynamic properties
```ts
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Injector,
Output,
inject,
} from "@angular/core";
import { FormGroup } from "@angular/forms";
import { CoreModule, uuid } from "@abp/ng.core";
import { ThemeSharedModule } from "@abp/ng.theme.shared";
import {
EXTENSIONS_IDENTIFIER,
FormPropData,
UiExtensionsModule,
generateFormFromProps,
} from "@abp/ng.theme.shared/extensions";
import { AuthorService, BookDto, BooksService } from "../../proxy";
import { eBooksComponents } from "../../enums";
@Component({
standalone: true,
selector: "app-rent-book",
templateUrl: "./rent-book.component.html",
imports: [CoreModule, UiExtensionsModule, ThemeSharedModule],
providers: [
{
provide: EXTENSIONS_IDENTIFIER,
useValue: eBooksComponents.RentBook,
},
AuthorService,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RentBookComponent {
protected readonly injector = inject(Injector);
protected readonly authorService = inject(AuthorService);
protected readonly booksService = inject(BooksService);
//#region Just for demo
readonly #authors = this.authorService.authors();
readonly #genres = this.booksService.genres();
readonly #books = this.booksService.books();
//#endregion
protected modalVisible = true;
@Output() modalVisibleChange = new EventEmitter<boolean>();
selected: BookDto;
form: FormGroup;
modalBusy = false;
protected buildForm(): void {
const data = new FormPropData(this.injector, this.selected);
this.form = generateFormFromProps(data);
}
constructor() {
this.buildForm();
}
save(): void {
if (this.form.invalid) {
return;
}
this.modalBusy = true;
const { authorId, genreId, bookId, returnDate } = this.form.value;
//#region Just for demo
const authorName = this.#authors.find(({ id }) => id === authorId).name;
const genreName = this.#genres.find(({ id }) => id === genreId).name;
const bookName = this.#books.find(({ id }) => id === bookId).name;
//#endregion
this.booksService.rentedBooks.update((books) => [
{
id: uuid(),
name: bookName,
author: authorName,
genre: genreName,
returnDate,
},
...books,
]);
this.modalBusy = false;
this.modalVisible = false;
}
}
```
```html
<abp-modal
[visible]="modalVisible"
[busy]="modalBusy"
(visibleChange)="modalVisibleChange.next($event)"
>
<ng-template #abpHeader>
<h3>{{ 'BookStore::RentABook' | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<ng-template #loaderRef>
<div class="text-center">
<i class="fa fa-pulse fa-spinner" aria-hidden="true"></i>
</div>
</ng-template>
<form
*ngIf="form; else loaderRef"
[formGroup]="form"
(ngSubmit)="save()"
validateOnSubmit
>
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
</form>
</ng-template>
<ng-template #abpFooter>
<button abpClose type="button" class="btn btn-secondary">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button
iconClass="fa fa-check"
[disabled]="form?.invalid"
(click)="save()"
>
{{ 'AbpIdentity::Save' | abpLocalization }}
</abp-button>
</ng-template>
</abp-modal>
```
Up to the present we constructed our module's default form properties.
- As you can see there is no book names we'll add it via contributors
![Rent Form Without Contribution](./assets/img/rent-form-without-contribution.png)
## Next, add new property dynamically (book name list as dropdown)
- Created new folder ./src/app/books-extended
- Create contributors/form-prop.contributors.ts
```ts
// ~/books-extened/contributors/form-prop.contributors.ts
import { Validators } from "@angular/forms";
import { map } from "rxjs";
import {
ePropType,
FormProp,
FormPropList,
} from "@abp/ng.theme.shared/extensions";
import {
BookDto,
BookStoreRentFormPropContributors,
BooksService,
DefaultOption,
RentBookComponent,
eBooksComponents,
} from "@book-store/books";
const { required, maxLength } = Validators;
const bookIdProp = new FormProp<BookDto>({
type: ePropType.String,
id: "bookId",
name: "bookId",
displayName: "BookStore::Name",
options: (data) => {
const rentBook = data.getInjected(RentBookComponent);
const { books } = data.getInjected(BooksService);
const bookOptions = books().map(({ id, name }) => ({
value: id,
key: name,
}));
return rentBook.form.controls.genreId.valueChanges.pipe(
map((value: string | undefined) =>
value ? [DefaultOption, ...bookOptions] : [DefaultOption]
)
);
},
validators: () => [required, maxLength(255)],
});
export function bookIdPropContributor(propList: FormPropList<BookDto>) {
propList.addByIndex(bookIdProp, 2);
}
export const bookStoreRentFormPropContributors: BookStoreRentFormPropContributors =
{
[eBooksComponents.RentBook]: [bookIdPropContributor],
};
```
- Load new contribution via routing & forLazy method
```ts
// ~/app-routing.module.ts
import { bookStoreRentFormPropContributors } from "./books-extended/contributors/form-prop.contributors";
const routes: Routes = [
// other routes...
{
path: "books",
loadChildren: () =>
import("@book-store/books").then((m) =>
m.BooksModule.forLazy({
rentFormPropContributors: bookStoreRentFormPropContributors,
})
),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes, {})],
exports: [RouterModule],
})
export class AppRoutingModule {}
```
Finally.. We've added new property to our module, and it'll be loaded after `Genre` selected.
## Conclusion
![Cascading Loading Demo](assets/gif/cascading-loading-demo.gif)
- In ABP Angular, we can create form properties and load dropdown options dynamically via Extensions System
- We can reach and track any value from `Service` or `Component`
- We can create our custom library or module and contribute it to any module in application
Thanks for reading, I hope it was helpful. If you have any questions, please let me know in the comments section. 👋👋
> You can find the source code of this article on [Github]()

16
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.editorconfig

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

50
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.eslintrc.json

@ -0,0 +1,50 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

50
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.gitignore

@ -0,0 +1,50 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
# Lock Files
*.lock
*lock.json

5
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/.prettierrc

@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 100,
"arrowParens": "avoid"
}

27
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/README.md

@ -0,0 +1,27 @@
# BookStore
This is a startup project based on the ABP framework. For more information, visit <a href="https://abp.io/" target="_blank">abp.io</a>
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

186
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/angular.json

@ -0,0 +1,186 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"cli": {
"analytics": false,
"schematicCollections": ["@angular-eslint/schematics"]
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"BookStore": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/BookStore",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"allowedCommonJsDependencies": ["chart.js", "js-sha256"],
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
{
"input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"inject": true,
"bundleName": "fontawesome-all.min"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css",
"inject": true,
"bundleName": "fontawesome-v4-shims.min"
},
{
"input": "node_modules/@swimlane/ngx-datatable/index.css",
"inject": true,
"bundleName": "ngx-datatable-index"
},
{
"input": "node_modules/@swimlane/ngx-datatable/assets/icons.css",
"inject": true,
"bundleName": "ngx-datatable-icons"
},
{
"input": "node_modules/@swimlane/ngx-datatable/themes/material.css",
"inject": true,
"bundleName": "ngx-datatable-material"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.css",
"inject": false,
"bundleName": "bootstrap-dim"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.css",
"inject": false,
"bundleName": "ng-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.css",
"inject": false,
"bundleName": "layout-bundle"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.css",
"inject": false,
"bundleName": "abp-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.rtl.css",
"inject": false,
"bundleName": "bootstrap-dim.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css",
"inject": false,
"bundleName": "font-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css",
"inject": false,
"bundleName": "font-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.rtl.css",
"inject": false,
"bundleName": "ng-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.rtl.css",
"inject": false,
"bundleName": "layout-bundle.rtl"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.rtl.css",
"inject": false,
"bundleName": "abp-bundle.rtl"
},
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/styles.scss"
],
"scripts": ["node_modules/bootstrap/dist/js/bootstrap.min.js"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "2.5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "BookStore:build:production"
},
"development": {
"browserTarget": "BookStore:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "BookStore:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
}
}

44
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/karma.conf.js

@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/BookStore'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

62
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/package.json

@ -0,0 +1,62 @@
{
"name": "BookStore",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build:prod": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint"
},
"private": true,
"dependencies": {
"@abp/ng.account": "~7.3.2",
"@abp/ng.components": "~7.3.2",
"@abp/ng.core": "~7.3.2",
"@abp/ng.oauth": "~7.3.2",
"@abp/ng.identity": "~7.3.2",
"@abp/ng.setting-management": "~7.3.2",
"@abp/ng.tenant-management": "~7.3.2",
"@abp/ng.theme.shared": "~7.3.2",
"@abp/ng.theme.lepton-x": "~2.3.1",
"@angular/animations": "~16.0.0",
"@angular/common": "~16.0.0",
"@angular/compiler": "~16.0.0",
"@angular/core": "~16.0.0",
"@angular/forms": "~16.0.0",
"@angular/localize": "~16.0.0",
"@angular/platform-browser": "~16.0.0",
"@angular/platform-browser-dynamic": "~16.0.0",
"@angular/router": "~16.0.0",
"bootstrap-icons": "~1.8.3",
"rxjs": "7.8.1",
"tslib": "^2.1.0",
"zone.js": "~0.13.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~16.0.0",
"@angular-eslint/builder": "~16.0.0",
"@angular-eslint/eslint-plugin": "~16.0.0",
"@angular-eslint/eslint-plugin-template": "~16.0.0",
"@angular-eslint/schematics": "~16.0.0",
"@angular-eslint/template-parser": "~16.0.0",
"@abp/ng.schematics": "~7.3.2",
"@angular/cli": "~16.0.0",
"@angular/compiler-cli": "~16.0.0",
"@angular/language-service": "~16.0.0",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"eslint": "^8.23.0",
"jasmine-core": "~4.0.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"typescript": "~5.0.4"
}
}

44
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app-routing.module.ts

@ -0,0 +1,44 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { bookStoreRentFormPropContributors } from './books-extended';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
{
path: 'books',
loadChildren: () =>
import('@book-store/books').then(m =>
m.BooksModule.forLazy({
rentFormPropContributors: bookStoreRentFormPropContributors,
})
),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes, {})],
exports: [RouterModule],
})
export class AppRoutingModule {}

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app.component.ts

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
`,
})
export class AppComponent {}

57
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/app.module.ts

@ -0,0 +1,57 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
import { AbpOAuthModule } from '@abp/ng.oauth';
import { BOOK_ROUTE_PROVIDER } from '@book-store/books';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule.forRoot({
environment,
registerLocaleFn: registerLocale(),
localizations: [
{
culture: 'en',
resources: [
{
resourceName: 'BookStore',
texts: {
'Menu:Books': 'Books',
},
},
],
},
],
}),
AbpOAuthModule.forRoot(),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
FeatureManagementModule.forRoot(),
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER, BOOK_ROUTE_PROVIDER],
bootstrap: [AppComponent],
})
export class AppModule {}

40
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/contributors/form-prop.contributors.ts

@ -0,0 +1,40 @@
import { Validators } from '@angular/forms';
import { map } from 'rxjs';
import { ePropType, FormProp, FormPropList } from '@abp/ng.theme.shared/extensions';
import {
BookDto,
BookStoreRentFormPropContributors,
BooksService,
DefaultOption,
RentBookComponent,
eBooksComponents,
} from '@book-store/books';
const { required, maxLength } = Validators;
const bookIdProp = new FormProp<BookDto>({
type: ePropType.String,
id: 'bookId',
name: 'bookId',
displayName: 'BookStore::Name',
options: data => {
const rentBook = data.getInjected(RentBookComponent);
const { books } = data.getInjected(BooksService);
const bookOptions = books().map(({ id, name }) => ({ value: id, key: name }));
return rentBook.form.controls.genreId.valueChanges.pipe(
map((value: string | undefined) =>
value ? [DefaultOption, ...bookOptions] : [DefaultOption]
)
);
},
validators: () => [required, maxLength(255)],
});
export function bookIdPropContributor(propList: FormPropList<BookDto>) {
propList.addByIndex(bookIdProp, 2);
}
export const bookStoreRentFormPropContributors: BookStoreRentFormPropContributors = {
[eBooksComponents.RentBook]: [bookIdPropContributor],
};

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/contributors/index.ts

@ -0,0 +1 @@
export * from './form-prop.contributors';

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books-extended/index.ts

@ -0,0 +1 @@
export * from './contributors';

36
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books-routing.module.ts

@ -0,0 +1,36 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, mapToCanActivate } from '@angular/router';
import {
ReplaceableComponents,
ReplaceableRouteContainerComponent,
RouterOutletComponent,
} from '@abp/ng.core';
import { eBooksComponents } from './enums';
import { BooksExtensionsGuard } from './guards';
import BooksComponent from './books.component';
const routes: Routes = [
{
path: '',
component: RouterOutletComponent,
canActivate: mapToCanActivate([BooksExtensionsGuard]),
children: [
{
path: '',
component: ReplaceableRouteContainerComponent,
data: {
replaceableComponent: {
key: eBooksComponents.Books,
defaultComponent: BooksComponent,
} as ReplaceableComponents.RouteData<BooksComponent>,
},
},
],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class BooksRoutingModule {}

34
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.component.html

@ -0,0 +1,34 @@
<div class="card">
<div class="card-body p-2">
<div class="d-flex align-items-center justify-content-between">
<h3 class="content-header-title m-0 mb-1">
{{ 'BookStore::RentedBooks' | abpLocalization }}
</h3>
<button class="btn btn-sm btn-primary mb-2" (click)="modalVisible = true">
<i class="fa fa-plus"></i> {{ 'BookStore::RentABook' | abpLocalization }}
</button>
</div>
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Book Name</th>
<th>Author</th>
<th>Genre</th>
<th>Return Date</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of booksService.rentedBooks()">
<td>{{ book.name }}</td>
<td>{{ book.author }}</td>
<td>{{ book.genre }}</td>
<td>{{ book.returnDate | date }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<app-rent-book *abpVisible="modalVisible" (modalVisibleChange)="modalVisible = $event" />

19
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.component.ts

@ -0,0 +1,19 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { NgFor, NgIf } from '@angular/common';
import { CoreModule } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { BooksService } from './proxy';
import { RentBookComponent } from './components';
@Component({
standalone: true,
selector: 'app-books',
templateUrl: './books.component.html',
imports: [NgIf, NgFor, CoreModule, ThemeSharedModule, RentBookComponent],
providers: [BooksService],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class BooksComponent {
protected readonly booksService = inject(BooksService);
protected modalVisible = false;
}

47
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/books.module.ts

@ -0,0 +1,47 @@
import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core';
import { CoreModule, LazyModuleFactory } from '@abp/ng.core';
import { BooksConfigOptions } from './models';
import { BooksExtensionsGuard } from './guards';
import { BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS } from './tokens';
import { BooksRoutingModule } from './books-routing.module';
@NgModule({
imports: [
BooksRoutingModule,
CoreModule.forChild({
localizations: [
{
culture: 'en',
resources: [
{
resourceName: 'BookStore',
texts: {
RentedBooks: 'Rented books',
RentABook: 'Rent a book',
Author: 'Author',
},
},
],
},
],
}),
],
})
export class BooksModule {
static forChild(options: BooksConfigOptions = {}): ModuleWithProviders<BooksModule> {
return {
ngModule: BooksModule,
providers: [
{
provide: BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS,
useValue: options.rentFormPropContributors,
},
BooksExtensionsGuard,
],
};
}
static forLazy(options: BooksConfigOptions = {}): NgModuleFactory<BooksConfigOptions> {
return new LazyModuleFactory(BooksModule.forChild(options));
}
}

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/index.ts

@ -0,0 +1 @@
export * from './rent-book/rent-book.component';

30
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/rent-book/rent-book.component.html

@ -0,0 +1,30 @@
<abp-modal
[visible]="modalVisible"
[busy]="modalBusy"
(visibleChange)="modalVisibleChange.next($event)"
>
<ng-template #abpHeader>
<h3>{{ 'BookStore::RentABook' | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<ng-template #loaderRef>
<div class="text-center">
<i class="fa fa-pulse fa-spinner" aria-hidden="true"></i>
</div>
</ng-template>
<form *ngIf="form; else loaderRef" [formGroup]="form" (ngSubmit)="save()" validateOnSubmit>
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
</form>
</ng-template>
<ng-template #abpFooter>
<button abpClose type="button" class="btn btn-secondary">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">
{{ 'AbpIdentity::Save' | abpLocalization }}
</abp-button>
</ng-template>
</abp-modal>

91
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/components/rent-book/rent-book.component.ts

@ -0,0 +1,91 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Injector,
Output,
inject,
} from '@angular/core';
import { FormGroup } from '@angular/forms';
import { CoreModule, uuid } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import {
EXTENSIONS_IDENTIFIER,
FormPropData,
UiExtensionsModule,
generateFormFromProps,
} from '@abp/ng.theme.shared/extensions';
import { AuthorService, BookDto, BooksService } from '../../proxy';
import { eBooksComponents } from '../../enums';
@Component({
standalone: true,
selector: 'app-rent-book',
templateUrl: './rent-book.component.html',
imports: [CoreModule, UiExtensionsModule, ThemeSharedModule],
providers: [
{
provide: EXTENSIONS_IDENTIFIER,
useValue: eBooksComponents.RentBook,
},
AuthorService,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RentBookComponent {
protected readonly injector = inject(Injector);
protected readonly authorService = inject(AuthorService);
protected readonly booksService = inject(BooksService);
//#region Just for demo
readonly #authors = this.authorService.authors();
readonly #genres = this.booksService.genres();
readonly #books = this.booksService.books();
//#endregion
protected modalVisible = true;
@Output() modalVisibleChange = new EventEmitter<boolean>();
selected: BookDto;
form: FormGroup;
modalBusy = false;
protected buildForm(): void {
const data = new FormPropData(this.injector, this.selected);
this.form = generateFormFromProps(data);
}
constructor() {
this.buildForm();
}
save(): void {
if (this.form.invalid) {
return;
}
this.modalBusy = true;
const { authorId, genreId, bookId, returnDate } = this.form.value;
//#region Just for demo
const authorName = this.#authors.find(({ id }) => id === authorId).name;
const genreName = this.#genres.find(({ id }) => id === genreId).name;
const bookName = this.#books.find(({ id }) => id === bookId).name;
//#endregion
this.booksService.rentedBooks.update(books => [
{
id: uuid(),
name: bookName,
author: authorName,
genre: genreName,
returnDate,
},
...books,
]);
this.modalBusy = false;
this.modalVisible = false;
}
}

55
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/defaults/default-books-form-props.ts

@ -0,0 +1,55 @@
import { Validators } from '@angular/forms';
import { map, of } from 'rxjs';
import { ePropType, FormProp } from '@abp/ng.theme.shared/extensions';
import { BookDto, AuthorService, BooksService } from '../proxy';
import { RentBookComponent } from '../components';
import { DefaultOption } from '../utils';
const { required } = Validators;
export const DEFAULT_RENT_FORM_PROPS = FormProp.createMany<BookDto>([
{
type: ePropType.String,
id: 'authorId',
name: 'authorId',
displayName: 'BookStore::Author',
defaultValue: null,
validators: () => [required],
options: data => {
const { authors } = data.getInjected(AuthorService);
return of([
DefaultOption,
...authors().map(author => ({ value: author.id, key: author.name })),
]);
},
},
{
type: ePropType.String,
id: 'genreId',
name: 'genreId',
displayName: 'BookStore::Genre',
defaultValue: null,
validators: () => [required],
options: data => {
const rentBookComponent = data.getInjected(RentBookComponent);
const { genres } = data.getInjected(BooksService);
const genreOptions = genres().map(({ id, name }) => ({ value: id, key: name }));
return rentBookComponent.form.controls.authorId.valueChanges.pipe(
map((value: string | undefined) =>
value ? [DefaultOption, ...genreOptions] : [DefaultOption]
)
);
},
},
{
type: ePropType.Date,
id: 'returnDate',
name: 'returnDate',
displayName: 'BookStore::ReturnDate',
defaultValue: null,
validators: () => [required],
},
]);

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/defaults/index.ts

@ -0,0 +1 @@
export * from './default-books-form-props';

4
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/enums/components.ts

@ -0,0 +1,4 @@
export enum eBooksComponents {
Books = 'Books',
RentBook = 'Books.RentBook',
}

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/enums/index.ts

@ -0,0 +1 @@
export * from './components';

37
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/guards/extensions.guard.ts

@ -0,0 +1,37 @@
import { Injectable, inject } from '@angular/core';
import { Observable, map, tap } from 'rxjs';
import { ConfigStateService, IAbpGuard } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
mapEntitiesToContributors,
mergeWithDefaultProps,
} from '@abp/ng.theme.shared/extensions';
import {
BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS,
DEFAULT_BOOK_STORE_CREATE_FORM_PROPS,
} from '../tokens';
@Injectable()
export class BooksExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
const createFormContributors =
inject(BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS, { optional: true }) || {};
return getObjectExtensionEntitiesFromStore(this.configState, 'BookStore').pipe(
mapEntitiesToContributors(this.configState, 'BookStore'),
tap(objectExtensionContributors => {
mergeWithDefaultProps(
this.extensions.createFormProps,
DEFAULT_BOOK_STORE_CREATE_FORM_PROPS,
objectExtensionContributors.createForm,
createFormContributors
);
}),
map(() => true)
);
}
}

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/guards/index.ts

@ -0,0 +1 @@
export * from './extensions.guard';

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/index.ts

@ -0,0 +1,10 @@
export * from './components';
export * from './defaults';
export * from './enums';
export * from './models';
export * from './providers';
export * from './proxy';
export * from './tokens';
export * from './utils';
export * from './books.component';
export * from './books.module';

11
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/models/config-options.ts

@ -0,0 +1,11 @@
import { CreateFormPropContributorCallback } from '@abp/ng.theme.shared/extensions';
import { BookDto } from '../proxy';
import { eBooksComponents } from '../enums';
export type BookStoreRentFormPropContributors = Partial<{
[eBooksComponents.RentBook]: CreateFormPropContributorCallback<BookDto>[];
}>;
export interface BooksConfigOptions {
rentFormPropContributors?: BookStoreRentFormPropContributors;
}

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/models/index.ts

@ -0,0 +1 @@
export * from './config-options';

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/providers/index.ts

@ -0,0 +1 @@
export * from './route.provider';

22
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/providers/route.provider.ts

@ -0,0 +1,22 @@
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { APP_INITIALIZER, inject } from '@angular/core';
export const BOOK_ROUTE_PROVIDER = [
{ provide: APP_INITIALIZER, useFactory: provideBookStoreRoutes, multi: true },
];
function provideBookStoreRoutes() {
const routesService = inject(RoutesService);
return () => {
routesService.add([
{
path: '/books',
name: '::Menu:Books',
iconClass: 'fas fa-book',
order: 1,
layout: eLayoutType.application,
},
]);
};
}

12
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/author.service.ts

@ -0,0 +1,12 @@
import { uuid } from '@abp/ng.core';
import { Injectable, signal } from '@angular/core';
@Injectable()
export class AuthorService {
readonly authors = signal([
{ id: uuid(), name: 'J.K. Rowling' },
{ id: uuid(), name: 'George R.R. Martin' },
{ id: uuid(), name: 'Stephen King' },
{ id: uuid(), name: 'J.R.R. Tolkien' },
]);
}

41
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/books.service.ts

@ -0,0 +1,41 @@
import { uuid } from '@abp/ng.core';
import { Injectable, signal } from '@angular/core';
@Injectable()
export class BooksService {
readonly genres = signal([
{ id: uuid(), name: 'Fantasy' },
{ id: uuid(), name: 'Science Fiction' },
{ id: uuid(), name: 'Romance' },
]);
readonly books = signal([
{ id: uuid(), name: 'The Shining', author: 'Stephen King', genre: 'Science Fiction' },
{ id: uuid(), name: 'The Notebook', author: 'Nicholas Sparks', genre: 'Romance' },
{ id: uuid(), name: 'The Last Song', author: 'Nicholas Sparks', genre: 'Romance' },
]);
readonly rentedBooks = signal([
{
id: uuid(),
name: 'The Lord of the Rings',
author: 'J. R. R. Tolkien',
genre: 'Fantasy',
returnDate: new Date('2024-01-01'),
},
{
id: uuid(),
name: 'The Hobbit',
author: 'J. R. R. Tolkien',
genre: 'Fantasy',
returnDate: new Date('2024-02-01'),
},
{
id: uuid(),
name: 'The Stand',
author: 'Stephen King',
genre: 'Science Fiction',
returnDate: new Date('2024-03-01'),
},
]);
}

3
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/index.ts

@ -0,0 +1,3 @@
export * from './models';
export * from './author.service';
export * from './books.service';

11
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/proxy/models.ts

@ -0,0 +1,11 @@
import { ExtensibleFullAuditedEntityDto, ExtensibleObject } from '@abp/ng.core';
export interface BookDto extends ExtensibleFullAuditedEntityDto<string> {
authorId: string;
name: string;
}
export interface CreateUpdateRentBookDto extends ExtensibleObject {
authorId: string;
name: string;
}

20
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/tokens/extensions.token.ts

@ -0,0 +1,20 @@
import { CreateFormPropContributorCallback } from '@abp/ng.theme.shared/extensions';
import { InjectionToken } from '@angular/core';
import { BookDto } from '../proxy';
import { eBooksComponents } from '../enums';
import { DEFAULT_RENT_FORM_PROPS } from '../defaults';
export const DEFAULT_BOOK_STORE_CREATE_FORM_PROPS = {
[eBooksComponents.RentBook]: DEFAULT_RENT_FORM_PROPS,
};
export const BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS =
new InjectionToken<CreateFormPropContributors>('BOOK_STORE_RENT_FORM_PROP_CONTRIBUTORS');
type CreateFormPropContributors = Partial<{
[eBooksComponents.RentBook]: CreateFormPropContributorCallback<BookDto>[];
/**
* Other creation form prop contributors...
*/
// [eBooksComponents.CreateBook]: CreateFormPropContributorCallback<BookDto>[];
}>;

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/tokens/index.ts

@ -0,0 +1 @@
export * from './extensions.token';

6
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/utils/common.ts

@ -0,0 +1,6 @@
import { ABP } from '@abp/ng.core';
export const DefaultOption = {
value: null,
key: '-',
} as ABP.Option<any>;

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/books/utils/index.ts

@ -0,0 +1 @@
export * from './common';

11
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home-routing.module.ts

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
const routes: Routes = [{ path: '', component: HomeComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}

380
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.component.html

@ -0,0 +1,380 @@
<div class="container">
<div class="p-5 text-center">
<div class="d-inline-block bg-success text-white p-1 h5 rounded mb-4" role="alert">
<h5 class="m-1">
<i class="fas fa-rocket" aria-hidden="true"></i> Congratulations,
<strong>BookStore</strong> is successfully running!
</h5>
</div>
<h1>{{ '::Welcome' | abpLocalization }}</h1>
<p class="lead px-lg-5 mx-lg-5">{{ '::LongWelcomeMessage' | abpLocalization }}</p>
<a
*ngIf="!hasLoggedIn"
(click)="authService.navigateToLogin()"
class="px-4 btn btn-primary ms-1"
role="button"
><i class="fa fa-sign-in" aria-hidden="true"></i>
{{ 'AbpAccount::Login' | abpLocalization }}</a
>
</div>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-auto text-center">
<img
src="https://abp.io/assets/png/mastering-abp-framework.webp"
style="max-width: 400px"
class="w-100 mb-5 my-md-3"
/>
</div>
<div class="col-md d-flex align-items-center">
<div class="pe-0 pe-md-4">
<small class="text-uppercase text-muted">THE OFFICIAL GUIDE</small>
<h2 class="mb-4">Mastering ABP Framework</h2>
<p class="mb-4">
Written by the creator of the ABP Framework, this book will help you gain a complete
understanding of the framework and modern web application development techniques.
</p>
<div class="mb-4">
<a
href="https://www.amazon.com/gp/product/B097Z2DM8Q/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0"
class="btn btn-success mb-1"
>
Buy on Amazon US
</a>
&nbsp;
<a
href="https://www.packtpub.com/product/mastering-abp-framework/9781801079242"
class="btn btn-primary mb-1"
>
Buy on PACKT
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="my-3 text-center">
<h3>Let's improve your application!</h3>
<p>Here are some links to help you get started:</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<div class="row text-center justify-content-md-center">
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Learn the ABP Framework',
description:
'Explore the compherensive documentation to learn how to build a modern web application.',
links: [
{
href: 'https://docs.abp.io/en/abp/latest?ref=tmpl',
label: 'See Documents'
}
]
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Samples',
description: 'See the example projects built with the ABP Framework.',
links: [
{
href: 'https://docs.abp.io/en/abp/latest/Samples/Index?ref=tmpl',
label: 'All samples'
}
]
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'ABP Community',
description: 'Get involved with a vibrant community and become a contributor.',
links: [
{
href: 'https://community.abp.io/',
label: 'Community'
},
{
href: 'https://docs.abp.io/en/abp/latest/Contribution/Index?ref=tmpl',
label: 'Contribute'
}
]
}
}
"
></ng-container>
</div>
<div class="row text-center mt-lg-3 justify-content-md-center">
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'ABP Blog',
description: 'Take a look at our recently published articles.',
links: [
{
href: 'https://blog.abp.io/abp?ref=tmpl',
label: 'See Blog'
}
]
}
}
"
></ng-container>
<ng-template #githubButtonsTemplate>
<p class="mb-1">
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp&amp;title=&amp;aria-label=Star%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-star&amp;data-text=Star&amp;data-size=large&amp;data-show-count=true"
style="
width: 122px;
height: 28px;
border: none;
display: inline-block;
margin-right: 4px;
"
></iframe>
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp%2Fissues&amp;title=&amp;aria-label=Issue%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-issue-opened&amp;data-text=Issue&amp;data-size=large"
style="
width: 72px;
height: 28px;
border: none;
display: inline-block;
margin-right: 4px;
"
></iframe>
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp%2Ffork&amp;title=&amp;aria-label=Fork%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-repo-forked&amp;data-text=Fork&amp;data-size=large&amp;"
style="width: 72px; height: 28px; border: none; display: inline-block"
></iframe>
</p>
</ng-template>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Github',
description:
'Do you love the ABP Framework? Please <strong>give a star</strong> to support it!',
links: [
{
href: 'https://github.com/abpframework/abp/issues/new?template=feature.md',
label: 'Request a feature'
}
],
customTemplate: githubButtonsTemplate
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Stackoverflow',
description: 'See answers to previously asked questions or ask a new one.',
links: [
{
href: 'https://stackoverflow.com/questions/tagged/abp',
label: 'Questions'
},
{
href: 'https://stackoverflow.com/questions/ask',
label: 'Ask a Question'
}
]
}
}
"
></ng-container>
</div>
</div>
</div>
<div class="mt-5 my-3 text-center">
<h3>Meet the ABP Commercial</h3>
<p>A Complete Web Application Platform Built on the ABP Framework</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<p class="px-lg-5 mx-lg-5 py-3 text-center">
<a href="https://commercial.abp.io/" target="_blank">ABP Commercial</a> is a platform based
on the open source ABP framework. It provides pre-built application modules, rapid
application development tooling, professional UI themes, premium support and more.
</p>
<div class="row text-center justify-content-md-center">
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Startup Templates',
href: 'https://commercial.abp.io/startup-templates?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Application Modules',
href: 'https://commercial.abp.io/modules?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Developer<br />Tools',
href: 'https://commercial.abp.io/tools?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'UI<br />Themes',
href: 'https://commercial.abp.io/themes?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Premium Support',
href: 'https://support.abp.io/QA/Questions?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Additional Services',
href: 'https://commercial.abp.io/additional-services?ref=tmpl'
}
}
"
></ng-container>
</div>
</div>
</div>
<div class="mb-5 text-center">
<p class="align-middle">
<a href="https://twitter.com/abpframework" target="_blank" class="mx-2"
><i class="fa fa-twitter" aria-hidden="true"></i
><span class="text-secondary"> Abp Framework</span></a
>
<a href="https://twitter.com/abpcommercial" target="_blank" class="mx-2"
><i class="fa fa-twitter" aria-hidden="true"></i
><span class="text-secondary"> Abp Commercial</span></a
>
<a href="https://github.com/abpframework/abp" target="_blank" class="mx-2"
><i class="fa fa-github" aria-hidden="true"></i
><span class="text-secondary"> abpframework</span></a
>
</p>
</div>
</div>
<ng-template #starterLinkTemplate let-context>
<div class="col-lg-4 border-start">
<div class="p-4">
<h5 class="mb-3">
<i class="fas fa-cubes text-secondary d-block my-3 fa-2x" aria-hidden="true"></i>
{{ context.title }}
</h5>
<p [innerHTML]="context.description"></p>
<ng-container
*ngIf="context.customTemplate"
[ngTemplateOutlet]="context.customTemplate"
></ng-container>
<a
*ngFor="let link of context.links"
[href]="link.href"
target="_blank"
class="btn btn-link px-1"
>{{ link.label }} <i class="fas fa-chevron-right" aria-hidden="true"></i
></a>
</div>
</div>
</ng-template>
<ng-template #featuresTemplate let-context>
<div class="col-lg-2 border-start">
<div class="p-3">
<h6>
<i class="fas fa-plus d-block mb-3 fa- 2x text-secondary" aria-hidden="true"></i>
<span [innerHTML]="context.title"></span>
<a [href]="context.href" target="_blank" class="d-block mt-2 btn btn-sm btn-link"
>Details <i class="fas fa-chevron-right" aria-hidden="true"></i
></a>
</h6>
</div>
</div>
</ng-template>
<style scoped>
.col-lg-2.border-start:nth-of-type(6n + 1) {
border-left: 0 !important;
}
.col-lg-4.border-start:nth-of-type(3n + 1) {
border-left: 0 !important;
}
@media (max-width: 991px) {
.border-start {
border-left: 0 !important;
}
}
</style>

15
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.component.ts

@ -0,0 +1,15 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { AuthService } from '@abp/ng.core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HomeComponent {
protected readonly authService = inject(AuthService);
get hasLoggedIn(): boolean {
return this.authService.isAuthenticated;
}
}

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/home/home.module.ts

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
@NgModule({
declarations: [HomeComponent],
imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}

20
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/route.provider.ts

@ -0,0 +1,20 @@
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { APP_INITIALIZER } from '@angular/core';
export const APP_ROUTE_PROVIDER = [
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
];
function configureRoutes(routesService: RoutesService) {
return () => {
routesService.add([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
]);
};
}

23
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/app/shared/shared.module.ts

@ -0,0 +1,23 @@
import { CoreModule } from '@abp/ng.core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgxValidateCoreModule } from '@ngx-validate/core';
@NgModule({
declarations: [],
imports: [
CoreModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule
],
exports: [
CoreModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule
],
providers: []
})
export class SharedModule {}

0
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/.gitkeep

BIN
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/images/logo/logo-light-thumbnail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/assets/images/logo/logo-light.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

26
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/environments/environment.prod.ts

@ -0,0 +1,26 @@
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: true,
application: {
baseUrl,
name: 'BookStore',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44315/',
redirectUri: baseUrl,
clientId: 'BookStore_App',
responseType: 'code',
scope: 'offline_access BookStore',
requireHttps: true
},
apis: {
default: {
url: 'https://localhost:44315',
rootNamespace: 'Volo.BookStore',
},
},
} as Environment;

26
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/environments/environment.ts

@ -0,0 +1,26 @@
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: false,
application: {
baseUrl,
name: 'BookStore',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44315/',
redirectUri: baseUrl,
clientId: 'BookStore_App',
responseType: 'code',
scope: 'offline_access BookStore',
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44315',
rootNamespace: 'Volo.BookStore',
},
},
} as Environment;

BIN
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

16
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/index.html

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>BookStore</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body class="bg-light">
<app-root>
<div class="donut centered"></div>
</app-root>
</body>
</html>

13
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/main.ts

@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));

54
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/polyfills.ts

@ -0,0 +1,54 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/******************************************************************
* Load `$localize` - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';

30
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/styles.scss

@ -0,0 +1,30 @@
/* You can add global styles to this file, and also import other style files */
@keyframes donut-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
:root {
--lpx-logo: url('/assets/images/logo/logo-light.png');
--lpx-logo-icon: url('/assets/images/logo/logo-light-thumbnail.png');
}
.donut {
display: inline-block;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #7983ff;
border-radius: 50%;
width: 30px;
height: 30px;
animation: donut-spin 1.2s linear infinite;
&.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/src/test.ts

@ -0,0 +1,10 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());

2
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/start.ps1

@ -0,0 +1,2 @@
yarn
yarn start

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.app.json

@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.d.ts"]
}

26
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.json

@ -0,0 +1,26 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "es2020",
"lib": ["es2018", "dom"],
"paths": {
"@proxy": ["src/app/proxy/index.ts"],
"@proxy/*": ["src/app/proxy/*"],
"@book-store/books": ["src/app/books/index.ts"]
},
"useDefineForClassFields": false
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false
}
}

10
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/angular/tsconfig.spec.json

@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["jasmine"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

1
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.gitattributes

@ -0,0 +1 @@
**/wwwroot/libs/** linguist-vendored

265
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.gitignore

@ -0,0 +1,265 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# BookStore
src/Volo.BookStore.Web/Logs/*
src/Volo.BookStore.Web.Host/Logs/*
src/Volo.BookStore.AuthServer/Logs/*
src/Volo.BookStore.HttpApi.Host/Logs/*
src/Volo.BookStore.HttpApi.Host/Logs/*
src/Volo.BookStore.DbMigrator/Logs/*
src/Volo.BookStore.Blazor.Server/Logs/*
src/Volo.BookStore.Blazor.Server.Tiered/Logs/*
# Use abp install-libs to restore.
**/wwwroot/libs/*

5
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/.prettierrc

@ -0,0 +1,5 @@
{
"singleQuote": true,
"useTabs": false,
"tabWidth": 4
}

5
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/NuGet.Config

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
</packageSources>
</configuration>

123
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/Volo.BookStore.sln

@ -0,0 +1,123 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29020.237
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Domain", "src\Volo.BookStore.Domain\Volo.BookStore.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Application", "src\Volo.BookStore.Application\Volo.BookStore.Application.csproj", "{1A94A50E-06DC-43C1-80B5-B662820EC3EB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CA9AC87F-097E-4F15-8393-4BC07735A5B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{04DBDB01-70F4-4E06-B468-8F87850B22BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Application.Tests", "test\Volo.BookStore.Application.Tests\Volo.BookStore.Application.Tests.csproj", "{50B2631D-129C-47B3-A587-029CCD6099BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.MongoDB", "src\Volo.BookStore.MongoDB\Volo.BookStore.MongoDB.csproj", "{E3444355-D47E-431E-BDD0-DD3A7113B2AE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Domain.Shared", "src\Volo.BookStore.Domain.Shared\Volo.BookStore.Domain.Shared.csproj", "{42F719ED-8413-4895-B5B4-5AB56079BC66}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Application.Contracts", "src\Volo.BookStore.Application.Contracts\Volo.BookStore.Application.Contracts.csproj", "{520659C8-C734-4298-A3DA-B539DB9DFC0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.HttpApi", "src\Volo.BookStore.HttpApi\Volo.BookStore.HttpApi.csproj", "{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.HttpApi.Client", "src\Volo.BookStore.HttpApi.Client\Volo.BookStore.HttpApi.Client.csproj", "{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.TestBase", "test\Volo.BookStore.TestBase\Volo.BookStore.TestBase.csproj", "{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.Domain.Tests", "test\Volo.BookStore.Domain.Tests\Volo.BookStore.Domain.Tests.csproj", "{E512F4D9-9375-480F-A2F6-A46509F9D824}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.MongoDB.Tests", "test\Volo.BookStore.MongoDB.Tests\Volo.BookStore.MongoDB.Tests.csproj", "{6015D17B-104B-4EC2-A9B7-D8A40C891458}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.HttpApi.Client.ConsoleTestApp", "test\Volo.BookStore.HttpApi.Client.ConsoleTestApp\Volo.BookStore.HttpApi.Client.ConsoleTestApp.csproj", "{EF480016-9127-4916-8735-D2466BDBC582}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.DbMigrator", "src\Volo.BookStore.DbMigrator\Volo.BookStore.DbMigrator.csproj", "{AA94D832-1CCC-4715-95A9-A483F23A1A5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.BookStore.HttpApi.Host", "src\Volo.BookStore.HttpApi.Host\Volo.BookStore.HttpApi.Host.csproj", "{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.Build.0 = Release|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.Build.0 = Release|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.Build.0 = Release|Any CPU
{E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3444355-D47E-431E-BDD0-DD3A7113B2AE}.Release|Any CPU.Build.0 = Release|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.Build.0 = Release|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.Build.0 = Release|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.Build.0 = Release|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.Build.0 = Release|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.Build.0 = Release|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.Build.0 = Release|Any CPU
{6015D17B-104B-4EC2-A9B7-D8A40C891458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6015D17B-104B-4EC2-A9B7-D8A40C891458}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6015D17B-104B-4EC2-A9B7-D8A40C891458}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6015D17B-104B-4EC2-A9B7-D8A40C891458}.Release|Any CPU.Build.0 = Release|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.Build.0 = Release|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.Build.0 = Release|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{1A94A50E-06DC-43C1-80B5-B662820EC3EB} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{50B2631D-129C-47B3-A587-029CCD6099BC} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{E3444355-D47E-431E-BDD0-DD3A7113B2AE} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{42F719ED-8413-4895-B5B4-5AB56079BC66} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{520659C8-C734-4298-A3DA-B539DB9DFC0B} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{E512F4D9-9375-480F-A2F6-A46509F9D824} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{6015D17B-104B-4EC2-A9B7-D8A40C891458} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{EF480016-9127-4916-8735-D2466BDBC582} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{AA94D832-1CCC-4715-95A9-A483F23A1A5D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6AAFA1C6-603E-13FA-45E5-7910AA9F661D}
EndGlobalSection
EndGlobal

23
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/Volo.BookStore.sln.DotSettings

@ -0,0 +1,23 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/IntelliSenseCompletingCharacters/CSharpCompletingCharacters/UpgradedFromVSSettings/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceDoWhileStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceFixedStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForeachStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceLockStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceUsingStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceWhileStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_REDUNDANT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Implementations/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Overrides/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
</wpf:ResourceDictionary>

19
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/common.props

@ -0,0 +1,19 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>1.0.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<AbpProjectType>app</AbpProjectType>
</PropertyGroup>
<Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile">
<PropertyGroup>
<NoWarn>$(NoWarn);0436</NoWarn>
</PropertyGroup>
</Target>
<ItemGroup>
<Content Remove="$(UserProfile)\.nuget\packages\*\*\contentFiles\any\*\*.abppkg*.json" />
</ItemGroup>
</Project>

28
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/BookStoreApplicationContractsModule.cs

@ -0,0 +1,28 @@
using Volo.Abp.Account;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace Volo.BookStore;
[DependsOn(
typeof(BookStoreDomainSharedModule),
typeof(AbpAccountApplicationContractsModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpIdentityApplicationContractsModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(AbpTenantManagementApplicationContractsModule),
typeof(AbpObjectExtendingModule)
)]
public class BookStoreApplicationContractsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
BookStoreDtoExtensions.Configure();
}
}

28
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/BookStoreDtoExtensions.cs

@ -0,0 +1,28 @@
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace Volo.BookStore;
public static class BookStoreDtoExtensions
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can add extension properties to DTOs
* defined in the depended modules.
*
* Example:
*
* ObjectExtensionManager.Instance
* .AddOrUpdateProperty<IdentityRoleDto, string>("Title");
*
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Object-Extensions
*/
});
}
}

20
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Permissions/BookStorePermissionDefinitionProvider.cs

@ -0,0 +1,20 @@
using Volo.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace Volo.BookStore.Permissions;
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup(BookStorePermissions.GroupName);
//Define your own permissions here. Example:
//myGroup.AddPermission(BookStorePermissions.MyPermission1, L("Permission:MyPermission1"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<BookStoreResource>(name);
}
}

9
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Permissions/BookStorePermissions.cs

@ -0,0 +1,9 @@
namespace Volo.BookStore.Permissions;
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
//Add your own permission names. Example:
//public const string MyPermission1 = GroupName + ".MyPermission1";
}

25
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application.Contracts/Volo.BookStore.Application.Contracts.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0</TargetFrameworks>
<Nullable>enable</Nullable>
<RootNamespace>Volo.BookStore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.BookStore.Domain.Shared\Volo.BookStore.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.ObjectExtending" Version="7.3.2" />
<PackageReference Include="Volo.Abp.Account.Application.Contracts" Version="7.3.2" />
<PackageReference Include="Volo.Abp.Identity.Application.Contracts" Version="7.3.2" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application.Contracts" Version="7.3.2" />
<PackageReference Include="Volo.Abp.TenantManagement.Application.Contracts" Version="7.3.2" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application.Contracts" Version="7.3.2" />
<PackageReference Include="Volo.Abp.SettingManagement.Application.Contracts" Version="7.3.2" />
</ItemGroup>
</Project>

17
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreAppService.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.BookStore.Localization;
using Volo.Abp.Application.Services;
namespace Volo.BookStore;
/* Inherit your application services from this class.
*/
public abstract class BookStoreAppService : ApplicationService
{
protected BookStoreAppService()
{
LocalizationResource = typeof(BookStoreResource);
}
}

13
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs

@ -0,0 +1,13 @@
using AutoMapper;
namespace Volo.BookStore;
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
}
}

31
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/BookStoreApplicationModule.cs

@ -0,0 +1,31 @@
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace Volo.BookStore;
[DependsOn(
typeof(BookStoreDomainModule),
typeof(AbpAccountApplicationModule),
typeof(BookStoreApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule)
)]
public class BookStoreApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<BookStoreApplicationModule>();
});
}
}

2
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/Properties/AssemblyInfo.cs

@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleToAttribute("Volo.BookStore.Application.Tests")]

25
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Application/Volo.BookStore.Application.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>Volo.BookStore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.BookStore.Domain\Volo.BookStore.Domain.csproj" />
<ProjectReference Include="..\Volo.BookStore.Application.Contracts\Volo.BookStore.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.Application" Version="7.3.2" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="7.3.2" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="7.3.2" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="7.3.2" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application" Version="7.3.2" />
<PackageReference Include="Volo.Abp.SettingManagement.Application" Version="7.3.2" />
</ItemGroup>
</Project>

14
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/BookStoreDbMigratorModule.cs

@ -0,0 +1,14 @@
using Volo.BookStore.MongoDB;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.BookStore.DbMigrator;
[DependsOn(
typeof(AbpAutofacModule),
typeof(BookStoreMongoDbModule),
typeof(BookStoreApplicationContractsModule)
)]
public class BookStoreDbMigratorModule : AbpModule
{
}

51
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/DbMigratorHostedService.cs

@ -0,0 +1,51 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Volo.BookStore.Data;
using Serilog;
using Volo.Abp;
using Volo.Abp.Data;
namespace Volo.BookStore.DbMigrator;
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IConfiguration _configuration;
public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime, IConfiguration configuration)
{
_hostApplicationLifetime = hostApplicationLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = await AbpApplicationFactory.CreateAsync<BookStoreDbMigratorModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
options.AddDataMigrationEnvironment();
}))
{
await application.InitializeAsync();
await application
.ServiceProvider
.GetRequiredService<BookStoreDbMigrationService>()
.MigrateAsync();
await application.ShutdownAsync();
_hostApplicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

41
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/Program.cs

@ -0,0 +1,41 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
namespace Volo.BookStore.DbMigrator;
class Program
{
static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
#if DEBUG
.MinimumLevel.Override("Volo.BookStore", LogEventLevel.Debug)
#else
.MinimumLevel.Override("Volo.BookStore", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddAppSettingsSecretsJson()
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DbMigratorHostedService>();
});
}

45
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/Volo.BookStore.DbMigrator.csproj

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="appsettings.secrets.json" />
<Content Include="appsettings.secrets.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="7.3.2" />
<ProjectReference Include="..\Volo.BookStore.Application.Contracts\Volo.BookStore.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.BookStore.MongoDB\Volo.BookStore.MongoDB.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
</Project>

30
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.DbMigrator/appsettings.json

@ -0,0 +1,30 @@
{
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/BookStore"
},
"Redis": {
"Configuration": "127.0.0.1"
},
"OpenIddict": {
"Applications": {
"BookStore_Web": {
"ClientId": "BookStore_Web",
"ClientSecret": "1q2w3e*",
"RootUrl": "https://localhost:44375"
},
"BookStore_App": {
"ClientId": "BookStore_App",
"RootUrl": "http://localhost:4200"
},
"BookStore_BlazorServerTiered": {
"ClientId": "BookStore_BlazorServerTiered",
"ClientSecret": "1q2w3e*",
"RootUrl": "https://localhost:44367"
},
"BookStore_Swagger": {
"ClientId": "BookStore_Swagger",
"RootUrl": "https://localhost:44315"
}
}
}
}

6
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreDomainErrorCodes.cs

@ -0,0 +1,6 @@
namespace Volo.BookStore;
public static class BookStoreDomainErrorCodes
{
/* You can add your business exception error codes here, as constants */
}

58
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreDomainSharedModule.cs

@ -0,0 +1,58 @@
using Volo.BookStore.Localization;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
namespace Volo.BookStore;
[DependsOn(
typeof(AbpAuditLoggingDomainSharedModule),
typeof(AbpBackgroundJobsDomainSharedModule),
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpIdentityDomainSharedModule),
typeof(AbpOpenIddictDomainSharedModule),
typeof(AbpPermissionManagementDomainSharedModule),
typeof(AbpSettingManagementDomainSharedModule),
typeof(AbpTenantManagementDomainSharedModule)
)]
public class BookStoreDomainSharedModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
BookStoreGlobalFeatureConfigurator.Configure();
BookStoreModuleExtensionConfigurator.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BookStoreDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<BookStoreResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/BookStore");
options.DefaultResourceType = typeof(BookStoreResource);
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("BookStore", typeof(BookStoreResource));
});
}
}

22
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreGlobalFeatureConfigurator.cs

@ -0,0 +1,22 @@
using Volo.Abp.Threading;
namespace Volo.BookStore;
public static class BookStoreGlobalFeatureConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can configure (enable/disable) global features of the used modules here.
*
* YOU CAN SAFELY DELETE THIS CLASS AND REMOVE ITS USAGES IF YOU DON'T NEED TO IT!
*
* Please refer to the documentation to lear more about the Global Features System:
* https://docs.abp.io/en/abp/latest/Global-Features
*/
});
}
}

73
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/BookStoreModuleExtensionConfigurator.cs

@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace Volo.BookStore;
public static class BookStoreModuleExtensionConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ConfigureExistingProperties();
ConfigureExtraProperties();
});
}
private static void ConfigureExistingProperties()
{
/* You can change max lengths for properties of the
* entities defined in the modules used by your application.
*
* Example: Change user and role name max lengths
IdentityUserConsts.MaxNameLength = 99;
IdentityRoleConsts.MaxNameLength = 99;
* Notice: It is not suggested to change property lengths
* unless you really need it. Go with the standard values wherever possible.
*
* If you are using EF Core, you will need to run the add-migration command after your changes.
*/
}
private static void ConfigureExtraProperties()
{
/* You can configure extra properties for the
* entities defined in the modules used by your application.
*
* This class can be used to define these extra properties
* with a high level, easy to use API.
*
* Example: Add a new property to the user entity of the identity module
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>( //property type: string
"SocialSecurityNumber", //property name
property =>
{
//validation rules
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});
property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = true;
//...other configurations for this property
}
);
});
});
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Module-Entity-Extensions
*/
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/ar.json

@ -0,0 +1,8 @@
{
"culture": "ar",
"texts": {
"Menu:Home": "الرئيسية",
"Menu:Home": "الصفحة الرئيسية",
"LongWelcomeMessage": "مرحبا بكم في التطبيق. هذا مشروع بدء تشغيل يعتمد على إطار عمل ABP. لمزيد من المعلومات ، يرجى زيارة abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/cs.json

@ -0,0 +1,8 @@
{
"culture": "cs",
"texts": {
"Menu:Home": "Úvod",
"Welcome": "Vítejte",
"LongWelcomeMessage": "Vítejte v aplikaci. Toto je startovací projekt založený na ABP frameworku. Pro více informací, navštivte abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/de.json

@ -0,0 +1,8 @@
{
"culture": "de",
"texts": {
"Menu:Home": "Home",
"Welcome": "Willkommen",
"LongWelcomeMessage": "Willkommen bei der Anwendung. Dies ist ein Startup-Projekt, das auf dem ABP-Framework basiert. Weitere Informationen finden Sie unter abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/en-GB.json

@ -0,0 +1,8 @@
{
"culture": "en-GB",
"texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/en.json

@ -0,0 +1,8 @@
{
"culture": "en",
"texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/es.json

@ -0,0 +1,8 @@
{
"culture": "es",
"texts": {
"Menu:Home": "Inicio",
"Welcome": "Bienvenido",
"LongWelcomeMessage": "Bienvenido a la aplicación, este es un proyecto base basado en el framework ABP. Para más información, visita abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/fi.json

@ -0,0 +1,8 @@
{
"culture": "fi",
"texts": {
"Menu:Home": "Koti",
"Welcome": "Tervetuloa",
"LongWelcomeMessage": "Tervetuloa sovellukseen. Tämä on ABP-kehykseen perustuva käynnistysprojekti. Lisätietoja on osoitteessa abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/fr.json

@ -0,0 +1,8 @@
{
"culture": "fr",
"texts": {
"Menu:Home": "Accueil",
"Welcome": "Bienvenue",
"LongWelcomeMessage": "Bienvenue dans l'application. Il s'agit d'un projet de démarrage basé sur le framework ABP. Pour plus d'informations, visitez abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hi.json

@ -0,0 +1,8 @@
{
"culture": "hi",
"texts": {
"Menu:Home": "घर",
"Welcome": "स्वागत हे",
"LongWelcomeMessage": "आवेदन करने के लिए आपका स्वागत है। यह एबीपी ढांचे पर आधारित एक स्टार्टअप परियोजना है। अधिक जानकारी के लिए, abp.io पर जाएं।"
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hr.json

@ -0,0 +1,8 @@
{
"culture": "hr",
"texts": {
"Menu:Home": "Početna",
"Welcome": "Dobrodošli",
"LongWelcomeMessage": "Dobrodošli u aplikaciju. Ovo je startup projekt temeljen na ABP framework-u. Za više informacija posjetite abp.io."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/hu.json

@ -0,0 +1,8 @@
{
"culture": "hu",
"texts": {
"Menu:Home": "Kezdőlap",
"Welcome": "Üdvözlöm",
"LongWelcomeMessage": "Üdvözöljük az alkalmazásban. Ez egy ABP keretrendszeren alapuló startup projekt. További információkért látogasson el az abp.io oldalra."
}
}

8
docs/en/Community-Articles/2023-07-09-Cascading-Option-Loading-With-Extensions/Volo.BookStore/aspnet-core/src/Volo.BookStore.Domain.Shared/Localization/BookStore/is.json

@ -0,0 +1,8 @@
{
"culture": "is",
"texts": {
"Menu:Home": "Heim",
"Welcome": "Velkomin",
"LongWelcomeMessage": "Verið velkomin í forritið. Þetta er startup verkefni sem byggir á ABP. Nánari upplýsingar er að finna á abp.io."
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save