Skip to content

Commit ebb4f11

Browse files
authored
Merge branch 'dev' into auto-merge/rel-10-2/4461
2 parents b53eae2 + 0e7cbae commit ebb4f11

895 files changed

Lines changed: 67350 additions & 128297 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
name: abp-angular
3+
description: ABP Angular UI patterns - generate-proxy, ListService, PermissionGuard, abpLocalization pipe, ConfirmationService, ToasterService, ConfigStateService. Use when building or reviewing Angular UI components, routing, or service integration in ABP Angular projects.
4+
---
5+
6+
# ABP Angular UI
7+
8+
> **Docs**: https://abp.io/docs/latest/framework/ui/angular/overview
9+
10+
## Project Structure
11+
```
12+
src/app/
13+
├── proxy/ # Auto-generated service proxies
14+
├── shared/ # Shared components, pipes, directives
15+
├── book/ # Feature module
16+
│ ├── book.module.ts
17+
│ ├── book-routing.module.ts
18+
│ ├── book-list/
19+
│ │ ├── book-list.component.ts
20+
│ │ ├── book-list.component.html
21+
│ │ └── book-list.component.scss
22+
│ └── book-detail/
23+
```
24+
25+
## Generate Service Proxies
26+
```bash
27+
abp generate-proxy -t ng
28+
```
29+
30+
This generates typed service classes in `src/app/proxy/`.
31+
32+
## List Component Pattern
33+
```typescript
34+
@Component({
35+
selector: 'app-book-list',
36+
templateUrl: './book-list.component.html'
37+
})
38+
export class BookListComponent implements OnInit {
39+
books = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
40+
41+
constructor(
42+
public readonly list: ListService,
43+
private bookService: BookService,
44+
private confirmation: ConfirmationService
45+
) {}
46+
47+
ngOnInit(): void {
48+
this.hookToQuery();
49+
}
50+
51+
private hookToQuery(): void {
52+
this.list.hookToQuery(query =>
53+
this.bookService.getList(query)
54+
).subscribe(response => {
55+
this.books = response;
56+
});
57+
}
58+
59+
create(): void {
60+
// Open create modal
61+
}
62+
63+
delete(book: BookDto): void {
64+
this.confirmation
65+
.warn('::AreYouSureToDelete', '::AreYouSure')
66+
.subscribe(status => {
67+
if (status === Confirmation.Status.confirm) {
68+
this.bookService.delete(book.id).subscribe(() => this.list.get());
69+
}
70+
});
71+
}
72+
}
73+
```
74+
75+
## Localization
76+
```typescript
77+
// In component
78+
constructor(private localizationService: LocalizationService) {}
79+
80+
getText(): string {
81+
return this.localizationService.instant('::Books');
82+
}
83+
```
84+
85+
```html
86+
<!-- In template -->
87+
<h1>{{ '::Books' | abpLocalization }}</h1>
88+
89+
<!-- With parameters -->
90+
<p>{{ '::WelcomeMessage' | abpLocalization: userName }}</p>
91+
```
92+
93+
## Authorization
94+
95+
### Permission Directive
96+
```html
97+
<button *abpPermission="'BookStore.Books.Create'">Create</button>
98+
```
99+
100+
### Permission Guard
101+
```typescript
102+
const routes: Routes = [
103+
{
104+
path: '',
105+
component: BookListComponent,
106+
canActivate: [PermissionGuard],
107+
data: {
108+
requiredPolicy: 'BookStore.Books'
109+
}
110+
}
111+
];
112+
```
113+
114+
### Programmatic Check
115+
```typescript
116+
constructor(private permissionService: PermissionService) {}
117+
118+
canCreate(): boolean {
119+
return this.permissionService.getGrantedPolicy('BookStore.Books.Create');
120+
}
121+
```
122+
123+
## Forms with Validation
124+
```typescript
125+
@Component({...})
126+
export class BookFormComponent {
127+
form: FormGroup;
128+
129+
constructor(private fb: FormBuilder) {
130+
this.buildForm();
131+
}
132+
133+
buildForm(): void {
134+
this.form = this.fb.group({
135+
name: ['', [Validators.required, Validators.maxLength(128)]],
136+
price: [0, [Validators.required, Validators.min(0)]]
137+
});
138+
}
139+
140+
save(): void {
141+
if (this.form.invalid) return;
142+
143+
this.bookService.create(this.form.value).subscribe(() => {
144+
// Handle success
145+
});
146+
}
147+
}
148+
```
149+
150+
```html
151+
<form [formGroup]="form" (ngSubmit)="save()">
152+
<div class="form-group">
153+
<label for="name">{{ '::Name' | abpLocalization }}</label>
154+
<input type="text" id="name" formControlName="name" class="form-control" />
155+
</div>
156+
157+
<button type="submit" class="btn btn-primary" [disabled]="form.invalid">
158+
{{ '::Save' | abpLocalization }}
159+
</button>
160+
</form>
161+
```
162+
163+
## Configuration API
164+
```typescript
165+
constructor(private configService: ConfigStateService) {}
166+
167+
getCurrentUser(): CurrentUserDto {
168+
return this.configService.getOne('currentUser');
169+
}
170+
171+
getSettings(): void {
172+
const setting = this.configService.getSetting('MyApp.MaxItemCount');
173+
}
174+
```
175+
176+
## Modal Service
177+
```typescript
178+
constructor(private modalService: ModalService) {}
179+
180+
openCreateModal(): void {
181+
const modalRef = this.modalService.open(BookFormComponent, {
182+
size: 'lg'
183+
});
184+
185+
modalRef.result.then(result => {
186+
if (result) {
187+
this.list.get();
188+
}
189+
});
190+
}
191+
```
192+
193+
## Toast Notifications
194+
```typescript
195+
constructor(private toaster: ToasterService) {}
196+
197+
showSuccess(): void {
198+
this.toaster.success('::BookCreatedSuccessfully', '::Success');
199+
}
200+
201+
showError(error: string): void {
202+
this.toaster.error(error, '::Error');
203+
}
204+
```
205+
206+
## Lazy Loading Modules
207+
```typescript
208+
// app-routing.module.ts
209+
const routes: Routes = [
210+
{
211+
path: 'books',
212+
loadChildren: () => import('./book/book.module').then(m => m.BookModule)
213+
}
214+
];
215+
```
216+
217+
## Theme & Styling
218+
- Use Bootstrap classes
219+
- ABP provides theme variables via CSS custom properties
220+
- Component-specific styles in `.component.scss`
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
name: abp-app-nolayers
3+
description: ABP Single-Layer (No-Layers / nolayers) application template - single project structure, feature-based file organization, no separate Domain/Application.Contracts projects. Use when working with the single-layer web application template or when the project has no layered separation.
4+
---
5+
6+
# ABP Single-Layer Application Template
7+
8+
> **Docs**: https://abp.io/docs/latest/solution-templates/single-layer-web-application
9+
10+
## Solution Structure
11+
12+
Single project containing everything:
13+
14+
```
15+
MyProject/
16+
├── src/
17+
│ └── MyProject/
18+
│ ├── Data/ # DbContext, migrations
19+
│ ├── Entities/ # Domain entities
20+
│ ├── Services/ # Application services + DTOs
21+
│ ├── Pages/ # Razor pages / Blazor components
22+
│ └── MyProjectModule.cs
23+
└── test/
24+
└── MyProject.Tests/
25+
```
26+
27+
## Key Differences from Layered
28+
29+
| Layered Template | Single-Layer Template |
30+
|------------------|----------------------|
31+
| DTOs in Application.Contracts | DTOs in Services folder (same project) |
32+
| Repository interfaces in Domain | Use generic `IRepository<T, TKey>` directly |
33+
| Separate Domain.Shared for constants | Constants in same project |
34+
| Multiple module classes | Single module class |
35+
36+
## File Organization
37+
38+
Group related files by feature:
39+
40+
```
41+
Services/
42+
├── Books/
43+
│ ├── BookAppService.cs
44+
│ ├── BookDto.cs
45+
│ ├── CreateBookDto.cs
46+
│ └── IBookAppService.cs
47+
└── Authors/
48+
├── AuthorAppService.cs
49+
└── ...
50+
```
51+
52+
## Simplified Entity (Still keep invariants)
53+
54+
Single-layer templates are structurally simpler, but you may still have real business invariants.
55+
56+
- For **trivial CRUD** entities, public setters can be acceptable.
57+
- For **non-trivial business rules**, still prefer encapsulation (private setters + methods) to prevent invalid states.
58+
59+
```csharp
60+
public class Book : AuditedAggregateRoot<Guid>
61+
{
62+
public string Name { get; set; } // OK for trivial CRUD only
63+
public decimal Price { get; set; }
64+
}
65+
```
66+
67+
## No Custom Repository Needed
68+
69+
Use generic repository directly - no need to define custom interfaces:
70+
71+
```csharp
72+
public class BookAppService : ApplicationService
73+
{
74+
private readonly IRepository<Book, Guid> _bookRepository;
75+
76+
// Generic repository is sufficient for single-layer apps
77+
}
78+
```

0 commit comments

Comments
 (0)