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

Snippet 1HTML
<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 -->

Snippet 2ts
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 -->

Snippet 3C#
[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

ProsCons
Maximum UX/UI flexibilityYou must reimplement ready-made things (filters, paging, export, audit)
Standard Angular/PrimeNG stackNo integration with the framework's scaffolding and metadata
Free backend, no conventionsSchema and migrations all on your shoulders
Good for public pages or isolated toolsHigh maintenance cost if the same entity also needs the WUIC back-office

Live Examples in WuicTest

  • Pure p-table CRUD → PrimeNG <p-table> + HttpClient for CRUD on /api/samples/tasks. Source folder: wwwroot/src/app/component/examples/pattern-4/4a-pure-ptable-crud/. Open demo.
  • Pure form wizardp-stepper multi-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