Overview
Pattern: Full custom
Neither the data layer nor the UI components are from the framework. You write plain Angular (optionally with PrimeNG as widget set) and call your backend via HttpClient. The framework only gives you the app shell (authentication, layout, theming, possibly utility services).
When to Use It
- Completely bespoke UX (multi-step wizard, custom designer, visual editor, specialized viewers).
- Public/landing page that must not use the back-office style.
- Integration with 3rd-party SDKs (Stripe checkout, custom Google Maps, advanced video players).
- Internal tools with very specific needs where the framework pattern would be overhead.
- Tests, playgrounds, isolated demos.
Architecture
- Developer: writes a standalone Angular component, possibly with reactive forms, and calls your backend with
HttpClient. - Framework: only provides the app shell (login cookie, layout, theming). No datasource, no list-grid, no metadata.
- Backend: a classic .NET Controller (or external service). No framework conventions.
What You Do (Frontend)
Standalone Angular component with standard widgets. Example: PrimeNG table + CRUD with HttpClient.
<!-- source: wwwroot/src/app/component/examples/pattern-4/4a-pure-ptable-crud/4a-pure-ptable-crud.component.html -->
<p-table [value]="tasks()" dataKey="id">
<ng-template pTemplate="header">
<tr><th>ID</th><th>Title</th><th>Done</th><th></th></tr>
</ng-template>
<ng-template pTemplate="body" let-row>
<tr>
<td>{{ row.id }}</td><!-- source: wwwroot/src/app/component/examples/pattern-4/4a-pure-ptable-crud/4a-pure-ptable-crud.component.ts -->
import { Component, signal, inject, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface TaskItem { id: number; title: string; done: boolean; }
@Component({
selector: 'app-tasks',What You Do (Backend)
A classic REST Controller. No integration with the framework.
<!-- source: WuicTest/Controllers/SamplesController.cs -->
[HttpGet("tasks")]
public IActionResult GetTasks() => Ok(_tasks);
[HttpPost("tasks")]
public IActionResult AddTask([FromBody] TaskItem item)
{
item.Id = _tasks.Count == 0 ? 1 : _tasks.Max(t => t.Id) + 1;Trade-offs
| Pros | Cons |
|---|---|
| Maximum UX/UI flexibility | You must reimplement ready-made things (filters, paging, export, audit) |
| Standard Angular/PrimeNG stack | No integration with the framework's scaffolding and metadata |
| Free backend, no conventions | Schema and migrations all on your shoulders |
| Good for public pages or isolated tools | High maintenance cost if the same entity also needs the WUIC back-office |
Live Examples in WuicTest
- Pure p-table CRUD → PrimeNG
<p-table>+HttpClientfor CRUD on/api/samples/tasks. Source folder:wwwroot/src/app/component/examples/pattern-4/4a-pure-ptable-crud/. Open demo. - Pure form wizard →
p-steppermulti-step with reactive forms, submit to/api/samples/registrations. Source folder:wwwroot/src/app/component/examples/pattern-4/4b-pure-form-wizard/. Open demo.
> In the WuicTest examples the server-side data is in in-memory lists: it is lost on restart. It's a demo simplification to avoid adding migrations. In production, replace with EF Core / Dapper / store of your choice.
See Also
- Pattern 1 — Full autogeneration: if the same entity also makes sense in a standard back-office.
- Pattern 2 — Framework data + Custom component: if the data source is in the framework model but you need custom UI.
- Pattern 3 — Framework component + Custom data: if you want to keep
<wuic-list-grid>while maintaining a free backend. - Pattern 5 — Framework component + Framework data (manual mount): if you want a custom layout but both data + widgets from the framework.