[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-98a32458-1986-4f76-b7de-4515e71566da":3,"$fLmcF5uW2j7BJfUMLP8i-0df_dw9WsisP1MNweS3i2yU":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"98a32458-1986-4f76-b7de-4515e71566da","angular","现代Angular（v20+）专家，精通信号、独立组件、无区域应用、SSR\u002F水合以及响应式模式。","cat_coding_frontend","mod_coding","sickn33,coding","---\nname: angular\ndescription: Modern Angular (v20+) expert with deep knowledge of Signals, Standalone Components, Zoneless applications, SSR\u002FHydration, and reactive patterns.\nrisk: safe\nsource: self\ndate_added: '2026-02-27'\n---\n\n# Angular Expert\n\nMaster modern Angular development with Signals, Standalone Components, Zoneless applications, SSR\u002FHydration, and the latest reactive patterns.\n\n## When to Use This Skill\n\n- Building new Angular applications (v20+)\n- Implementing Signals-based reactive patterns\n- Creating Standalone Components and migrating from NgModules\n- Configuring Zoneless Angular applications\n- Implementing SSR, prerendering, and hydration\n- Optimizing Angular performance\n- Adopting modern Angular patterns and best practices\n\n## Do Not Use This Skill When\n\n- Migrating from AngularJS (1.x) → use `angular-migration` skill\n- Working with legacy Angular apps that cannot upgrade\n- General TypeScript issues → use `typescript-expert` skill\n\n## Instructions\n\n1. Assess the Angular version and project structure\n2. Apply modern patterns (Signals, Standalone, Zoneless)\n3. Implement with proper typing and reactivity\n4. Validate with build and tests\n\n## Safety\n\n- Always test changes in development before production\n- Gradual migration for existing apps (don't big-bang refactor)\n- Keep backward compatibility during transitions\n\n---\n\n## Angular Version Timeline\n\n| Version        | Release | Key Features                                           |\n| -------------- | ------- | ------------------------------------------------------ |\n| **Angular 20** | Q2 2025 | Signals stable, Zoneless stable, Incremental hydration |\n| **Angular 21** | Q4 2025 | Signals-first default, Enhanced SSR                    |\n| **Angular 22** | Q2 2026 | Signal Forms, Selectorless components                  |\n\n---\n\n## 1. Signals: The New Reactive Primitive\n\nSignals are Angular's fine-grained reactivity system, replacing zone.js-based change detection.\n\n### Core Concepts\n\n```typescript\nimport { signal, computed, effect } from \"@angular\u002Fcore\";\n\n\u002F\u002F Writable signal\nconst count = signal(0);\n\n\u002F\u002F Read value\nconsole.log(count()); \u002F\u002F 0\n\n\u002F\u002F Update value\ncount.set(5); \u002F\u002F Direct set\ncount.update((v) => v + 1); \u002F\u002F Functional update\n\n\u002F\u002F Computed (derived) signal\nconst doubled = computed(() => count() * 2);\n\n\u002F\u002F Effect (side effects)\neffect(() => {\n  console.log(`Count changed to: ${count()}`);\n});\n```\n\n### Signal-Based Inputs and Outputs\n\n```typescript\nimport { Component, input, output, model } from \"@angular\u002Fcore\";\n\n@Component({\n  selector: \"app-user-card\",\n  standalone: true,\n  template: `\n    \u003Cdiv class=\"card\">\n      \u003Ch3>{{ name() }}\u003C\u002Fh3>\n      \u003Cspan>{{ role() }}\u003C\u002Fspan>\n      \u003Cbutton (click)=\"select.emit(id())\">Select\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  `,\n})\nexport class UserCardComponent {\n  \u002F\u002F Signal inputs (read-only)\n  id = input.required\u003Cstring>();\n  name = input.required\u003Cstring>();\n  role = input\u003Cstring>(\"User\"); \u002F\u002F With default\n\n  \u002F\u002F Output\n  select = output\u003Cstring>();\n\n  \u002F\u002F Two-way binding (model)\n  isSelected = model(false);\n}\n\n\u002F\u002F Usage:\n\u002F\u002F \u003Capp-user-card [id]=\"'123'\" [name]=\"'John'\" [(isSelected)]=\"selected\" \u002F>\n```\n\n### Signal Queries (ViewChild\u002FContentChild)\n\n```typescript\nimport {\n  Component,\n  viewChild,\n  viewChildren,\n  contentChild,\n} from \"@angular\u002Fcore\";\n\n@Component({\n  selector: \"app-container\",\n  standalone: true,\n  template: `\n    \u003Cinput #searchInput \u002F>\n    \u003Capp-item *ngFor=\"let item of items()\" \u002F>\n  `,\n})\nexport class ContainerComponent {\n  \u002F\u002F Signal-based queries\n  searchInput = viewChild\u003CElementRef>(\"searchInput\");\n  items = viewChildren(ItemComponent);\n  projectedContent = contentChild(HeaderDirective);\n\n  focusSearch() {\n    this.searchInput()?.nativeElement.focus();\n  }\n}\n```\n\n### When to Use Signals vs RxJS\n\n| Use Case                | Signals         | RxJS                             |\n| ----------------------- | --------------- | -------------------------------- |\n| Local component state   | ✅ Preferred    | Overkill                         |\n| Derived\u002Fcomputed values | ✅ `computed()` | `combineLatest` works            |\n| Side effects            | ✅ `effect()`   | `tap` operator                   |\n| HTTP requests           | ❌              | ✅ HttpClient returns Observable |\n| Event streams           | ❌              | ✅ `fromEvent`, operators        |\n| Complex async flows     | ❌              | ✅ `switchMap`, `mergeMap`       |\n\n---\n\n## 2. Standalone Components\n\nStandalone components are self-contained and don't require NgModule declarations.\n\n### Creating Standalone Components\n\n```typescript\nimport { Component } from \"@angular\u002Fcore\";\nimport { CommonModule } from \"@angular\u002Fcommon\";\nimport { RouterLink } from \"@angular\u002Frouter\";\n\n@Component({\n  selector: \"app-header\",\n  standalone: true,\n  imports: [CommonModule, RouterLink], \u002F\u002F Direct imports\n  template: `\n    \u003Cheader>\n      \u003Ca routerLink=\"\u002F\">Home\u003C\u002Fa>\n      \u003Ca routerLink=\"\u002Fabout\">About\u003C\u002Fa>\n    \u003C\u002Fheader>\n  `,\n})\nexport class HeaderComponent {}\n```\n\n### Bootstrapping Without NgModule\n\n```typescript\n\u002F\u002F main.ts\nimport { bootstrapApplication } from \"@angular\u002Fplatform-browser\";\nimport { provideRouter } from \"@angular\u002Frouter\";\nimport { provideHttpClient } from \"@angular\u002Fcommon\u002Fhttp\";\nimport { AppComponent } from \".\u002Fapp\u002Fapp.component\";\nimport { routes } from \".\u002Fapp\u002Fapp.routes\";\n\nbootstrapApplication(AppComponent, {\n  providers: [provideRouter(routes), provideHttpClient()],\n});\n```\n\n### Lazy Loading Standalone Components\n\n```typescript\n\u002F\u002F app.routes.ts\nimport { Routes } from \"@angular\u002Frouter\";\n\nexport const routes: Routes = [\n  {\n    path: \"dashboard\",\n    loadComponent: () =>\n      import(\".\u002Fdashboard\u002Fdashboard.component\").then(\n        (m) => m.DashboardComponent,\n      ),\n  },\n  {\n    path: \"admin\",\n    loadChildren: () =>\n      import(\".\u002Fadmin\u002Fadmin.routes\").then((m) => m.ADMIN_ROUTES),\n  },\n];\n```\n\n---\n\n## 3. Zoneless Angular\n\nZoneless applications don't use zone.js, improving performance and debugging.\n\n### Enabling Zoneless Mode\n\n```typescript\n\u002F\u002F main.ts\nimport { bootstrapApplication } from \"@angular\u002Fplatform-browser\";\nimport { provideZonelessChangeDetection } from \"@angular\u002Fcore\";\nimport { AppComponent } from \".\u002Fapp\u002Fapp.component\";\n\nbootstrapApplication(AppComponent, {\n  providers: [provideZonelessChangeDetection()],\n});\n```\n\n### Zoneless Component Patterns\n\n```typescript\nimport { Component, signal, ChangeDetectionStrategy } from \"@angular\u002Fcore\";\n\n@Component({\n  selector: \"app-counter\",\n  standalone: true,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    \u003Cdiv>Count: {{ count() }}\u003C\u002Fdiv>\n    \u003Cbutton (click)=\"increment()\">+\u003C\u002Fbutton>\n  `,\n})\nexport class CounterComponent {\n  count = signal(0);\n\n  increment() {\n    this.count.update((v) => v + 1);\n    \u002F\u002F No zone.js needed - Signal triggers change detection\n  }\n}\n```\n\n### Key Zoneless Benefits\n\n- **Performance**: No zone.js patches on async APIs\n- **Debugging**: Clean stack traces without zone wrappers\n- **Bundle size**: Smaller without zone.js (~15KB savings)\n- **Interoperability**: Better with Web Components and micro-frontends\n\n---\n\n## 4. Server-Side Rendering & Hydration\n\n### SSR Setup with Angular CLI\n\n```bash\nng add @angular\u002Fssr\n```\n\n### Hydration Configuration\n\n```typescript\n\u002F\u002F app.config.ts\nimport { ApplicationConfig } from \"@angular\u002Fcore\";\nimport {\n  provideClientHydration,\n  withEventReplay,\n} from \"@angular\u002Fplatform-browser\";\n\nexport const appConfig: ApplicationConfig = {\n  providers: [provideClientHydration(withEventReplay())],\n};\n```\n\n### Incremental Hydration (v20+)\n\n```typescript\nimport { Component } from \"@angular\u002Fcore\";\n\n@Component({\n  selector: \"app-page\",\n  standalone: true,\n  template: `\n    \u003Capp-hero \u002F>\n\n    @defer (hydrate on viewport) {\n      \u003Capp-comments \u002F>\n    }\n\n    @defer (hydrate on interaction) {\n      \u003Capp-chat-widget \u002F>\n    }\n  `,\n})\nexport class PageComponent {}\n```\n\n### Hydration Triggers\n\n| Trigger          | When to Use                             |\n| ---------------- | --------------------------------------- |\n| `on idle`        | Low-priority, hydrate when browser idle |\n| `on viewport`    | Hydrate when element enters viewport    |\n| `on interaction` | Hydrate on first user interaction       |\n| `on hover`       | Hydrate when user hovers                |\n| `on timer(ms)`   | Hydrate after specified delay           |\n\n---\n\n## 5. Modern Routing Patterns\n\n### Functional Route Guards\n\n```typescript\n\u002F\u002F auth.guard.ts\nimport { inject } from \"@angular\u002Fcore\";\nimport { Router, CanActivateFn } from \"@angular\u002Frouter\";\nimport { AuthService } from \".\u002Fauth.service\";\n\nexport const authGuard: CanActivateFn = (route, state) => {\n  const auth = inject(AuthService);\n  const router = inject(Router);\n\n  if (auth.isAuthenticated()) {\n    return true;\n  }\n\n  return router.createUrlTree([\"\u002Flogin\"], {\n    queryParams: { returnUrl: state.url },\n  });\n};\n\n\u002F\u002F Usage in routes\nexport const routes: Routes = [\n  {\n    path: \"dashboard\",\n    loadComponent: () => import(\".\u002Fdashboard.component\"),\n    canActivate: [authGuard],\n  },\n];\n```\n\n### Route-Level Data Resolvers\n\n```typescript\nimport { inject } from '@angular\u002Fcore';\nimport { ResolveFn } from '@angular\u002Frouter';\nimport { UserService } from '.\u002Fuser.service';\nimport { User } from '.\u002Fuser.model';\n\nexport const userResolver: ResolveFn\u003CUser> = (route) => {\n  const userService = inject(UserService);\n  return userService.getUser(route.paramMap.get('id')!);\n};\n\n\u002F\u002F In routes\n{\n  path: 'user\u002F:id',\n  loadComponent: () => import('.\u002Fuser.component'),\n  resolve: { user: userResolver }\n}\n\n\u002F\u002F In component\nexport class UserComponent {\n  private route = inject(ActivatedRoute);\n  user = toSignal(this.route.data.pipe(map(d => d['user'])));\n}\n```\n\n---\n\n## 6. Dependency Injection Patterns\n\n### Modern inject() Function\n\n```typescript\nimport { Component, inject } from '@angular\u002Fcore';\nimport { HttpClient } from '@angular\u002Fcommon\u002Fhttp';\nimport { UserService } from '.\u002Fuser.service';\n\n@Component({...})\nexport class UserComponent {\n  \u002F\u002F Modern inject() - no constructor needed\n  private http = inject(HttpClient);\n  private userService = inject(UserService);\n\n  \u002F\u002F Works in any injection context\n  users = toSignal(this.userService.getUsers());\n}\n```\n\n### Injection Tokens for Configuration\n\n```typescript\nimport { InjectionToken, inject } from \"@angular\u002Fcore\";\n\n\u002F\u002F Define token\nexport const API_BASE_URL = new InjectionToken\u003Cstring>(\"API_BASE_URL\");\n\n\u002F\u002F Provide in config\nbootstrapApplication(AppComponent, {\n  providers: [{ provide: API_BASE_URL, useValue: \"https:\u002F\u002Fapi.example.com\" }],\n});\n\n\u002F\u002F Inject in service\n@Injectable({ providedIn: \"root\" })\nexport class ApiService {\n  private baseUrl = inject(API_BASE_URL);\n\n  get(endpoint: string) {\n    return this.http.get(`${this.baseUrl}\u002F${endpoint}`);\n  }\n}\n```\n\n---\n\n## 7. Component Composition & Reusability\n\n### Content Projection (Slots)\n\n```typescript\n@Component({\n  selector: 'app-card',\n  template: `\n    \u003Cdiv class=\"card\">\n      \u003Cdiv class=\"header\">\n        \u003C!-- Select by attribute -->\n        \u003Cng-content select=\"[card-header]\">\u003C\u002Fng-content>\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"body\">\n        \u003C!-- Default slot -->\n        \u003Cng-content>\u003C\u002Fng-content>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  `\n})\nexport class CardComponent {}\n\n\u002F\u002F Usage\n\u003Capp-card>\n  \u003Ch3 card-header>Title\u003C\u002Fh3>\n  \u003Cp>Body content\u003C\u002Fp>\n\u003C\u002Fapp-card>\n```\n\n### Host Directives (Composition)\n\n```typescript\n\u002F\u002F Reusable behaviors without inheritance\n@Directive({\n  standalone: true,\n  selector: '[appTooltip]',\n  inputs: ['tooltip'] \u002F\u002F Signal input alias\n})\nexport class TooltipDirective { ... }\n\n@Component({\n  selector: 'app-button',\n  standalone: true,\n  hostDirectives: [\n    {\n      directive: TooltipDirective,\n      inputs: ['tooltip: title'] \u002F\u002F Map input\n    }\n  ],\n  template: `\u003Cng-content \u002F>`\n})\nexport class ButtonComponent {}\n```\n\n---\n\n## 8. State Management Patterns\n\n### Signal-Based State Service\n\n```typescript\nimport { Injectable, signal, computed } from \"@angular\u002Fcore\";\n\ninterface AppState {\n  user: User | null;\n  theme: \"light\" | \"dark\";\n  notifications: Notification[];\n}\n\n@Injectable({ providedIn: \"root\" })\nexport class StateService {\n  \u002F\u002F Private writable signals\n  private _user = signal\u003CUser | null>(null);\n  private _theme = signal\u003C\"light\" | \"dark\">(\"light\");\n  private _notifications = signal\u003CNotification[]>([]);\n\n  \u002F\u002F Public read-only computed\n  readonly user = computed(() => this._user());\n  readonly theme = computed(() => this._theme());\n  readonly notifications = computed(() => this._notifications());\n  readonly unreadCount = computed(\n    () => this._notifications().filter((n) => !n.read).length,\n  );\n\n  \u002F\u002F Actions\n  setUser(user: User | null) {\n    this._user.set(user);\n  }\n\n  toggleTheme() {\n    this._theme.update((t) => (t === \"light\" ? \"dark\" : \"light\"));\n  }\n\n  addNotification(notification: Notification) {\n    this._notifications.update((n) => [...n, notification]);\n  }\n}\n```\n\n### Component Store Pattern with Signals\n\n```typescript\nimport { Injectable, signal, computed, inject } from \"@angular\u002Fcore\";\nimport { HttpClient } from \"@angular\u002Fcommon\u002Fhttp\";\nimport { toSignal } from \"@angular\u002Fcore\u002Frxjs-interop\";\n\n@Injectable()\nexport class ProductStore {\n  private http = inject(HttpClient);\n\n  \u002F\u002F State\n  private _products = signal\u003CProduct[]>([]);\n  private _loading = signal(false);\n  private _filter = signal(\"\");\n\n  \u002F\u002F Selectors\n  readonly products = computed(() => this._products());\n  readonly loading = computed(() => this._loading());\n  readonly filteredProducts = computed(() => {\n    const filter = this._filter().toLowerCase();\n    return this._products().filter((p) =>\n      p.name.toLowerCase().includes(filter),\n    );\n  });\n\n  \u002F\u002F Actions\n  loadProducts() {\n    this._loading.set(true);\n    this.http.get\u003CProduct[]>(\"\u002Fapi\u002Fproducts\").subscribe({\n      next: (products) => {\n        this._products.set(products);\n        this._loading.set(false);\n      },\n      error: () => this._loading.set(false),\n    });\n  }\n\n  setFilter(filter: string) {\n    this._filter.set(filter);\n  }\n}\n```\n\n---\n\n## 9. Forms with Signals (Coming in v22+)\n\n### Current Reactive Forms\n\n```typescript\nimport { Component, inject } from \"@angular\u002Fcore\";\nimport { FormBuilder, Validators, ReactiveFormsModule } from \"@angular\u002Fforms\";\n\n@Component({\n  selector: \"app-user-form\",\n  standalone: true,\n  imports: [ReactiveFormsModule],\n  template: `\n    \u003Cform [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n      \u003Cinput formControlName=\"name\" placeholder=\"Name\" \u002F>\n      \u003Cinput formControlName=\"email\" type=\"email\" placeholder=\"Email\" \u002F>\n      \u003Cbutton [disabled]=\"form.invalid\">Submit\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  `,\n})\nexport class UserFormComponent {\n  private fb = inject(FormBuilder);\n\n  form = this.fb.group({\n    name: [\"\", Validators.required],\n    email: [\"\", [Validators.required, Validators.email]],\n  });\n\n  onSubmit() {\n    if (this.form.valid) {\n      console.log(this.form.value);\n    }\n  }\n}\n```\n\n### Signal-Aware Form Patterns (Preview)\n\n```typescript\n\u002F\u002F Future Signal Forms API (experimental)\nimport { Component, signal } from '@angular\u002Fcore';\n\n@Component({...})\nexport class SignalFormComponent {\n  name = signal('');\n  email = signal('');\n\n  \u002F\u002F Computed validation\n  isValid = computed(() =>\n    this.name().length > 0 &&\n    this.email().includes('@')\n  );\n\n  submit() {\n    if (this.isValid()) {\n      console.log({ name: this.name(), email: this.email() });\n    }\n  }\n}\n```\n\n---\n\n## 10. Performance Optimization\n\n### Change Detection Strategies\n\n```typescript\n@Component({\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  \u002F\u002F Only checks when:\n  \u002F\u002F 1. Input signal\u002Freference changes\n  \u002F\u002F 2. Event handler runs\n  \u002F\u002F 3. Async pipe emits\n  \u002F\u002F 4. Signal value changes\n})\n```\n\n### Defer Blocks for Lazy Loading\n\n```typescript\n@Component({\n  template: `\n    \u003C!-- Immediate loading -->\n    \u003Capp-header \u002F>\n\n    \u003C!-- Lazy load when visible -->\n    @defer (on viewport) {\n      \u003Capp-heavy-chart \u002F>\n    } @placeholder {\n      \u003Cdiv class=\"skeleton\" \u002F>\n    } @loading (minimum 200ms) {\n      \u003Capp-spinner \u002F>\n    } @error {\n      \u003Cp>Failed to load chart\u003C\u002Fp>\n    }\n  `\n})\n```\n\n### NgOptimizedImage\n\n```typescript\nimport { NgOptimizedImage } from '@angular\u002Fcommon';\n\n@Component({\n  imports: [NgOptimizedImage],\n  template: `\n    \u003Cimg\n      ngSrc=\"hero.jpg\"\n      width=\"800\"\n      height=\"600\"\n      priority\n    \u002F>\n\n    \u003Cimg\n      ngSrc=\"thumbnail.jpg\"\n      width=\"200\"\n      height=\"150\"\n      loading=\"lazy\"\n      placeholder=\"blur\"\n    \u002F>\n  `\n})\n```\n\n---\n\n## 11. Testing Modern Angular\n\n### Testing Signal Components\n\n```typescript\nimport { ComponentFixture, TestBed } from \"@angular\u002Fcore\u002Ftesting\";\nimport { CounterComponent } from \".\u002Fcounter.component\";\n\ndescribe(\"CounterComponent\", () => {\n  let component: CounterComponent;\n  let fixture: ComponentFixture\u003CCounterComponent>;\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [CounterComponent], \u002F\u002F Standalone import\n    }).compileComponents();\n\n    fixture = TestBed.createComponent(CounterComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it(\"should increment count\", () => {\n    expect(component.count()).toBe(0);\n\n    component.increment();\n\n    expect(component.count()).toBe(1);\n  });\n\n  it(\"should update DOM on signal change\", () => {\n    component.count.set(5);\n    fixture.detectChanges();\n\n    const el = fixture.nativeElement.querySelector(\".count\");\n    expect(el.textContent).toContain(\"5\");\n  });\n});\n```\n\n### Testing with Signal Inputs\n\n```typescript\nimport { ComponentFixture, TestBed } from \"@angular\u002Fcore\u002Ftesting\";\nimport { ComponentRef } from \"@angular\u002Fcore\";\nimport { UserCardComponent } from \".\u002Fuser-card.component\";\n\ndescribe(\"UserCardComponent\", () => {\n  let fixture: ComponentFixture\u003CUserCardComponent>;\n  let componentRef: ComponentRef\u003CUserCardComponent>;\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [UserCardComponent],\n    }).compileComponents();\n\n    fixture = TestBed.createComponent(UserCardComponent);\n    componentRef = fixture.componentRef;\n\n    \u002F\u002F Set signal inputs via setInput\n    componentRef.setInput(\"id\", \"123\");\n    componentRef.setInput(\"name\", \"John Doe\");\n\n    fixture.detectChanges();\n  });\n\n  it(\"should display user name\", () => {\n    const el = fixture.nativeElement.querySelector(\"h3\");\n    expect(el.textContent).toContain(\"John Doe\");\n  });\n});\n```\n\n---\n\n## Best Practices Summary\n\n| Pattern              | ✅ Do                          | ❌ Don't                        |\n| -------------------- | ------------------------------ | ------------------------------- |\n| **State**            | Use Signals for local state    | Overuse RxJS for simple state   |\n| **Components**       | Standalone with direct imports | Bloated SharedModules           |\n| **Change Detection** | OnPush + Signals               | Default CD everywhere           |\n| **Lazy Loading**     | `@defer` and `loadComponent`   | Eager load everything           |\n| **DI**               | `inject()` function            | Constructor injection (verbose) |\n| **Inputs**           | `input()` signal function      | `@Input()` decorator (legacy)   |\n| **Zoneless**         | Enable for new projects        | Force on legacy without testing |\n\n---\n\n## Resources\n\n- [Angular.dev Documentation](https:\u002F\u002Fangular.dev)\n- [Angular Signals Guide](https:\u002F\u002Fangular.dev\u002Fguide\u002Fsignals)\n- [Angular SSR Guide](https:\u002F\u002Fangular.dev\u002Fguide\u002Fssr)\n- [Angular Update Guide](https:\u002F\u002Fangular.dev\u002Fupdate-guide)\n- [Angular Blog](https:\u002F\u002Fblog.angular.dev)\n\n---\n\n## Common Troubleshooting\n\n| Issue                          | Solution                                            |\n| ------------------------------ | --------------------------------------------------- |\n| Signal not updating UI         | Ensure `OnPush` + call signal as function `count()` |\n| Hydration mismatch             | Check server\u002Fclient content consistency             |\n| Circular dependency            | Use `inject()` with `forwardRef`                    |\n| Zoneless not detecting changes | Trigger via signal updates, not mutations           |\n| SSR fetch fails                | Use `TransferState` or `withFetch()`                |\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,172,180,"2026-05-16 13:02:51",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"编程开发","coding","mdi-code-braces","代码生成、调试、审查，提升开发效率",2,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"前端开发","frontend","mdi-language-html5","HTML\u002FCSS\u002FJavaScript\u002F框架相关",1,96,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"c9515b06-6f6f-4aef-969b-475280cb6780","1.0.0","angular.zip",7887,"uploads\u002Fskills\u002F98a32458-1986-4f76-b7de-4515e71566da\u002Fangular.zip","8a1a868e64c12d8e534bccf42674fe1a4c1ac51eca4f4f2f3c2b0d8fbda5f87c","[{\"path\":\"README.md\",\"isDirectory\":false,\"size\":1400},{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":20467},{\"path\":\"metadata.json\",\"isDirectory\":false,\"size\":741}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]