# Angular

> How midcode runs ng serve, finds each element of an Angular page in the components' templates after it loads, and edits those templates, external or inline.

- Page: https://midcode.app/docs/frameworks/angular
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt
- Status: This ships with the next release of midcode. The version you can download today (1.1.2) doesn't have it yet.

midcode edits an Angular app in its components' templates: text, classes, attributes and structure, in `.html` templates and in the ones written inside a component's `.ts`. Angular's build takes no plugin from outside, so midcode doesn't mark elements while the app compiles. It finds each element in your templates after the page has loaded. What Angular computes (`{{ }}`, `[bound]` values, `@if` and `@for` blocks) is left exactly as written. midcode 1.1.2 and earlier don't edit Angular projects.

## At a glance

| | Angular |
| --- | --- |
| Detected by | `angular.json` at the project's root |
| Runs with | `./node_modules/.bin/ng serve --host 127.0.0.1 --port $PORT` |
| Elements are marked by | Matching each loaded page against the components' templates |
| Editing | Text, classes, attributes, tag; insert, move, duplicate, delete, wrap in a stack |
| Components | No instances or props panel. A component's host element is selected like any element |
| Pages | The routes of `app.routes.ts` or a routing module |
| Styles | Your Tailwind 4 classes, or `mid:` utilities with `public/midcode.css` linked from `src/index.html` |
| Tried with | Angular 22 (standalone components, zoneless) |

## How midcode runs it

A folder with an `angular.json` is an Angular project. midcode runs your project's own CLI, in your login shell, from the project's folder:

```bash
NG_CLI_ANALYTICS=false ./node_modules/.bin/ng serve --host 127.0.0.1 --port $PORT
```

`$PORT` is a free port midcode picks, starting at 4310. `NG_CLI_ANALYTICS=false` keeps the CLI from stopping to ask about analytics, which nobody could answer. Nothing in `angular.json`, your `package.json` or your build changes. To run it another way, see [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).

### How elements get their marks

When a page loads in a frame, midcode reads its elements and looks each one up in the project's templates. Where it's sure, it sets `data-mc="file:line:col"` on the element in the page (never in your files), and from there the canvas treats it like any other marked element.

An element of the page is a written element when what's fixed in the template is there in the page: its tag, its `id`, every class that isn't computed, its text when text is all it holds, and attributes like `href`, `src`, `alt`, `type` or `placeholder`. midcode looks first among the children of what its parent turned out to be, then in the whole project, where only an unmistakable match counts. In doubt the element stays unmarked: it can't be edited, and it's never edited by mistake.

An Angular page keeps drawing itself: the router swaps views, an `@if` flips. midcode watches the page, and when elements arrive without a mark it matches the page again. After an edit it reloads nothing: Angular's dev server swaps the template in place. [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md) describes the matching in full; Angular goes the same way.

### Which files are templates

- A file named `*.component.html` or `*.ng.html`, and, in an Angular project, any other `.html` (`app.html`, `src/index.html`).
- A component's `.ts` with its template inside (`template: '…'`, in backticks or quotes). It's read where it sits, so a mark points into the `.ts` file. A template the code builds with `${…}` isn't read.

Files like `*.spec.ts`, `*.routes.ts`, `*.config.ts`, `*.service.ts` and `*.module.ts` are never read for templates.

## What you can edit

Take this template:

```html title="src/app/home/home.html"
<section class="hero">
  <h1>{{ title() }}</h1>
  <p class="lead">Furniture made to order.</p>
  @for (wood of woods(); track wood.id) {
    <app-card [wood]="wood" class="card" />
  }
</section>
```

| Element | On the canvas |
| --- | --- |
| `<p class="lead">` | Everything: its text, its classes, its place |
| `<h1>` | Classes and attributes. Its text is `{{ title() }}`, which isn't written there |
| `<app-card>` | Its written classes and attributes. `[wood]` shows as code. Written once and drawn for every item, so an edit changes them all |

What the reader takes as computed, and never rewrites:

- `{{ … }}`, and ICU messages (`{count, plural, …}`).
- The blocks: `@if`, `@else`, `@for`, `@empty`, `@switch`, `@case`, `@default`, `@defer` with `@placeholder`, `@loading` and `@error`, a `@let x = …;`, and the `}` that closes a block.
- `[property]`, `(event)`, `[(both)]`, `*structural`, `#reference` and `@animation` on a tag.

`<ng-container>`, `<ng-template>` and `<ng-content>` aren't in the page, though what's inside them is. Any other tag with a dash is a component's host element: it's in the page, and you can select it where it's written.

### Text

```diff title="src/app/home/home.html"
-  <p class="lead">Furniture made to order.</p>
+  <p class="lead">Furniture made to last.</p>
```

In an Angular template a `{` opens a message, a `}` closes a block and an `@` opens one, so midcode writes those three as entities: typing `hello@oak.studio` leaves `hello&#64;oak.studio` in the file, which the page shows as you typed it.

### Classes and attributes

