|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 12 MiB |
|
After Width: | Height: | Size: 403 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 104 KiB |
@ -0,0 +1,225 @@ |
|||
# Introducing ABP Low-Code: Build Real ABP Apps in Minutes |
|||
|
|||
**Create runtime-managed pages, generated React screens, code-first C# entities, and Script API extensions without leaving the ABP application model.** |
|||
|
|||
 |
|||
|
|||
> **Want to try the same path?** Start from ABP Studio, enable the Low-Code runtime and designer, define pages in the Admin Console, and see them resolve inside the running ABP app. |
|||
|
|||
--- |
|||
|
|||
## ABP Low-Code at a glance |
|||
|
|||
| Runtime authoring | Generated screens | ABP-native extensibility | One application model | |
|||
| :---: | :---: | :---: | :---: | |
|||
| Define and update pages in the Admin Console | Grid, Form, Calendar, Kanban, Gallery, Dashboard | Code-first entities, Script API actions, and C# query paths | Runtime metadata, generated UI, and application code stay together | |
|||
|
|||
--- |
|||
|
|||
## Built into the ABP Platform |
|||
|
|||
Low-code is most useful when speed does not create a separate stack to maintain later. |
|||
|
|||
That is where many low-code products start to strain. They move quickly at the beginning, then force a second implementation track when the app needs permissions, auditability, custom logic, or tighter integration with existing application code. |
|||
|
|||
ABP Low-Code takes a different path. It runs **inside the ABP Platform**, so runtime-managed pages are part of an application foundation that already includes identity, permissions, audit logging, APIs, and code-level extensibility. |
|||
|
|||
--- |
|||
|
|||
## Edit at runtime. See it in the app. |
|||
|
|||
In the Low-Code Designer, you update a runtime-managed page. A few seconds later, the same application surface is visible in the live app. No rebuild loop. No parallel front-end implementation. No "we will wire it later" gap between authoring and runtime. |
|||
|
|||
ABP Low-Code shortens the cycle from model change to running screen while keeping the output grounded in the same ABP application. |
|||
|
|||
 |
|||
|
|||
> **What this shows:** authoring and runtime are connected. Pages are defined in the designer and resolved in the running application. |
|||
|
|||
--- |
|||
|
|||
## CRUD is table stakes |
|||
|
|||
If low-code only saves you from drawing a table and a form, it is not enough. Business applications need richer operational surfaces. |
|||
|
|||
In the generated app, the `Events` screen ships with search, actions, filters, and form-driven editing. The form structure already understands tabs, relations, validation, and business-shaped input instead of leaving you with a blank shell to finish by hand. |
|||
|
|||
 |
|||
|
|||
The point is not just generated CRUD. It is generated CRUD that already looks like the operational screens teams maintain in real applications. |
|||
|
|||
--- |
|||
|
|||
## One model, multiple operational screens |
|||
|
|||
Business users do not think in one view. Operators want a calendar for scheduling, a kanban board for workflow, a grid for bulk operations, a gallery when media matters, and a dashboard when they need the state of the business at a glance. |
|||
|
|||
ABP Low-Code keeps those surfaces attached to the same underlying model. The same `Session` model can appear as a **calendar** for planning and a **kanban pipeline** for operational flow. The same generated app can also include a **speaker gallery** and an **overview dashboard** for metrics. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
This is where ABP Low-Code starts to feel less like a form generator and more like a runtime application layer: one model, many working screens, no second implementation track for each view type. |
|||
|
|||
--- |
|||
|
|||
## When low-code needs code |
|||
|
|||
The real differentiator is not that ABP Low-Code can go fast. It is that **speed does not require isolation from the application foundation**. |
|||
|
|||
When generated CRUD is not enough, you extend the same app instead of throwing the low-code layer away. |
|||
|
|||
ABP Low-Code exposes a server-side **Script API** inside the same application model. That scripting surface can back: |
|||
|
|||
- **Custom endpoints** when the UI needs an API-shaped response. |
|||
- **Interceptors** when create or update commands need validation or mutation. |
|||
- **Event handlers** when logic should react to runtime events. |
|||
- **Background jobs** when work should continue asynchronously. |
|||
- **Background workers** when operational logic should run on a schedule. |
|||
|
|||
In this article, the visible proof happens to be `GET /api/custom/eventflow/highlights`. The GIF shows an endpoint because it is the easiest proof surface to read. But the broader point is that endpoints are only one consumer of the same low-code scripting layer. |
|||
|
|||
That hybrid model matters in both directions: |
|||
|
|||
- **Code-first ABP entities can be surfaced in low-code flows and runtime pages.** |
|||
- **Low-code-managed data and screens stay reachable from Script API actions, application services, repository queries, and custom endpoints.** |
|||
- **Teams do not lose architectural control just because they gained a faster authoring layer.** |
|||
|
|||
 |
