diff --git a/docs/en/low-code/fluent-api.md b/docs/en/low-code/fluent-api.md index de24871c3c..1d122963c1 100644 --- a/docs/en/low-code/fluent-api.md +++ b/docs/en/low-code/fluent-api.md @@ -16,7 +16,7 @@ C# Attributes and the Fluent API are the **recommended way** to define dynamic e ````csharp [DynamicEntity] [DynamicEntityUI(PageTitle = "Products")] -public class Product : DynamicEntityBase +public class Product { [DynamicPropertyUnique] public string Name { get; set; } @@ -30,7 +30,18 @@ public class Product : DynamicEntityBase } ```` -### Step 2: Add Migration and Run +### Step 2: Register the Assembly + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + AbpDynamicEntityConfig.SourceAssemblies.Add( + new DynamicEntityAssemblyInfo(typeof(YourDomainModule).Assembly) + ); +} +```` + +### Step 3: Add Migration and Run ```bash dotnet ef migrations add Added_Product @@ -39,12 +50,12 @@ dotnet ef database update You now have a complete Product management page with data grid, create/edit modals, search, sorting, and pagination. -### Step 3: Add Relationships +### Step 4: Add Relationships ````csharp [DynamicEntity] [DynamicEntityUI(PageTitle = "Orders")] -public class Order : DynamicEntityBase +public class Order { [DynamicForeignKey("MyApp.Customers.Customer", "Name", ForeignAccess.Edit)] public Guid CustomerId { get; set; } @@ -54,7 +65,7 @@ public class Order : DynamicEntityBase } [DynamicEntity(Parent = "MyApp.Orders.Order")] -public class OrderLine : DynamicEntityBase +public class OrderLine { [DynamicForeignKey("MyApp.Products.Product", "Name")] public Guid ProductId { get; set; } @@ -86,7 +97,7 @@ Marks a class as a dynamic entity. The entity name is derived from the class nam ````csharp [DynamicEntity] -public class Product : DynamicEntityBase +public class Product { public string Name { get; set; } public decimal Price { get; set; } @@ -97,7 +108,7 @@ Use the `Parent` property for parent-child (master-detail) relationships: ````csharp [DynamicEntity(Parent = "MyApp.Orders.Order")] -public class OrderLine : DynamicEntityBase +public class OrderLine { public Guid ProductId { get; set; } public int Quantity { get; set; } @@ -111,7 +122,7 @@ Configures entity-level UI. Entities with `PageTitle` get a menu item and a dedi ````csharp [DynamicEntity] [DynamicEntityUI(PageTitle = "Product Management")] -public class Product : DynamicEntityBase +public class Product { // ... } @@ -157,6 +168,10 @@ public string RegistrationNumber { get; set; } | `EditingFormAvailability` | enum | `Available` | Visibility on edit form | | `QuickLookOrder` | int | `-2` | Order in quick-look panel | +The quick-look panel shows a summary of the selected record: + +![Quick-look panel showing entity details](images/quick-look.png) + ### `[DynamicPropertyServerOnly]` Hides a property from API clients entirely. It is stored in the database but never returned to the client: @@ -200,7 +215,7 @@ Defines JavaScript interceptors on a class for CRUD lifecycle hooks: InterceptorType.Post, "context.log('Deleted: ' + context.commandArgs.entityId);" )] -public class Organization : DynamicEntityBase +public class Organization { public string Name { get; set; } } @@ -230,7 +245,7 @@ Reference in an entity: ````csharp [DynamicEntity] [DynamicEntityUI(PageTitle = "Organizations")] -public class Organization : DynamicEntityBase +public class Organization { public string Name { get; set; } public OrganizationType OrganizationType { get; set; } @@ -261,38 +276,28 @@ The Fluent API has the **highest priority** in the configuration system. Use `Ab ### Basic Usage -Configure in your Low-Code Initializer (e.g. `MyAppLowCodeInitializer`): +Configure in your Domain module's `ConfigureServices`: ````csharp -public static class MyAppLowCodeInitializer +public override void ConfigureServices(ServiceConfigurationContext context) { - private static readonly AsyncOneTimeRunner Runner = new(); - - public static async Task InitializeAsync() - { - await Runner.RunAsync(async () => + AbpDynamicEntityConfig.EntityConfigurations.Configure( + "MyApp.Products.Product", + entity => { - AbpDynamicEntityConfig.EntityConfigurations.Configure( - "MyApp.Products.Product", - entity => - { - entity.DefaultDisplayPropertyName = "Name"; - - var priceProperty = entity.AddOrGetProperty("Price"); - priceProperty.AsRequired(); - priceProperty.UI = new EntityPropertyUIDescriptor - { - DisplayName = "Unit Price", - CreationFormAvailability = EntityPropertyUIFormAvailability.Available - }; - - entity.AddOrGetProperty("InternalNotes").AsServerOnly(); - } - ); + entity.DefaultDisplayPropertyName = "Name"; - await DynamicModelManager.Instance.InitializeAsync(); - }); - } + var priceProperty = entity.AddOrGetProperty("Price"); + priceProperty.AsRequired(); + priceProperty.UI = new EntityPropertyUIDescriptor + { + DisplayName = "Unit Price", + CreationFormAvailability = EntityPropertyUIFormAvailability.Available + }; + + entity.AddOrGetProperty("InternalNotes").AsServerOnly(); + } + ); } ```` @@ -434,7 +439,7 @@ public enum OrderStatus // Customer entity [DynamicEntity] [DynamicEntityUI(PageTitle = "Customers")] -public class Customer : DynamicEntityBase +public class Customer { [DynamicPropertyUnique] public string Name { get; set; } @@ -452,7 +457,7 @@ public class Customer : DynamicEntityBase // Product entity [DynamicEntity] [DynamicEntityUI(PageTitle = "Products")] -public class Product : DynamicEntityBase +public class Product { [DynamicPropertyUnique] public string Name { get; set; } @@ -473,7 +478,7 @@ public class Product : DynamicEntityBase } }" )] -public class Order : DynamicEntityBase +public class Order { [DynamicForeignKey("MyApp.Customers.Customer", "Name", ForeignAccess.Edit)] public Guid CustomerId { get; set; } @@ -484,7 +489,7 @@ public class Order : DynamicEntityBase } [DynamicEntity(Parent = "MyApp.Orders.Order")] -public class OrderLine : DynamicEntityBase +public class OrderLine { [DynamicForeignKey("MyApp.Products.Product", "Name")] public Guid ProductId { get; set; } diff --git a/docs/en/low-code/foreign-access.md b/docs/en/low-code/foreign-access.md index aa9383f545..34503772f3 100644 --- a/docs/en/low-code/foreign-access.md +++ b/docs/en/low-code/foreign-access.md @@ -108,6 +108,8 @@ Set the `access` field on a foreign key property: When foreign access is configured between two **dynamic entities**: +![Actions menu showing foreign access items (Order, Visited Country, etc.)](images/actions-menu.png) + ### `ForeignAccess.View` An **action menu item** appears on the target entity's data grid row (e.g., a "Visited Countries" item on the Country row). Clicking it opens a read-only modal showing related records. @@ -116,6 +118,8 @@ An **action menu item** appears on the target entity's data grid row (e.g., a "V An **action menu item** appears on the target entity's data grid row (e.g., an "Orders" item on the Customer row). Clicking it opens a fully functional CRUD modal where users can create, edit, and delete related records. +![Foreign access modal with full CRUD capabilities](images/foreign-access-modal.png) + ### `ForeignAccess.None` No action menu item is added. The foreign key exists only for data integrity and lookup display. diff --git a/docs/en/low-code/images/actions-menu.png b/docs/en/low-code/images/actions-menu.png new file mode 100644 index 0000000000..b733160e8d Binary files /dev/null and b/docs/en/low-code/images/actions-menu.png differ diff --git a/docs/en/low-code/images/create-modal.png b/docs/en/low-code/images/create-modal.png new file mode 100644 index 0000000000..dba8e40124 Binary files /dev/null and b/docs/en/low-code/images/create-modal.png differ diff --git a/docs/en/low-code/images/data-grid.png b/docs/en/low-code/images/data-grid.png new file mode 100644 index 0000000000..9650a9cb56 Binary files /dev/null and b/docs/en/low-code/images/data-grid.png differ diff --git a/docs/en/low-code/images/foreign-access-modal.png b/docs/en/low-code/images/foreign-access-modal.png new file mode 100644 index 0000000000..fd004fc8a2 Binary files /dev/null and b/docs/en/low-code/images/foreign-access-modal.png differ diff --git a/docs/en/low-code/images/interceptor-error.png b/docs/en/low-code/images/interceptor-error.png new file mode 100644 index 0000000000..cbbe294188 Binary files /dev/null and b/docs/en/low-code/images/interceptor-error.png differ diff --git a/docs/en/low-code/images/menu-items.png b/docs/en/low-code/images/menu-items.png new file mode 100644 index 0000000000..96ae3f7f08 Binary files /dev/null and b/docs/en/low-code/images/menu-items.png differ diff --git a/docs/en/low-code/images/quick-look.png b/docs/en/low-code/images/quick-look.png new file mode 100644 index 0000000000..9e4c33b3f5 Binary files /dev/null and b/docs/en/low-code/images/quick-look.png differ diff --git a/docs/en/low-code/index.md b/docs/en/low-code/index.md index 9d1fcd91c3..5d493a7d9d 100644 --- a/docs/en/low-code/index.md +++ b/docs/en/low-code/index.md @@ -17,6 +17,8 @@ The ABP Low-Code System allows you to define entities using C# attributes or Flu No need to write DTOs, application services, repositories, or UI pages manually. +![Auto-generated menu items in the sidebar](images/menu-items.png) + ## Why Low-Code? Traditionally, adding a new entity with full CRUD functionality to an ABP application requires: @@ -51,6 +53,10 @@ public class Product : DynamicEntityBase Run `dotnet ef migrations add Added_Product` and start your application. You get a complete Product management page with search, filtering, sorting, pagination, create/edit forms, and foreign key dropdown — all auto-generated. +![Auto-generated data grid with search, filters, and actions](images/data-grid.png) + +![Auto-generated create/edit modal with form fields and foreign key lookups](images/create-modal.png) + ## Getting Started ### 1. Create a Low-Code Initializer diff --git a/docs/en/low-code/interceptors.md b/docs/en/low-code/interceptors.md index 78d8bd0837..bfdb5db11e 100644 --- a/docs/en/low-code/interceptors.md +++ b/docs/en/low-code/interceptors.md @@ -136,6 +136,8 @@ Set this variable to a string to **abort** the operation and return an error: globalError = 'Cannot delete this entity!'; ``` +![Interceptor validation error displayed in the UI](images/interceptor-error.png) + ## Examples ### Pre-Create: Validation