jwebmp-angular

Generates Angular 21 TypeScript projects from JWebMP annotations and serves SPAs via Vert.x with STOMP/WebSocket bridging. Provides @NgApp, @NgComponent, @NgRoutable, @NgDataService annotations, TypeScript code generation, reactive messaging, Angular control-flow components, and WebSocket group management. Use when working with JWebMP Angular integration, TypeScript generation, Angular components, STOMP/WebSocket communication, or building Angular 21 applications with JWebMP.

5 stars

Best use case

jwebmp-angular is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates Angular 21 TypeScript projects from JWebMP annotations and serves SPAs via Vert.x with STOMP/WebSocket bridging. Provides @NgApp, @NgComponent, @NgRoutable, @NgDataService annotations, TypeScript code generation, reactive messaging, Angular control-flow components, and WebSocket group management. Use when working with JWebMP Angular integration, TypeScript generation, Angular components, STOMP/WebSocket communication, or building Angular 21 applications with JWebMP.

Teams using jwebmp-angular should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/jwebmp-angular/SKILL.md --create-dirs "https://raw.githubusercontent.com/GuicedEE/ai-rules/main/skills/.system/jwebmp-angular/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/jwebmp-angular/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How jwebmp-angular Compares

Feature / Agentjwebmp-angularStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates Angular 21 TypeScript projects from JWebMP annotations and serves SPAs via Vert.x with STOMP/WebSocket bridging. Provides @NgApp, @NgComponent, @NgRoutable, @NgDataService annotations, TypeScript code generation, reactive messaging, Angular control-flow components, and WebSocket group management. Use when working with JWebMP Angular integration, TypeScript generation, Angular components, STOMP/WebSocket communication, or building Angular 21 applications with JWebMP.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# JWebMP Angular Plugin

Angular 21 TypeScript project generation and SPA hosting with STOMP/WebSocket bridge for JWebMP.

## Core Features

- **TypeScript code generation** from Java annotations
- **SPA hosting** via Vert.x with static asset serving
- **STOMP/WebSocket bridge** for real-time communication
- **Reactive message processing** with event bus integration
- **Angular control-flow components** (@if, @for, @let)
- **Routing module** with @NgRoutable support
- **Environment module** for configuration

## Quick Start

### 1. Define an Angular App

```java
@NgApp(value = "my-app", bootComponent = AppComponent.class)
public class MyApp extends NGApplication<MyApp> { }
```

### 2. Create Boot Component

```java
@NgComponent("app-root")
public class AppComponent extends DivSimple<AppComponent>
        implements INgComponent<AppComponent> { }
```

### 3. Add Routable Pages

```java
@NgRoutable(path = "dashboard", parent = {AppComponent.class})
@NgComponent("app-dashboard")
public class DashboardPage extends DivSimple<DashboardPage>
        implements INgComponent<DashboardPage> { }
```

### 4. Enable TypeScript Generation

```bash
export JWEBMP_PROCESS_ANGULAR_TS=true
# Or
mvn verify -Djwebmp.process.angular.ts=true
```

### 5. Start Application

```java
IGuiceContext.instance();
// → TypeScript generated to ~/.jwebmp/<appName>/
// → Vert.x serves dist at configured routes
// → STOMP/WebSocket bridge live at /eventbus
```

## TypeScript Compiler Pipeline

```
TypeScriptCompiler
 ├── AngularAppSetup        → Scaffolds angular.json, package.json, tsconfig
 ├── DependencyManager      → Resolves @TsDependency / @TsDevDependency
 ├── ComponentProcessor     → Processes INgComponent, INgDirective, INgDataService
 ├── AngularModuleProcessor → Generates boot module, routing module
 ├── TypeScriptCodeGenerator → Renders .ts files from annotations
 ├── AssetManager           → Copies @NgAsset, @NgScript, @NgStyleSheet
 └── TypeScriptCodeValidator → Validates generated output
```

## Annotations

### @NgApp

```java
@NgApp(value = "my-app", bootComponent = AppComponent.class)
public class MyApp extends NGApplication<MyApp> { }
```

### @NgComponent

```java
@NgComponent("app-header")
public class HeaderComponent implements INgComponent<HeaderComponent> {
    @Override
    public String render() {
        return "<header><h1>My App</h1></header>";
    }
}
```

### @NgRoutable

```java
@NgRoutable(path = "users", parent = {AppComponent.class})
@NgComponent("app-users")
public class UsersPage implements INgComponent<UsersPage> { }
```

### @NgDataService

