# Abp Angular

> 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.

- Skill: `majiayu000/abp-angular` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/abp-angular`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/abp-angular/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/abp-angular

---


# ABP Angular UI

> **Docs**: https://abp.io/docs/latest/framework/ui/angular/overview

## Project Structure
```
src/app/
├── proxy/              # Auto-generated service proxies
├── shared/             # Shared components, pipes, directives
├── book/               # Feature module
│   ├── book.module.ts
│   ├── book-routing.module.ts
│   ├── book-list/
│   │   ├── book-list.component.ts
│   │   ├── book-list.component.html
│   │   └── book-list.component.scss
│   └── book-detail/
```

## Generate Service Proxies
```bash
abp generate-proxy -t ng
```

This generates typed service classes in `src/app/proxy/`.

## List Component Pattern
```typescript
@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html'
})
export class BookListComponent implements OnInit {
  books = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;

  constructor(
    public readonly list: ListService,
    private bookService: BookService,
    private confirmation: ConfirmationService
  ) {}

  ngOnInit(): void {
    this.hookToQuery();
  }

  private hookToQuery(): void {
    this.list.hookToQuery(query =>
      this.bookService.getList(query)
    ).subscribe(response => {
      this.books = response;
    });
  }

  create(): void {
    // Open create modal
  }

  delete(book: BookDto): void {
    this.confirmation
      .warn('::AreYouSureToDelete', '::AreYouSure')
      .subscribe(status => {
        if (status === Confirmation.Status.confirm) {
          this.bookService.delete(book.id).subscribe(() => this.list.get());
        }
      });
  }
}
```

## Localization
```typescript
// In component
constructor(private localizationService: LocalizationService) {}

getText(): string {
  return this.localizationService.instant('::Books');
}
```

```html
<!-- In template -->
<h1>{{ '::Books' | abpLocalization }}</h1>

<!-- With parameters -->
<p>{{ '::WelcomeMessage' | abpLocalization: userName }}</p>
```

## Authorization

### Permission Directive
```html
<button *abpPermission="'BookStore.Books.Create'">Create</button>
```

### Permission Guard
```typescript
const routes: Routes = [
  {
    path: '',
    component: BookListComponent,
    canActivate: [PermissionGuard],
    data: {
      requiredPolicy: 'BookStore.Books'
    }
  }
];
```

### Programmatic Check
```typescript
constructor(private permissionService: PermissionService) {}

canCreate(): boolean {
  return this.permissionService.getGrantedPolicy('BookStore.Books.Create');
}
```

## Forms with Validation
```typescript
@Component({...})
export class BookFormComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.buildForm();
  }

  buildForm(): void {
    this.form = this.fb.group({
      name: ['', [Validators.required, Validators.maxLength(128)]],
      price: [0, [Validators.required, Validators.min(0)]]
    });
  }

  save(): void {
    if (this.form.invalid) return;

    this.bookService.create(this.form.value).subscribe(() => {
      // Handle success
    });
  }
}
```

```html
<form [formGroup]="form" (ngSubmit)="save()">
  <div class="form-group">
    <label for="name">{{ '::Name' | abpLocalization }}</label>
    <input type="text" id="name" formControlName="name" class="form-control" />
  </div>

  <button type="submit" class="btn btn-primary" [disabled]="form.invalid">
    {{ '::Save' | abpLocalization }}
  </button>
</form>
```

## Configuration API
```typescript
constructor(private configService: ConfigStateService) {}

getCurrentUser(): CurrentUserDto {
  return this.configService.getOne('currentUser');
}

getSettings(): void {
  const setting = this.configService.getSetting('MyApp.MaxItemCount');
}
```

## Modal Service
```typescript
constructor(private modalService: ModalService) {}

openCreateModal(): void {
  const modalRef = this.modalService.open(BookFormComponent, {
    size: 'lg'
  });

  modalRef.result.then(result => {
    if (result) {
      this.list.get();
    }
  });
}
```

## Toast Notifications
```typescript
constructor(private toaster: ToasterService) {}

showSuccess(): void {
  this.toaster.success('::BookCreatedSuccessfully', '::Success');
}

showError(error: string): void {
  this.toaster.error(error, '::Error');
}
```

## Lazy Loading Modules
```typescript
// app-routing.module.ts
const routes: Routes = [
  {
    path: 'books',
    loadChildren: () => import('./book/book.module').then(m => m.BookModule)
  }
];
```

## Theme & Styling
- Use Bootstrap classes
- ABP provides theme variables via CSS custom properties
- Component-specific styles in `.component.scss`

