Skip to content

Latest commit

 

History

History
150 lines (99 loc) · 10.6 KB

SKILL.md

File metadata and controls

150 lines (99 loc) · 10.6 KB
name webforms-to-angular
description Migrate ASP.NET Web Forms (2008 era, .NET Framework 2.0/3.5/4.x) projects to a modern stack of Angular (standalone components, signals) frontend + ASP.NET Core .NET 8 backend. Use this skill whenever the user mentions migrating, modernizing, or porting Web Forms, .aspx, .ascx, code-behind, GridView, Repeater, ViewState, postback, UpdatePanel, FormsAuthentication, or any legacy ASP.NET concept — even if they don't explicitly say "Angular". Also use when the user wants to analyze a Web Forms project to plan a migration, extract business logic from code-behind into a clean API layer, or generate Angular components that replicate the behavior of an .aspx page. The skill assumes business logic is mixed between code-behind and a service layer (typical of 2008 projects) and produces code that is idiomatic for both Angular 17+ and .NET 8.

Web Forms → Angular + .NET 8 Migration Skill

What this skill does

It guides a per-page migration of an ASP.NET Web Forms application into:

  • Frontend: Angular 17+ with standalone components, signals, typed reactive forms, and HTTP client.
  • Backend: ASP.NET Core .NET 8 with Minimal APIs (or Controllers when the user prefers), clean separation between transport (HTTP), application logic, and data access.

The skill is page-driven: the unit of work is one .aspx (or .ascx) plus its .aspx.cs code-behind. Trying to migrate a whole solution in one shot fails — always work page-by-page after an inventory pass.

How to use this skill — the four phases

Every migration request falls into one of four phases. Identify which one the user is in before reading any reference file, and then load only the references you need.