```java
@NgDataService
public class UserService implements INgDataService<UserService> {
    @Override
    public Object getData(AjaxCall<?> call, AjaxResponse<?> response) {
        return userRepository.findAll();
    }
}
```

### @NgRestClient

Generate a fully typed, signal-based `@Injectable` Angular REST service from a
single Java class — the REST counterpart to `@NgGraphQL`. Defined in the
`tsclient` plugin (`com.jwebmp.core.base.angular.client.annotations.angular`);
see the **jwebmp-tsclient** skill for the full attribute reference.

```java
@NgRestClient(
    url = "/api/users",
    method = NgRestClient.HttpMethod.GET,
    responseType = User.class,
    responseArray = true,
    cachingEnabled = true,
    pollingEnabled = true)
@NgRestClientHeader(name = "Accept", value = "application/json")
@NgRestClientQueryParam(name = "active", value = "true")
public class UsersClient implements INgRestClient<UsersClient> {}
```

Generates an `@Injectable` HttpClient service exposing `data`/`loading`/`error`/
`success`/`polling` signals, `execute()` / `executeWithBody()`, plus polling,
caching, deduplication, deep-merge, retry, and `NONE/BEARER/BASIC/CUSTOM` auth.

## STOMP/WebSocket Communication

### WebSocket Bridge Flow

```
Angular Client (STOMP)
 └─ ws://host/eventbus
     ├─ SEND /toBus/incoming    → Event bus consumer
     │   ├─ Deserialize WebSocketMessageReceiver
     │   ├─ Dispatch to IWebSocketMessageReceiver (ajax/data/dataSend)
     │   ├─ Run in CallScope (WebSocket)
     │   └─ Reply via event bus:
     │       ├─ dataReturns → publish to per-key addresses
     │       ├─ sessionStorage → publish to "SessionStorage"
     │       └─ localStorage → publish to "LocalStorage"
     └─ SUBSCRIBE /toStomp/*   ← Server pushes via StompEventBusPublisher
```

### Built-in Message Receivers

| Receiver | Action | Purpose |
|---|---|---|
| `WebSocketAjaxCallReceiver` | `ajax` | Deserializes AjaxCall, fires event, returns AjaxResponse |
| `WebSocketDataRequestCallReceiver` | `data` | Resolves INgDataService, calls getData() |
| `WebSocketDataSendCallReceiver` | `dataSend` | Resolves INgDataService, calls receiveData() |
| `WSAddToGroupMessageReceiver` | `AddToWebSocketGroup` | Adds session to WebSocket group |
| `WSRemoveFromWebsocketGroupMessageReceiver` | `RemoveFromWebSocketGroup` | Removes session from group |

### STOMP Configuration

| Setting | Value | Notes |
|---|---|---|
| WebSocket path | `/eventbus` | STOMP over WebSocket endpoint |
| Server heartbeat | `10000` ms | Server → client |
| Client heartbeat | `50000` ms | Client → server (lenient for background tabs) |
| Sub-protocols | `v10.stomp`, `v11.stomp`, `v12.stomp` | Advertised on HTTP upgrade |
| Idle timeout | `0` (disabled) | Relies on STOMP heartbeats |

## Angular Control-Flow Components

### NgIf / NgIfElse

```java
NgIf<MyComponent> ifBlock = new NgIf<>(this)
    .setCondition("isLoggedIn")
    .add(new Paragraph<>().setText("Welcome!"));

NgIfElse<MyComponent> ifElse = new NgIfElse<>(this)
    .setCondition("hasData")
    .add(new Div<>().setText("Data loaded"))
    .setElseBlock(new NgElse<>()
        .add(new Div<>().setText("No data")));
```

Renders:
```typescript
@if (isLoggedIn) {
  <p>Welcome!</p>
}

@if (hasData) {
  <div>Data loaded</div>
} @else {
  <div>No data</div>
}
```

### NgFor

```java
NgFor<MyComponent> forLoop = new NgFor<>(this)
    .setIterable("users")
    .setTrackBy("userId")
    .add(new Div<>().setText("{{ user.name }}"));
```

Renders:
```typescript
@for (user of users; track userId) {
  <div>{{ user.name }}</div>
}
```

### NgLet

```java
NgLet<MyComponent> letVar = new NgLet<>(this)
    .setVariable("total")
    .setExpression("calculateTotal()");
```

Renders:
```typescript
@let total = calculateTotal();
```

## Routing

### AngularRoutingModule

Scans @NgRoutable classes and generates route tree:

```java
@NgRoutable(path = "dashboard", parent = {AppComponent.class})
public class DashboardPage { }

@NgRoutable(path = "users", parent = {DashboardPage.class})
public class UsersPage { }

@NgRoutable(path = "profile/:id", parent = {DashboardPage.class})
public class ProfilePage { }
```