|||
|
|||
The actual capability is the shared ABP application model behind it: script when runtime logic is enough, C# when typed application services and repository queries are the better fit. |
|||
|
|||
This is the difference between "low-code as a shortcut" and "low-code as part of your application platform." |
|||
|
|||
--- |
|||
|
|||
## From code-first entity to generated page |
|||
|
|||
The first direction is code-first to low-code. A **code-first** `SponsorActivation` entity checked into the ASP.NET Core project can still become a working runtime page without forking into a separate low-code-only model. |
|||
|
|||
The code-first entity carries the same metadata that ABP Low-Code uses to generate the page: |
|||
|
|||
```csharp |
|||
[DynamicEntity(DefaultDisplayPropertyName = nameof(CompanyName))] |
|||
[DynamicEntityUI("Sponsor Activations")] |
|||
[DynamicEntityAttachments("application/pdf", "image/*", MaxFileCount = 4)] |
|||
public class SponsorActivation : DynamicEntityBase |
|||
{ |
|||
[Required] |
|||
[DynamicPropertyUI(DisplayName = "Sponsor")] |
|||
public string CompanyName { get; private set; } |
|||
|
|||
[Required] |
|||
[EmailAddress] |
|||
[DynamicPropertyUI(DisplayName = "Contact Email")] |
|||
public string ContactEmail { get; private set; } |
|||
|
|||
public SponsorActivationStatus Status { get; set; } |
|||
|
|||
[DynamicForeignKey("EventFlow.Events.Event", "Title")] |
|||
public Guid? EventId { get; set; } |
|||
|
|||
[DynamicForeignKey("Volo.Abp.Identity.IdentityUser", nameof(IdentityUser.UserName), ForeignAccess.View)] |
|||
public Guid? OwnerUserId { get; set; } |
|||
|
|||
[DynamicPropertyType(EntityPropertyType.Money)] |
|||
public decimal ActivationBudget { get; set; } |
|||
|
|||
[DynamicPropertyImageOptions("image/png", "image/jpeg")] |
|||
public string? BrandLogo { get; set; } |
|||
|
|||
[DynamicPropertyFileOptions("application/pdf", ".pptx", ".docx")] |
|||
public string? ActivationBrief { get; set; } |
|||
} |
|||
``` |
|||
|
|||
That class lives as normal C# source, gets migrated like the rest of the application, and is seeded with real records so the runtime page does not open as an empty shell. |
|||
|
|||
Inside the designer, selecting the `SponsorActivation` entity auto-generates the page identity, binds the grid to the entity, and lands on a real runtime route at `/dynamic/sponsor-activation`. The generated surface includes sponsor, email, event lookup, owner lookup, budget, image, and file fields directly from the C# model. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
That is the distinction that matters: code-first ABP entities can move through low-code without becoming throwaway artifacts, and low-code-generated surfaces remain part of the same application story. |
|||
|
|||
--- |
|||
|
|||
## Low-code data stays reachable from C# |
|||
|
|||
The bridge also works in the other direction. A normal ABP application service can query a low-code model through `IRepository<DynamicEntity, Guid>`, apply real filters, and combine that result with code-first aggregates. |
|||
|
|||
The service behind the endpoint in the previous section looks like this: |
|||
|
|||
```csharp |
|||
public async Task<EventFlowLowCodeProofDto> GetHybridSummaryAsync() |
|||
{ |
|||
var liveSessionQuery = (await _dynamicEntityRepository |
|||
.SetEntityName("EventFlow.Events.Session") |
|||
.GetQueryableAsync()) |
|||
.Where("int(it[\"Status\"]) == @0", 2); |
|||
|
|||
var publicSessionQuery = liveSessionQuery |
|||
.Where("bool(it[\"IsPublic\"]) == @0", true); |
|||
|
|||
var sponsorQuery = (await _sponsorActivationRepository.GetQueryableAsync()) |
|||
.Where(activation => |
|||
activation.Status == SponsorActivationStatus.Approved || |
|||
activation.Status == SponsorActivationStatus.Live); |
|||
|
|||
var liveSessionCount = await AsyncExecuter.CountAsync(liveSessionQuery); |
|||
var publicSessionCount = await AsyncExecuter.CountAsync(publicSessionQuery); |
|||
var activeSponsorActivationCount = await AsyncExecuter.CountAsync(sponsorQuery); |
|||
|
|||
return new EventFlowLowCodeProofDto |
|||
{ |
|||
LiveSessionCount = liveSessionCount, |
|||
PublicSessionCount = publicSessionCount, |
|||
ActiveSponsorActivationCount = activeSponsorActivationCount |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
Here, low-code-managed `Session` rows are filtered from C# with real `Where(...)` clauses, then combined with the typed `SponsorActivation` repository. The endpoint and dashboard are just one presentation surface for that shared ABP query path. |
|||
|
|||
That is the ABP difference: low-code data stays reachable from code, and code-first entities stay reachable from low-code. |
|||
|
|||
--- |
|||
|
|||
## Why ABP Low-Code matters |
|||
|
|||
The value is not novelty. It is a faster way to build real business applications without separating speed from the application foundation. |
|||
|
|||
- **Speed without replatforming.** Runtime-managed screens reduce delivery time without moving the team onto a separate application stack. |
|||
- **Governance without friction.** Permissions, identity, auditability, and ABP platform foundations stay part of the story from day one. |
|||
- **Extensibility without rewrite pressure.** When custom behavior shows up, the same application can be extended instead of replacing the low-code output. |
|||
|
|||
That is the core ABP Low-Code promise: faster delivery, still inside the application model you can extend. |
|||
|
|||
--- |
|||
|
|||
## Try it yourself |
|||
|
|||
The public starting point for ABP Low-Code is **ABP Studio**. |
|||
|
|||
 |
|||
|
|||
1. Open **ABP Studio** and create a new solution. |
|||
2. In the solution wizard, enable **Include Low-Code runtime and designer**. |
|||
3. Complete the wizard, then run the generated backend and React UI from the solution. |
|||
4. Sign in with the administrator account created for that solution. |
|||
5. Open **Admin Console** to define runtime-managed entities, forms, pages, permissions, endpoints, and script actions. |
|||
6. Switch to the application side to see those changes resolve live in the running app. |
|||
|
|||
--- |
|||
|
|||
## Further reading |
|||
|
|||
- [ABP Low-Code Designer Documentation](https://abp.io/docs/latest/low-code/designer) |
|||
- [ABP Low-Code Configuration & Fluent API](https://abp.io/docs/latest/low-code/fluent-api) |
|||
- [ABP Low-Code Scripting API](https://abp.io/docs/latest/low-code/scripting-api) |
|||
- [ABP Low-Code Script Actions](https://abp.io/docs/latest/low-code/script-actions) |
|||
- [ABP Low-Code Interceptors](https://abp.io/docs/latest/low-code/interceptors) |
|||
- [ABP Studio Documentation](https://abp.io/docs/latest/studio) |
|||
- [Get Started with ABP: Creating a Layered Web Application](https://abp.io/docs/latest/get-started/layered-web-application) |
|||
@ -0,0 +1 @@ |
|||
Discover how ABP Low-Code blends runtime page building with code-first entities, C# queries, and extensible application logic. |
|||
@ -0,0 +1,116 @@ |
|||
```json |
|||
//[doc-seo] |
|||
{ |
|||
"Description": "Upgrade your ABP solutions to Angular version 22.0.x" |
|||
} |
|||
``` |
|||
|
|||
# Release Notes: Angular 22 and TypeScript 6 Upgrade |
|||
|
|||
## Overview |
|||
|
|||
This release updates ABP Angular UI applications to: |
|||
|
|||
* Angular `22.x` |
|||
* TypeScript `6.x` |
|||
|
|||
This upgrade aligns ABP projects with the latest Angular ecosystem and provides access to the newest framework improvements while ensuring long-term maintainability and support. |
|||
|
|||
## What's Changed |
|||
|
|||
### 1. Frontend Stack Upgrades |
|||
|
|||
The core frontend stack has been updated: |
|||
|
|||
* `@angular/*` packages have been upgraded to version 22 |
|||
* `typescript` has been upgraded to version 6 |
|||
* ABP and ABP Commercial npm packages must be upgraded to the corresponding ABP release line (version 10.6) |
|||
|
|||
### 2. Change Detection Behavior |
|||
|
|||
Angular 22 introduces updated change detection behavior. |
|||
|
|||
* Components without explicit change detection configuration now follow OnPush-style behavior by default |
|||
* Some existing pages may no longer update automatically after asynchronous operations |
|||
* UI state should be managed using Angular Signals or the `async` pipe where appropriate |
|||
|
|||
### 3. Stricter Type and Template Checks |
|||
|
|||
Angular 22 and TypeScript 6 introduce additional compile-time validations. |
|||
|
|||
* More template and type-related issues may be reported during builds |
|||
* Existing assumptions around nullable values and optional properties may require additional guards or type refinements |
|||
* Applications with strict template checking enabled may require code updates |
|||
|
|||
### 4. Upload Progress Handling |
|||
|
|||
Applications that rely on file upload progress events may require additional HTTP client configuration. |
|||
|
|||
* Browser-side HTTP configuration may need `withXhr()` enabled to ensure upload progress events are emitted correctly |
|||
|
|||
### 5. Chart Update Behavior |
|||
|
|||
Chart components may require additional updates when used with asynchronous data sources. |
|||
|
|||
* Under OnPush-style change detection, chart updates may not be detected automatically |
|||
* Consider using Signals for chart data bindings |
|||
* Calling `reinit()` after asynchronous data updates may be necessary in some scenarios |
|||
|
|||
## Required Actions |
|||
|
|||
### 1. Upgrade Related Packages Together |
|||
|
|||
Keep Angular, TypeScript, ABP, and ABP Commercial packages on compatible versions. |
|||
|
|||
To use Angular 22: |
|||
|
|||
* Angular: `22.x` |
|||
* TypeScript: `6.x` |
|||
* ABP Framework: `10.6.x` |
|||
|
|||
### 2. Review UI State Management |
|||
|
|||
Review pages that depend on asynchronous state updates, including: |
|||
|
|||
* List and table data |
|||
* Loading and busy indicators |
|||
* Modal dialog state |
|||
* Dashboard and chart data |
|||
|
|||
Consider migrating these scenarios to Angular Signals or the `async` pipe. |
|||
|
|||
### 3. Apply a Temporary TypeScript Compatibility Setting (If Needed) |
|||
|
|||
If your project uses `downlevelIteration`, you may temporarily add the following configuration: |
|||
|
|||
```json |
|||
{ |
|||
"ignoreDeprecations": "6.0" |
|||
} |
|||
``` |
|||
|
|||
This can help ease the migration process while addressing TypeScript 6 deprecation warnings. |
|||
|
|||
### 4. Perform Regression Testing |
|||
|
|||
We recommend validating all critical application flows after upgrading, including: |
|||
|
|||
* Authentication and account management pages |
|||
* CRUD list and detail pages |
|||
* Permission and feature management dialogs |
|||
* File upload workflows |
|||
* Dashboard and chart components |
|||
|
|||
## Areas to Validate Carefully |
|||
|
|||
Pay particular attention to the following scenarios: |
|||
|
|||
* Busy or loading indicators not updating correctly |
|||
* Modal open/close state inconsistencies |
|||
* List pages not refreshing after asynchronous operations |
|||
* Upload progress events not being emitted |
|||
* Charts rendering without data after API responses |
|||
|
|||
## References |
|||
|
|||
* Detailed migration guide: [Upgrade ABP to 10.6](../../../../release-info/migration-guides/abp-10-6-angular-22.md) |
|||
@ -0,0 +1,193 @@ |
|||
```json |
|||
//[doc-seo] |
|||
{ |
|||
"Description": "Upgrade your ABP solutions to Angular version 22.0.x" |
|||
} |
|||
``` |
|||
|
|||
# Angular 22 and ABP 10.6 Upgrade Guide |
|||
|
|||
This guide explains how to upgrade ABP Angular applications to **Angular 22** and **TypeScript 6**. |
|||
|
|||
## 1. Target Versions |
|||
|
|||
Update all frontend dependencies together to maintain compatibility: |
|||
|
|||
- `@angular/*` → `~22.0.0` |
|||
- `typescript` → `~6.0.0` |
|||
- `@abp/*` → corresponding ABP version (10.6) |
|||
- `@volo/*`, `@volosoft/*` (if applicable) → corresponding ABP version (10.6) |
|||
- `angular-oauth2-oidc` (if applicable) → `~22.0.0` |
|||
|
|||
Avoid mixing Angular 21 and Angular 22 packages within the same workspace. |
|||
|
|||
## 2. Prerequisites |
|||
|
|||
Before starting the upgrade: |
|||
|
|||
1. Use a Node.js version supported by Angular 22. |
|||
2. Ensure your backend ABP version is compatible with the frontend package versions you plan to install. |
|||
3. Commit or back up your current project state. |
|||
|
|||
## 3. Upgrade Process |
|||
|
|||
1. Update package versions in `package.json`. |
|||
2. Run the Angular or Nx migration commands applicable to your project. |
|||
3. Remove existing installation artifacts: |
|||
- Delete `node_modules` |
|||
- Delete the lock file (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) |
|||
|
|||
4. Reinstall all dependencies. |
|||
5. Build the application and resolve any compilation or template errors. |
|||
|
|||
## 4. Required Changes |
|||
|
|||
### 4.1 TypeScript 6 Deprecation Handling |
|||
|
|||
Projects that still use `downlevelIteration: true` may encounter TypeScript 6 deprecation diagnostics. |
|||
|
|||
Add the following temporary setting to your root `tsconfig` file (and library production configurations if required): |
|||
|
|||
```json |
|||
{ |
|||
"compilerOptions": { |
|||
"downlevelIteration": true, |
|||
"ignoreDeprecations": "6.0" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
As a long-term solution, remove `downlevelIteration` when your target runtime environment no longer requires it. |
|||
|
|||
### 4.2 Updated Change Detection Behavior |
|||
|
|||
Angular 22 introduces updated change detection behavior for components that do not explicitly configure a change detection strategy. |
|||
|
|||
Common symptoms include: |
|||
|
|||
- Loading indicators not updating |
|||
- Modal busy states not clearing |
|||
- Lists or charts not refreshing after asynchronous operations |
|||
|
|||
Recommended approaches: |
|||
|
|||
1. Use `signal()` for component state. |
|||
2. Use `toSignal()` when consuming observable streams. |
|||
3. Use the `async` pipe for observable-based UI state. |
|||
4. Use `ChangeDetectionStrategy.Eager` only as a temporary compatibility measure for legacy components. |
|||
|
|||
### 4.3 ABP List Pages (`ListService`) |
|||
|
|||
When working with `ListService`, prefer converting observable results to signals instead of manually subscribing. |
|||
|
|||
```typescript |
|||
readonly data = toSignal( |
|||
this.list.hookToQuery(query => this.service.getList(query)), |
|||
{ initialValue: { items: [], totalCount: 0 } }, |
|||
); |
|||
``` |
|||
|
|||
Update template bindings accordingly: |
|||
|
|||
- `data.items` → `data().items` |
|||
- `data.totalCount` → `data().totalCount` |
|||
|
|||
### 4.4 Modals and Loading States |
|||
|
|||
For components such as `abp-modal`, `abp-button`, and permission or feature management dialogs, maintain state using signals. |
|||
|
|||
```typescript |
|||
readonly isModalVisible = signal(false); |
|||
readonly modalBusy = signal(false); |
|||
``` |
|||
|
|||
```html |
|||
<abp-button [loading]="modalBusy()" /> |
|||
<abp-modal |
|||
[visible]="isModalVisible()" |
|||
(visibleChange)="isModalVisible.set($event)" |
|||
[busy]="modalBusy()" |
|||
/> |
|||
``` |
|||
|
|||
If you use `*abpReplaceableTemplate`, pass signal values through `inputs.value` and update state through the corresponding event callbacks. |
|||
|
|||
### 4.5 Template Type Checking |
|||
|
|||
Angular 22 enables `strictTemplates` by default. |
|||
|
|||
Resolve template typing issues where possible, or temporarily disable strict template checking: |
|||
|
|||
```json |
|||
{ |
|||
"angularCompilerOptions": { |
|||
"strictTemplates": false |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Common adjustments include: |
|||
|
|||
- Updating optional chaining (`?.`) and null coalescing (`??`) usage |
|||
- Guarding optional form references before binding |
|||
- Resolving duplicate input or output bindings |
|||
|
|||
### 4.6 Upload Progress Events |
|||
|
|||
Applications that rely on upload progress events should include the XHR backend in browser-side HTTP configuration: |
|||
|
|||
```typescript |
|||
provideHttpClient(withFetch(), withXhr()); |
|||
``` |
|||
|
|||
Do not enable the XHR backend in server-side rendering (SSR) bootstrap code. |
|||
|
|||
### 4.7 Chart Components (`abp-chart`) |
|||
|
|||
If charts do not update after asynchronous data loading: |
|||
|
|||
- Store chart data in a signal |
|||
- Bind chart inputs using signal values (for example, `[data]="chartData()"`) |
|||
- Call `reinit()` after assigning new data rather than relying solely on `refresh()` |
|||
|
|||
## 5. Custom or Forked UI Modules |
|||
|
|||
If your project contains customized copies of ABP modules such as Identity, Tenant Management, Account, or CMS Kit: |
|||
|
|||
1. Compare your implementation with the updated package versions. |
|||
2. Apply the recommended signal-based state management patterns. |
|||
3. Re-test CRUD pages, permission dialogs, feature dialogs, and account-related workflows. |
|||
|
|||
## 6. Validation Checklist |
|||
|
|||
After completing the upgrade, verify that: |
|||
|
|||
- Dependencies are installed correctly without duplicate Angular versions |
|||
- The application builds successfully |
|||
- Unit tests pass (if applicable) |
|||
- Login, registration, and password recovery workflows function correctly |
|||
- CRUD list pages refresh as expected |
|||
- Modal loading and busy states behave correctly |
|||
- Permission and feature dialogs open and close correctly |
|||
- Upload progress events work as expected (if applicable) |
|||
- Dashboard charts render correctly after data is loaded |
|||
|
|||
## 7. Troubleshooting |
|||
|
|||
| Symptom | Likely Cause | Resolution | |
|||
| ----------------------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------- | |
|||
| Form type conflicts (`AbstractControl`, etc.) | Multiple Angular versions installed | Align package versions and perform a clean reinstall | |
|||
| TypeScript deprecation errors related to `downlevelIteration` | TypeScript 6 diagnostics | Add `ignoreDeprecations: "6.0"` temporarily | |
|||
| Errors involving optional configuration or environment properties | Stricter type checking | Add null checks and optional chaining where appropriate | |
|||
| DTO or library compilation issues | Type incompatibilities in DTO definitions | Prefer interfaces and optional properties where appropriate | |
|||
| Upload progress events are not emitted | Missing XHR backend configuration | Add `withXhr()` to browser-side HTTP configuration | |
|||
| Charts remain empty after data loads | State changes are not being detected | Use signals and call `reinit()` after updating chart data | |
|||
|
|||
## 8. Summary |
|||
|
|||
When upgrading to Angular 22, focus on the following areas: |
|||
|
|||
1. Upgrade Angular, TypeScript, ABP, and commercial packages together. |
|||
2. Update UI state management to use signals, `toSignal()`, or the `async` pipe where appropriate. |
|||
3. Resolve TypeScript 6 and template type-checking issues. |
|||
4. Validate critical application workflows, including modals, list pages, uploads, and chart components. |
|||
@ -0,0 +1 @@ |
|||
{} |
|||
@ -0,0 +1 @@ |
|||
{} |
|||
@ -0,0 +1 @@ |
|||
{} |
|||
@ -1,55 +1,81 @@ |
|||
@if (actionList.length > 1) { |
|||
<div ngbDropdown container="body" class="d-inline-block"> |
|||
<button class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" ngbDropdownToggle> |
|||
<i [class]="icon()" [class.me-1]="icon()"></i>{{ text() | abpLocalization }} |
|||
</button> |
|||
<div ngbDropdownMenu> |
|||
@for (action of actionList; track action.text) { |
|||
<ng-container [ngTemplateOutlet]="dropDownBtnItemTmp" [ngTemplateOutletContext]="{ $implicit: action }"> |
|||
</ng-container> |
|||
} |
|||
<div ngbDropdown container="body" class="d-inline-block"> |
|||
<button |
|||
class="btn btn-primary btn-sm dropdown-toggle" |
|||
data-toggle="dropdown" |
|||
aria-haspopup="true" |
|||
ngbDropdownToggle |
|||
> |
|||
<i [class]="icon()" [class.me-1]="icon()"></i>{{ text() | abpLocalization }} |
|||
</button> |
|||
<div ngbDropdownMenu> |
|||
@for (action of actionList; track $index) { |
|||
<ng-container |
|||
[ngTemplateOutlet]="dropDownBtnItemTmp" |
|||
[ngTemplateOutletContext]="{ $implicit: action }" |
|||
/> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@if (actionList.length === 1) { |
|||
<ng-container [ngTemplateOutlet]="btnTmp" |
|||
[ngTemplateOutletContext]="{ $implicit: actionList.get(0).value }"></ng-container> |
|||
<ng-container |
|||
[ngTemplateOutlet]="btnTmp" |
|||
[ngTemplateOutletContext]="{ $implicit: actionList.get(0).value }" |
|||
/> |
|||
} |
|||
|
|||
<ng-template #dropDownBtnItemTmp let-action> |
|||
@if (action.visible(data)) { |
|||
<button ngbDropdownItem *abpPermission="action.permission; runChangeDetection: false" (click)="action.action(data)" |
|||
type="button"> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"></ng-container> |
|||
</button> |
|||
<button |
|||
ngbDropdownItem |
|||
*abpPermission="action.permission; runChangeDetection: false" |
|||
(click)="action.action(data)" |
|||
type="button" |
|||
> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }" /> |
|||
</button> |
|||
} |
|||
</ng-template> |
|||
|
|||
<ng-template #buttonContentTmp let-action> |
|||
<i [class]="action.icon" [class.me-1]="action.icon && !action.showOnlyIcon"></i> |
|||
@if (!action.showOnlyIcon) { |
|||
@if (action.icon) { |
|||
<span>{{ action.text | abpLocalization }}</span> |
|||
} @else { |
|||
<div abpEllipsis>{{ action.text | abpLocalization }}</div> |
|||
} |
|||
@if (action.icon) { |
|||
<span>{{ action.text | abpLocalization }}</span> |
|||
} @else { |
|||
<div abpEllipsis>{{ action.text | abpLocalization }}</div> |
|||
} |
|||
} |
|||
</ng-template> |
|||
|
|||
<ng-template #btnTmp let-action> |
|||
@if (action.visible(data)) { |
|||
@if (action.tooltip) { |
|||
<button *abpPermission="action.permission; runChangeDetection: false" (click)="action.action(data)" type="button" |
|||
[class]="action.btnClass" [style]="action.btnStyle" [ngbTooltip]="action.tooltip.text | abpLocalization" |
|||
[placement]="action.tooltip.placement || 'auto'" triggers="hover" container="body"> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"></ng-container> |
|||
</button> |
|||
} @else { |
|||
<button *abpPermission="action.permission; runChangeDetection: false" (click)="action.action(data)" type="button" |
|||
[class]="action.btnClass" [style]="action.btnStyle"> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"></ng-container> |
|||
</button> |
|||
} |
|||
@if (action.tooltip) { |
|||
<button |
|||
*abpPermission="action.permission; runChangeDetection: false" |
|||
(click)="action.action(data)" |
|||
type="button" |
|||
[class]="action.btnClass" |
|||
[style]="action.btnStyle" |
|||
[ngbTooltip]="action.tooltip.text | abpLocalization" |
|||
[placement]="action.tooltip.placement || 'auto'" |
|||
triggers="hover" |
|||
container="body" |
|||
> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }" /> |
|||
</button> |
|||
} @else { |
|||
<button |
|||
*abpPermission="action.permission; runChangeDetection: false" |
|||
(click)="action.action(data)" |
|||
type="button" |
|||
[class]="action.btnClass" |
|||
[style]="action.btnStyle" |
|||
> |
|||
<ng-container *ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }" /> |
|||
</button> |
|||
} |
|||
} |
|||
</ng-template> |
|||
</ng-template> |
|||
|
|||