Phase 0 — Inventory (do this first if the user hasn't yet)

If the user is starting a new migration and you don't have a clear picture of the project, run an inventory pass first. The goal is to know what you're dealing with before touching any single page.

Use scripts/inventory.py against the solution root. It walks the project and emits a JSON report with:

  • Every .aspx / .ascx / .master
  • Server controls used per page (GridView, Repeater, FormView, custom user controls, third-party like Telerik/DevExpress/Infragistics)
  • Code-behind size (rough complexity signal)
  • Detected cross-cutting concerns (FormsAuthentication, Session, Cache, UpdatePanel, AJAX ScriptManager)
  • Database access patterns visible in code-behind (SqlCommand, EF, Linq2SQL, custom DAL)

Present the inventory to the user and agree on a migration order before generating anything. Good defaults: leaf pages first, pages with the least cross-cutting concerns first, dashboards/reports last.

Phase 1 — Analyze a single page

User says something like "migrate OrdersGrid.aspx" or "what's in this page".

  1. Read the .aspx markup and the .aspx.cs code-behind. If there's a .designer.cs, read it too — it tells you exactly which controls the code-behind references.
  2. Run scripts/analyze_aspx.py <path> to get a structured JSON of controls + event handlers, or do the parse inline if the page is small.
  3. Open references/control-mapping.md to translate each control into its Angular equivalent.
  4. If the code-behind has business logic mixed in (it usually does — the user confirmed this is their situation), open references/codebehind-extraction.md and identify what belongs in the API vs. what was purely presentational glue.
  5. If the page uses ViewState, IsPostBack, dynamic control creation, or UpdatePanel, open references/viewstate-and-postback.md — these need explicit translation, not naive mapping.
  6. If the page uses FormsAuthentication, [Authorize] attributes, or reads User.Identity, open references/auth-and-session.md.

Produce an analysis summary before writing any code. Format:

## Page: OrdersGrid.aspx

### Controls to migrate
- asp:GridView "gvOrders"  →  <table> with *ngFor + signal-based pagination
- asp:DropDownList "ddlStatus"  →  <select> bound to FormControl
- asp:Button "btnExport"  →  (click) handler → calls service.export()

### Server-side behavior
- Page_Load: loads orders from OrderService.GetByUser(currentUserId)
- gvOrders_RowCommand: handles "Approve" / "Reject" → OrderService.UpdateStatus
- btnExport_Click: streams CSV via Response.Write

### To extract into API
- GET    /api/orders?status={status}        ← replaces Page_Load query
- PATCH  /api/orders/{id}/status            ← replaces RowCommand
- GET    /api/orders/export?status={status} ← replaces btnExport_Click (returns file)

### Concerns to flag
- ViewState is used to remember the current filter across postbacks → in Angular this becomes a signal in the component (or a route query param if shareable)
- FormsAuthentication ticket → migrate to JWT or cookie auth; see auth-and-session.md

Then ask the user: "Does this analysis look right? Once you confirm, I'll generate the API endpoints, then the Angular component."

Phase 2 — Generate the .NET 8 API

Only after the analysis is confirmed. Open references/api-design-net8.md.

Generate, in order:

  1. DTOs / contracts — one record per request and response. Keep them flat; don't ship EF entities over the wire.
  2. Endpoint(s) — Minimal API by default. Use the template in assets/templates/minimal-api-endpoint.cs.tmpl. Group related endpoints with MapGroup.
  3. Application service — pure C# class with the business logic that was previously mixed into code-behind. No HttpContext, no Page, no Session. Constructor-inject what it needs.
  4. Wiring — show the Program.cs lines the user must add (DI registrations, the MapGroup call). Don't rewrite the whole Program.cs; just emit the diff.

Always emit a small integration note describing how this endpoint will be consumed by the Angular service generated in Phase 3.

Phase 3 — Generate the Angular component

Open references/angular-patterns.md.

Generate three files using the templates in assets/templates/:

  1. <feature>.service.ts — typed HTTP service. One method per endpoint from Phase 2. Returns Observable<T> or signal — match the project's style if you can detect it.
  2. <feature>.component.ts — standalone component, signals for state, typed reactive form for inputs.
  3. <feature>.component.html — template that replicates the original page's structure.

Default style choices when the user hasn't specified:

  • Standalone components (no NgModule).
  • Signals for local component state. RxJS only for HTTP and stream-like concerns.
  • Typed reactive forms (FormGroup<T> with FormControl<T>), not template-driven.
  • @if / @for control flow (Angular 17+), not *ngIf / *ngFor.
  • Material or plain HTML — ask once at the start of the migration and remember the answer for the rest.

Critical principles

Never migrate one-to-one. Web Forms encodes server-driven UI patterns (postback, ViewState, server-side eventing) that have no direct Angular equivalent. Your job is to translate the user-visible behavior, not the mechanism. A GridView with sorting and paging becomes a client-side table with column sort and a paginator, served by a paginated API — not a custom directive that pretends to be a GridView.

Code-behind is not application logic. When you read .aspx.cs, mentally bucket every line into one of: (a) UI plumbing that disappears (binding controls, reading TextBox.Text), (b) presentational logic that moves to Angular (when to disable a button, when to show an error), (c) business logic that moves to a .NET 8 application service. The third bucket is what becomes API endpoints.

ViewState is not state. It's a workaround for HTTP being stateless plus Web Forms pretending it isn't. In Angular, state is either local (signal/component) or shared (service/signal store/route params). Never try to "preserve" ViewState — figure out what it was preserving and put that thing in the right place.

Don't recreate WebMethod-style RPC. It's tempting to map every server event handler to a POST endpoint named after the handler. Resist this. Design REST-ish endpoints around resources, not around button clicks. btnApprove_Click becomes PATCH /orders/{id}/status, not POST /api/btnApprove.

Keep the legacy app running until the new one is verified. Suggest a strangler-fig approach: the .NET 8 API can be deployed alongside the legacy app, and Angular pages can replace .aspx pages one route at a time behind a reverse proxy. Don't push for a big-bang cutover.

When to ask vs. when to generate

Ask the user before:

  • Choosing a UI component library (Material vs. PrimeNG vs. plain) — once per migration, then remember.
  • Deciding the API style when ambiguous (Minimal API vs. Controllers, REST vs. tRPC-style RPC).
  • Picking the auth strategy if the legacy app uses something unusual (custom auth modules, federated SSO).
  • Touching the database schema — never assume you can change it; many legacy apps share a DB with other systems.

Generate without asking:

  • File scaffolding once the analysis is confirmed.
  • Standard refactors (extracting a service from code-behind, splitting a DTO from an entity).
  • Naming conventions (kebab-case for Angular files, PascalCase for C#, I-prefixed interfaces only if the user's existing .NET code uses that style).

Reference files — when to read each

If the page or task involves... Read
Any server control (GridView, Repeater, etc.) references/control-mapping.md
Untangling logic from code-behind references/codebehind-extraction.md
ViewState, IsPostBack, UpdatePanel, dynamic controls references/viewstate-and-postback.md
Designing the .NET 8 endpoints references/api-design-net8.md
Writing the Angular component / service references/angular-patterns.md
FormsAuthentication, roles, Session-based auth references/auth-and-session.md

Read only what's relevant. A static "About" page doesn't need viewstate-and-postback.md.

Templates

The files in assets/templates/ are starting points, not final outputs. Customize them to match the user's project style — namespaces, file headers, lint rules. Use them when you need a consistent scaffold, ignore them when the user has shown you a different convention earlier in the conversation.

Examples

assets/examples/ contains a worked end-to-end migration of a small CustomerList.aspx. Read these when you need a concrete reference for what "done" looks like.