Generates:
```typescript
RouterModule.forRoot([
  {
    path: 'dashboard',
    component: DashboardComponent,
    children: [
      { path: 'users', component: UsersComponent },
      { path: 'profile/:id', component: ProfileComponent }
    ]
  }
])
```

### RouterLink Component

```java
RouterLink link = new RouterLink()
    .setRouterLink("/dashboard")
    .setText("Go to Dashboard");

RouterLink paramLink = new RouterLink()
    .setRouterLink("/profile/123")
    .setQueryParams(Map.of("tab", "settings"))
    .setText("View Profile");
```

## Environment Module

```java
EnvironmentModule env = new EnvironmentModule()
    .setOptions(new EnvironmentOptions()
        .setProduction(false)
        .setApiUrl("http://localhost:8080/api")
        .addCustomProperty("featureFlags", Map.of(
            "newUI", true,
            "betaFeatures", false
        )));
```

Generates:
```typescript
export const environment = {
  production: false,
  apiUrl: 'http://localhost:8080/api',
  featureFlags: {
    newUI: true,
    betaFeatures: false
  }
};
```

## Vert.x Router Wiring

| Route | Method | Handler | Purpose |
|---|---|---|---|
| `/eventbus/*` | WebSocket | STOMP server | WebSocket → STOMP bridge |
| `/assets/*` | GET | StaticHandler | Angular compiled assets (1-year cache) |
| `/{file}.{ext}` | GET | StaticHandler | Root-level static files |
| `/**` (SPA fallback) | GET | `sendFile(index.html)` | Angular Router routes |

## SPI Extension Points

| SPI | Purpose |
|---|---|
| `AngularScanPackages` | Add packages to Angular classpath scan |
| `RenderedAssets` | Provide additional build assets |
| `NpmrcConfigurator` | Customize `.npmrc` file |
| `WebSocketGroupAdd` | Custom logic for WebSocket group joins |
| `TypescriptIndexPageConfigurator` | Customize generated `index.html` |
| `IWebSocketAuthDataProvider` | Provide authentication data for WebSocket |

## Configuration

| Environment Variable | Default | Purpose |
|---|---|---|
| `JWEBMP_PROCESS_ANGULAR_TS` | `false` | Enable/disable TypeScript generation |
| `jwebmp.outputDirectory` | — | Override output directory |
| `jwebmp` | `~` (user home) | Base directory |
| `ENVIRONMENT` | `dev` | Runtime environment hint |
| `PORT` | `8080` | Server port |

## NPM Dependencies

The plugin manages Angular dependencies:

```json
{
  "dependencies": {
    "@angular/core": "^20.0.0",
    "@angular/common": "^20.0.0",
    "@angular/router": "^20.0.0",
    "@angular/forms": "^20.0.0",
    "@stomp/ng2-stompjs": "^latest",
    "rxjs": "^7.8.0"
  }
}
```

## Common Patterns

### Data Service with WebSocket

```java
@NgDataService
public class LiveDataService implements INgDataService<LiveDataService> {
    @Inject
    private DataRepository repository;

    @Override
    public Object getData(AjaxCall<?> call, AjaxResponse<?> response) {
        String entityId = call.getParameters().get("id");
        return repository.findById(entityId);
    }

    @Override
    public void receiveData(AjaxCall<?> call, AjaxResponse<?> response) {
        String data = call.getParameters().get("data");
        repository.save(data);

        // Push update to all clients in group
        StompEventBusPublisher.publish("/toStomp/updates", data);
    }
}
```

### Event Handling

```java
public class ButtonClickEvent extends OnClickAdapter {
    @Override
    public void onClick(AjaxCall<?> call, AjaxResponse<?> response) {
        // Process server-side
        String result = processData();

        // Update UI
        response.addComponent(new Div<>().setText("Result: " + result));

        // Publish to WebSocket group
        StompEventBusPublisher.publish("/toStomp/notifications", result);
    }
}
```

### WebSocket Group Management

```java
@NgComponent("chat-room")
public class ChatRoom implements INgComponent<ChatRoom> {
    @Override
    public void configure(IComponentHierarchyBase<?, ?> component) {
        // Component auto-joins WebSocket group
        component.setAttribute("websocketgroup", "chat-room-1");
    }
}
```

## JPMS Module