The [style panel](https://midcode.app/docs/editor/styles.md) writes into the static `class` attribute and creates it when there's none. `[class.active]`, `[ngClass]` and `[class]` are bindings and are left alone.

Attributes are read as written, and a value bound to code shows as code under the name it binds. A value is written the way Angular writes it: a text as `placeholder="Your email"`, a switch that's on as `disabled`, a number or other code as `[maxlength]="40"`.

### Inline templates

The same edits work inside a component's `.ts`:

```diff title="src/app/badge/badge.ts"
 @Component({
   selector: 'app-badge',
-  template: `<span class="badge">New</span>`,
+  template: `<span class="badge">Just in</span>`,
 })
```

Quotes, backticks and backslashes you type are written as entities there (`It&#39;s here`), so they can't end the string. Before saving, midcode checks that everything outside the templates is unchanged. If an edit would reach the code around them, nothing is written and it says so: "That can't be written inside this component's code as it is: it would end its template. Edit it in the code."

### Structure

[Insert](https://midcode.app/docs/editor/insert.md) adds plain markup, an element is dragged before, after or inside another one in the same file (`⌥`-drag leaves a copy there instead), and several can be deleted, moved or wrapped in a stack (`⇧A`) at once.

## Pages

The page list comes from the files where the app lists its routes: `*.routes.ts`, `*-routing.module.ts` and `*.routing.ts` under `src/`, the app's own first. Every `path: '…'` is a page, under the paths of the routes it's a child of:

| Route | Page |
| --- | --- |
| `{ path: '', component: Home }` | The home page |
| `{ path: 'work', loadComponent: () => import('./work/work') }` | `/work` |
| `{ path: 'work/:id', component: Project }` | `/work/[id]`, a dynamic route |
| `{ path: '**', component: NotFound }` | Not a page |

When a route's component can be followed through its import, the page knows its template file. A dynamic route has no address of its own: open one of its pages by typing a real path in the page menu in the top bar ("Search, or type a path and press Enter"). [Pages and navigation](https://midcode.app/docs/editor/pages.md) has the rest.

midcode doesn't add pages to an Angular app: a route is a line of code and a component, yours or your agent's to write.

## Site settings

The globe in the top bar opens [Site settings](https://midcode.app/docs/editor/site-settings.md). The title, the description, the language, search engines, the favicon and the social image are written as tags in the `<head>` of `src/index.html`, and the images are copied into `public/`. There's one form for the whole app. This wasn't tried on an Angular project.

## Styles

With Tailwind 4 through PostCSS, which is what `ng new --style=tailwind` sets up, the panel writes your own utilities: see [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md).

Without it, midcode writes its own prefixed utilities (`mid:p-6`, `mid:md:flex`) and keeps their plain CSS in `public/midcode.css`. The first style edit creates the file and links it from the page every route goes through, as one undoable step:

```diff title="src/index.html"
   <link rel="icon" type="image/x-icon" href="favicon.ico">
+  <link rel="stylesheet" href="/midcode.css">
 </head>
```

These are global rules, each one `!important`, so they apply inside every component whatever its view encapsulation. Your components' own styles aren't edited. [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md) has the details.

## Limits

- On a page with a header, a router outlet, `@for`, `@if` / `@else`, a component with an inline template and projected content, 48 of 49 elements were found. The missing one is the host element the router creates, which isn't written anywhere.
- Tried on that app: text, classes and attributes, in external and inline templates. Not tried: apps built on NgModules, server-side rendering, component libraries such as Angular Material, and one `templateUrl` shared by several components.
- What a library draws has its template in `node_modules`, which midcode doesn't read: those elements stay unmarked.
- midcode reads a block's opening and its closing `}` as markers, not as a container. An element that uses a `@for`'s item can be dragged out of the block, where that name no longer exists. `⌘Z` puts it back.
- Routes of a lazy-loaded routing file show without the prefix they're mounted under.
- Styles without Tailwind need the `public/` folder Angular has served since version 18. An older project, with its assets listed in `angular.json`, wasn't tried. New images are copied into `public/` too, and without that folder Site settings can't place a favicon or a social image: it asks you to put the file where the site serves it and link it in the code.
- An insert that needs an import is refused, and Insert's interactive components are React ("Needs a React page").
- There are no component instances here, so no [props, variants or states](https://midcode.app/docs/editor/components.md). [The free canvas](https://midcode.app/docs/editor/free-canvas.md) is for Next.js projects on the App Router.

## Troubleshooting

**An element can be selected but not edited.** midcode didn't find it in a template with certainty. That happens when several written elements could be it and nothing fixed tells them apart: give it a class or an `id` of its own. It also happens when the element isn't written in your templates at all (a library's, or one a script adds).

**The server didn't start.** Open the log with **View log** on the card. "Change how it runs" under it lets you give another command, such as your own `npm start`; midcode keeps it in `.midcode/server.json` ([Any other stack](https://midcode.app/docs/frameworks/custom-server.md)).