```java
module com.jwebmp.core.angular {
    requires transitive com.jwebmp.core.base.angular.client;
    requires transitive com.jwebmp.vertx;
    requires transitive io.vertx.eventbusbridge;
    requires transitive io.vertx.stomp;

    provides IGuicePreStartup with AngularPreStartup;
    provides IGuicePostStartup with AngularTSPostStartup;
    provides IGuiceModule with AngularTSSiteBinder;
    provides VertxRouterConfigurator with AngularTSSiteBinder;
    provides IWebSocketMessageReceiver with
        WebSocketAjaxCallReceiver,
        WebSocketDataRequestCallReceiver,
        WebSocketDataSendCallReceiver,
        WSAddToGroupMessageReceiver,
        WSRemoveFromWebsocketGroupMessageReceiver;
}
```

## Key Classes

- `AngularTSSiteBinder` — Core module + router configuration
- `TypeScriptCompiler` — Orchestrates TS generation
- `NGApplication` — Base class for @NgApp
- `AngularRoutingModule` — Generates RouterModule.forRoot()
- `EnvironmentModule` — Generates environment config
- `StompEventBusPublisher` — Helper for STOMP publishing

## Installation

```xml
<dependency>
  <groupId>com.jwebmp.plugins</groupId>
  <artifactId>angular</artifactId>
</dependency>
```

## References

- Module: `com.jwebmp.core.angular`
- Java: 25+
- Angular: 20
- Dependencies: JWebMP Core, Vert.x, STOMP
- License: Apache 2.0

## Build Notes

Plugin generates TypeScript but **does not** run `ng build`. Build separately:

```bash
cd ~/.jwebmp/my-app
npm install
ng build
```

Dist output served automatically by Vert.x.

Related Skills

jwebmp-webawesome

5
from GuicedEE/ai-rules

WebAwesome icon integration for JWebMP — modern, open-source icon library. Provides 1,500+ icons with solid/regular styles, sizing, rotation, animation, and CSS utilities. Drop-in FontAwesome alternative with fresh designs. Use when working with WebAwesome icons, modern icon designs, or as FontAwesome alternative in JWebMP applications.

jwebmp-webawesome-pro

5
from GuicedEE/ai-rules

WebAwesome Pro integration for JWebMP with premium icons and features. Extends jwebmp-webawesome with additional styles, premium icons, and advanced features. Use when working with WebAwesome Pro icons or premium WebAwesome features in JWebMP applications.

jwebmp-weather-icons

5
from GuicedEE/ai-rules

Weather Icons font library for JWebMP providing weather icon fonts. Use when displaying weather-related icons.

jwebmp-waypoints

5
from GuicedEE/ai-rules

Waypoints jQuery plugin for JWebMP triggering functions when elements enter the viewport. Use when implementing scroll-based interactions and animations.

jwebmp-waves-effect

5
from GuicedEE/ai-rules

Waves material design ripple effect for JWebMP creating Material Design click ripples on elements. Use when adding Material Design interaction effects.

jwebmp-vertx

5
from GuicedEE/ai-rules

Portable connector between JWebMP and Vert.x 5 powered by GuicedEE. Provides automatic page routing, AJAX event pipeline, data component servlet, CSS endpoint, site-loader script, WebSocket broadcasting via event bus, user-agent detection, and call-scope integration. Use when working with JWebMP Vert.x integration, HTTP routing, AJAX handling, WebSocket communication, or building reactive web applications with JWebMP.

jwebmp-tsclient

5
from GuicedEE/ai-rules

TypeScript client generation for JWebMP plugins. Provides annotations and utilities for generating TypeScript interfaces, components, services, and modules from Java code. Supports @TsDependency, @TsDevDependency, @NgComponent, @NgDataService, @NgRestClient annotations. Use when creating JWebMP plugins that generate TypeScript code, defining npm dependencies, building Angular-integrated components, or generating typed Angular REST client services.

jwebmp-toastr

5
from GuicedEE/ai-rules

Toastr jQuery notification plugin integration for JWebMP displaying non-blocking toast notifications. Use when showing transient user notifications and alerts.

jwebmp-themify-icons

5
from GuicedEE/ai-rules

Themify Icons font library for JWebMP providing a comprehensive icon font collection. Use when adding Themify icons to projects.

jwebmp-skycons

5
from GuicedEE/ai-rules

Skycons animated weather icons for JWebMP creating beautiful animated SVG weather visualizations. Use when rendering weather data with animated icons.

jwebmp-session-storage

5
from GuicedEE/ai-rules

Browser Session Storage integration for JWebMP providing client-side temporary data storage for the browser session. Use when storing temporary user session data.

jwebmp-prism

5
from GuicedEE/ai-rules

Prism syntax highlighter integration for JWebMP providing powerful code highlighting with line numbers, copy button, and themes. Use when displaying highlighted code with advanced features.