{"id":"17070665-595d-48a3-b91d-5f5bacb6e497","shortId":"uHPpVw","kind":"skill","title":"angular-migration","tagline":"Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.","description":"# Angular Migration\n\nMaster AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.\n\n## Use this skill when\n\n- Migrating AngularJS (1.x) applications to Angular (2+)\n- Running hybrid AngularJS/Angular applications\n- Converting directives to components\n- Modernizing dependency injection\n- Migrating routing systems\n- Updating to latest Angular versions\n- Implementing Angular best practices\n\n## Do not use this skill when\n\n- You are not migrating from AngularJS to Angular\n- The app is already on a modern Angular version\n- You need only a small UI fix without framework changes\n\n## Instructions\n\n1. Assess the AngularJS codebase, dependencies, and migration risks.\n2. Choose a migration strategy (hybrid vs rewrite) and define milestones.\n3. Set up ngUpgrade and migrate modules, components, and routing.\n4. Validate with tests and plan a safe cutover.\n\n## Safety\n\n- Avoid big-bang cutovers without rollback and staging validation.\n- Keep hybrid compatibility testing during incremental migration.\n\n## Migration Strategies\n\n### 1. Big Bang (Complete Rewrite)\n- Rewrite entire app in Angular\n- Parallel development\n- Switch over at once\n- **Best for:** Small apps, green field projects\n\n### 2. Incremental (Hybrid Approach)\n- Run AngularJS and Angular side-by-side\n- Migrate feature by feature\n- ngUpgrade for interop\n- **Best for:** Large apps, continuous delivery\n\n### 3. Vertical Slice\n- Migrate one feature completely\n- New features in Angular, maintain old in AngularJS\n- Gradually replace\n- **Best for:** Medium apps, distinct features\n\n## Hybrid App Setup\n\n```typescript\n// main.ts - Bootstrap hybrid app\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { UpgradeModule } from '@angular/upgrade/static';\nimport { AppModule } from './app/app.module';\n\nplatformBrowserDynamic()\n  .bootstrapModule(AppModule)\n  .then(platformRef => {\n    const upgrade = platformRef.injector.get(UpgradeModule);\n    // Bootstrap AngularJS\n    upgrade.bootstrap(document.body, ['myAngularJSApp'], { strictDi: true });\n  });\n```\n\n```typescript\n// app.module.ts\nimport { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { UpgradeModule } from '@angular/upgrade/static';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    UpgradeModule\n  ]\n})\nexport class AppModule {\n  constructor(private upgrade: UpgradeModule) {}\n\n  ngDoBootstrap() {\n    // Bootstrapped manually in main.ts\n  }\n}\n```\n\n## Component Migration\n\n### AngularJS Controller → Angular Component\n```javascript\n// Before: AngularJS controller\nangular.module('myApp').controller('UserController', function($scope, UserService) {\n  $scope.user = {};\n\n  $scope.loadUser = function(id) {\n    UserService.getUser(id).then(function(user) {\n      $scope.user = user;\n    });\n  };\n\n  $scope.saveUser = function() {\n    UserService.saveUser($scope.user);\n  };\n});\n```\n\n```typescript\n// After: Angular component\nimport { Component, OnInit } from '@angular/core';\nimport { UserService } from './user.service';\n\n@Component({\n  selector: 'app-user',\n  template: `\n    <div>\n      <h2>{{ user.name }}</h2>\n      <button (click)=\"saveUser()\">Save</button>\n    </div>\n  `\n})\nexport class UserComponent implements OnInit {\n  user: any = {};\n\n  constructor(private userService: UserService) {}\n\n  ngOnInit() {\n    this.loadUser(1);\n  }\n\n  loadUser(id: number) {\n    this.userService.getUser(id).subscribe(user => {\n      this.user = user;\n    });\n  }\n\n  saveUser() {\n    this.userService.saveUser(this.user);\n  }\n}\n```\n\n### AngularJS Directive → Angular Component\n```javascript\n// Before: AngularJS directive\nangular.module('myApp').directive('userCard', function() {\n  return {\n    restrict: 'E',\n    scope: {\n      user: '=',\n      onDelete: '&'\n    },\n    template: `\n      <div class=\"card\">\n        <h3>{{ user.name }}</h3>\n        <button ng-click=\"onDelete()\">Delete</button>\n      </div>\n    `\n  };\n});\n```\n\n```typescript\n// After: Angular component\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\n\n@Component({\n  selector: 'app-user-card',\n  template: `\n    <div class=\"card\">\n      <h3>{{ user.name }}</h3>\n      <button (click)=\"delete.emit()\">Delete</button>\n    </div>\n  `\n})\nexport class UserCardComponent {\n  @Input() user: any;\n  @Output() delete = new EventEmitter<void>();\n}\n\n// Usage: <app-user-card [user]=\"user\" (delete)=\"handleDelete()\"></app-user-card>\n```\n\n## Service Migration\n\n```javascript\n// Before: AngularJS service\nangular.module('myApp').factory('UserService', function($http) {\n  return {\n    getUser: function(id) {\n      return $http.get('/api/users/' + id);\n    },\n    saveUser: function(user) {\n      return $http.post('/api/users', user);\n    }\n  };\n});\n```\n\n```typescript\n// After: Angular service\nimport { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable } from 'rxjs';\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class UserService {\n  constructor(private http: HttpClient) {}\n\n  getUser(id: number): Observable<any> {\n    return this.http.get(`/api/users/${id}`);\n  }\n\n  saveUser(user: any): Observable<any> {\n    return this.http.post('/api/users', user);\n  }\n}\n```\n\n## Dependency Injection Changes\n\n### Downgrading Angular → AngularJS\n```typescript\n// Angular service\nimport { Injectable } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class NewService {\n  getData() {\n    return 'data from Angular';\n  }\n}\n\n// Make available to AngularJS\nimport { downgradeInjectable } from '@angular/upgrade/static';\n\nangular.module('myApp')\n  .factory('newService', downgradeInjectable(NewService));\n\n// Use in AngularJS\nangular.module('myApp').controller('OldController', function(newService) {\n  console.log(newService.getData());\n});\n```\n\n### Upgrading AngularJS → Angular\n```typescript\n// AngularJS service\nangular.module('myApp').factory('oldService', function() {\n  return {\n    getData: function() {\n      return 'data from AngularJS';\n    }\n  };\n});\n\n// Make available to Angular\nimport { InjectionToken } from '@angular/core';\n\nexport const OLD_SERVICE = new InjectionToken<any>('oldService');\n\n@NgModule({\n  providers: [\n    {\n      provide: OLD_SERVICE,\n      useFactory: (i: any) => i.get('oldService'),\n      deps: ['$injector']\n    }\n  ]\n})\n\n// Use in Angular\n@Component({...})\nexport class NewComponent {\n  constructor(@Inject(OLD_SERVICE) private oldService: any) {\n    console.log(this.oldService.getData());\n  }\n}\n```\n\n## Routing Migration\n\n```javascript\n// Before: AngularJS routing\nangular.module('myApp').config(function($routeProvider) {\n  $routeProvider\n    .when('/users', {\n      template: '<user-list></user-list>'\n    })\n    .when('/users/:id', {\n      template: '<user-detail></user-detail>'\n    });\n});\n```\n\n```typescript\n// After: Angular routing\nimport { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nconst routes: Routes = [\n  { path: 'users', component: UserListComponent },\n  { path: 'users/:id', component: UserDetailComponent }\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes)],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule {}\n```\n\n## Forms Migration\n\n```html\n<!-- Before: AngularJS -->\n<form name=\"userForm\" ng-submit=\"saveUser()\">\n  <input type=\"text\" ng-model=\"user.name\" required>\n  <input type=\"email\" ng-model=\"user.email\" required>\n  <button ng-disabled=\"userForm.$invalid\">Save</button>\n</form>\n```\n\n```typescript\n// After: Angular (Template-driven)\n@Component({\n  template: `\n    <form #userForm=\"ngForm\" (ngSubmit)=\"saveUser()\">\n      <input type=\"text\" [(ngModel)]=\"user.name\" name=\"name\" required>\n      <input type=\"email\" [(ngModel)]=\"user.email\" name=\"email\" required>\n      <button [disabled]=\"userForm.invalid\">Save</button>\n    </form>\n  `\n})\n\n// Or Reactive Forms (preferred)\nimport { FormBuilder, FormGroup, Validators } from '@angular/forms';\n\n@Component({\n  template: `\n    <form [formGroup]=\"userForm\" (ngSubmit)=\"saveUser()\">\n      <input formControlName=\"name\">\n      <input formControlName=\"email\">\n      <button [disabled]=\"userForm.invalid\">Save</button>\n    </form>\n  `\n})\nexport class UserFormComponent {\n  userForm: FormGroup;\n\n  constructor(private fb: FormBuilder) {\n    this.userForm = this.fb.group({\n      name: ['', Validators.required],\n      email: ['', [Validators.required, Validators.email]]\n    });\n  }\n\n  saveUser() {\n    console.log(this.userForm.value);\n  }\n}\n```\n\n## Migration Timeline\n\n```\nPhase 1: Setup (1-2 weeks)\n- Install Angular CLI\n- Set up hybrid app\n- Configure build tools\n- Set up testing\n\nPhase 2: Infrastructure (2-4 weeks)\n- Migrate services\n- Migrate utilities\n- Set up routing\n- Migrate shared components\n\nPhase 3: Feature Migration (varies)\n- Migrate feature by feature\n- Test thoroughly\n- Deploy incrementally\n\nPhase 4: Cleanup (1-2 weeks)\n- Remove AngularJS code\n- Remove ngUpgrade\n- Optimize bundle\n- Final testing\n```\n\n## Resources\n\n- **references/hybrid-mode.md**: Hybrid app patterns\n- **references/component-migration.md**: Component conversion guide\n- **references/dependency-injection.md**: DI migration strategies\n- **references/routing.md**: Routing migration\n- **assets/hybrid-bootstrap.ts**: Hybrid app template\n- **assets/migration-timeline.md**: Project planning\n- **scripts/analyze-angular-app.sh**: App analysis script\n\n## Best Practices\n\n1. **Start with Services**: Migrate services first (easier)\n2. **Incremental Approach**: Feature-by-feature migration\n3. **Test Continuously**: Test at every step\n4. **Use TypeScript**: Migrate to TypeScript early\n5. **Follow Style Guide**: Angular style guide from day 1\n6. **Optimize Later**: Get it working, then optimize\n7. **Document**: Keep migration notes\n\n## Common Pitfalls\n\n- Not setting up hybrid app correctly\n- Migrating UI before logic\n- Ignoring change detection differences\n- Not handling scope properly\n- Mixing patterns (AngularJS + Angular)\n- Inadequate testing\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.","tags":["angular","migration","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows"],"capabilities":["skill","source-sickn33","skill-angular-migration","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/angular-migration","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34964 github stars · SKILL.md body (10,336 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-25T00:50:25.992Z","embedding":null,"createdAt":"2026-04-18T21:30:58.167Z","updatedAt":"2026-04-25T00:50:25.992Z","lastSeenAt":"2026-04-25T00:50:25.992Z","tsv":"'-2':764,812 '-4':783 '/api/users':467,474,508,516 '/app/app.module':256 '/user.service':347 '/users':641,644 '1':44,107,166,372,761,763,811,852,891 '2':49,116,189,780,782,860 '3':127,214,796,868 '4':137,809,875 '5':882 '6':892 '7':900 'alreadi':90 'analysi':848 'angular':2,7,20,25,48,67,70,86,94,175,196,224,307,337,387,409,478,522,525,541,569,588,614,649,687,767,886,928 'angular-migr':1 'angular.module':313,393,455,550,559,573,634 'angular/common/http':487 'angular/core':278,343,417,483,530,592,654 'angular/forms':727 'angular/platform-browser':282 'angular/platform-browser-dynamic':248 'angular/router':659 'angular/upgrade/static':252,286,549 'angularj':5,23,43,84,110,194,228,267,305,311,385,391,453,523,545,558,568,571,584,632,815,927 'angularjs/angular':52 'app':11,29,88,173,185,211,234,238,244,351,421,442,772,826,841,847,911 'app-us':350 'app-user-card':420,441 'app.module.ts':274 'applic':46,53 'appmodul':254,259,293 'approach':192,862 'approutingmodul':680 'ask':964 'assess':108 'assets/hybrid-bootstrap.ts':839 'assets/migration-timeline.md':843 'avail':543,586 'avoid':147 'bang':150,168 'best':71,182,208,231,850 'big':149,167 'big-bang':148 'bootstrap':242,266,299 'bootstrapmodul':258 'boundari':972 'browsermodul':280,289 'build':774 'bundl':820 'button':355,426,714,735 'card':423,444 'chang':16,34,105,520,918 'choos':117 'clarif':966 'class':292,360,431,496,535,617,679,740 'cleanup':810 'clear':939 'cli':768 'click':356,427 'code':816 'codebas':111 'common':905 'compat':159 'complet':169,220 'compon':12,30,57,134,303,308,338,340,348,388,410,412,418,615,665,670,691,728,794,829 'config':636 'configur':773 'console.log':565,626,756 'const':262,594,660 'constructor':294,366,498,619,744 'continu':212,870 'control':306,312,315,561 'convers':13,31,830 'convert':54 'correct':912 'criteria':975 'cutov':145,151 'data':539,582 'day':890 'defin':125 'delet':406,429,437,447 'delete.emit':428 'deliveri':213 'dep':610 'depend':14,32,59,112,518 'deploy':806 'describ':943 'detect':919 'develop':177 'di':833 'differ':920 'direct':55,386,392,395 'disabl':715,736 'distinct':235 'document':901 'document.body':269 'downgrad':521 'downgradeinject':547,554 'driven':690 'e':400 'earli':881 'easier':859 'email':708,712,752 'entir':172 'environ':955 'environment-specif':954 'eventemitt':415,439 'everi':873 'expert':960 'export':291,359,430,495,534,593,616,676,678,739 'factori':457,552,575 'fb':746 'featur':202,204,219,222,236,797,801,803,864,866 'feature-by-featur':863 'field':187 'final':821 'first':858 'fix':102 'follow':883 'form':681,693,720,730 'formbuild':723,747 'formgroup':724,731,743 'framework':104 'function':317,322,327,332,397,459,463,470,563,577,580,637 'get':895 'getdata':537,579 'getus':462,502 'gradual':229 'green':186 'guid':831,885,888 'handl':922 'handledelet':448 'html':683 'http':460,500 'http.get':466 'http.post':473 'httpclient':485,501 'hybrid':10,28,51,121,158,191,237,243,771,825,840,910 'i.get':608 'id':323,325,374,377,464,468,503,509,645,669 'ignor':917 'implement':69,362 'import':245,249,253,275,279,283,288,339,344,411,480,484,488,527,546,589,651,655,673,722 'inadequ':929 'includ':9,27 'increment':162,190,807,861 'infrastructur':781 'inject':15,33,60,481,492,519,528,531,620 'injectiontoken':590,598 'injector':611 'input':413,433,698,706,969 'instal':766 'instruct':106 'interop':207 'javascript':309,389,451,630 'keep':157,902 'larg':210 'later':894 'latest':66 'limit':931 'loadus':373 'logic':916 'main.ts':241,302 'maintain':225 'make':542,585 'manual':300 'master':4,22 'match':940 'medium':233 'migrat':3,8,19,21,26,37,42,61,82,114,119,132,163,164,201,217,304,450,629,682,758,785,787,792,798,800,834,838,856,867,878,903,913 'mileston':126 'miss':977 'mix':925 'modern':58,93 'modul':133 'myangularjsapp':270 'myapp':314,394,456,551,560,574,635 'name':703,704,711,750 'need':97 'new':221,438,597 'newcompon':618 'newservic':536,553,555,564 'newservice.getdata':566 'ngdobootstrap':298 'ngform':695 'ngmodel':701,709 'ngmodul':276,287,600,652,672 'ngoninit':370 'ngsubmit':696,733 'ngupgrad':130,205,818 'note':904 'number':375,504 'observ':489,505,513 'old':226,595,603,621 'oldcontrol':562 'oldservic':576,599,609,624 'ondelet':403 'one':218 'oninit':341,363 'optim':819,893,899 'output':414,436,949 'parallel':176 'path':663,667 'pattern':827,926 'permiss':970 'phase':760,779,795,808 'pitfal':906 'plan':142,845 'platformbrowserdynam':246,257 'platformref':261 'platformref.injector.get':264 'practic':72,851 'prefer':721 'privat':295,367,499,623,745 'project':188,844 'proper':924 'provid':601,602 'providedin':493,532 'reactiv':719 'references/component-migration.md':828 'references/dependency-injection.md':832 'references/hybrid-mode.md':824 'references/routing.md':836 'remov':814,817 'replac':230 'requir':705,713,968 'resourc':823 'restrict':399 'return':398,461,465,472,506,514,538,578,581 'review':961 'rewrit':123,170,171 'risk':115 'rollback':153 'root':494,533 'rout':18,36,62,136,628,633,650,657,661,662,675,791,837 'routeprovid':638,639 'routermodul':656,677 'routermodule.forroot':674 'run':50,193 'rxjs':491 'safe':144 'safeti':146,971 'save':358,684,717,738 'saveus':357,382,469,510,697,734,755 'scope':318,401,923,942 'scope.loaduser':321 'scope.saveuser':331 'scope.user':320,329,334 'script':849 'scripts/analyze-angular-app.sh':846 'selector':349,419 'servic':449,454,479,526,572,596,604,622,786,855,857 'set':128,769,776,789,908 'setup':239,762 'share':793 'side':198,200 'side-by-sid':197 'skill':40,77,934 'skill-angular-migration' 'slice':216 'small':100,184 'source-sickn33' 'specif':956 'stage':155 'start':853 'step':874 'stop':962 'strategi':120,165,835 'strictdi':271 'style':884,887 'subscrib':378 'substitut':952 'success':974 'switch':178 'system':63 'task':938 'templat':353,404,424,642,646,689,692,729,842 'template-driven':688 'test':140,160,778,804,822,869,871,930,958 'text':700 'this.fb.group':749 'this.http.get':507 'this.http.post':515 'this.loaduser':371 'this.oldservice.getdata':627 'this.user':380,384 'this.userform':748 'this.userform.value':757 'this.userservice.getuser':376 'this.userservice.saveuser':383 'thorough':805 'timelin':759 'tool':775 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'treat':947 'true':272 'type':699,707 'typescript':240,273,335,407,476,524,570,647,685,877,880 'ui':101,914 'updat':64 'upgrad':263,296,567 'upgrade.bootstrap':268 'upgrademodul':250,265,284,290,297 'usag':440 'use':38,75,556,612,876,932 'usefactori':605 'user':328,330,352,364,379,381,402,422,434,443,445,446,471,475,511,517,664,668 'user.email':710 'user.name':354,405,425,702 'usercard':396 'usercardcompon':432 'usercompon':361 'usercontrol':316 'userdetailcompon':671 'userform':694,732,742 'userform.invalid':716,737 'userformcompon':741 'userlistcompon':666 'userservic':319,345,368,369,458,497 'userservice.getuser':324 'userservice.saveuser':333 'util':788 'valid':138,156,725,957 'validators.email':754 'validators.required':751,753 'vari':799 'version':68,95 'vertic':215 'vs':122 'week':765,784,813 'without':103,152 'work':897 'x':45","prices":[{"id":"f73b182e-dace-45ec-92e4-17053f994018","listingId":"17070665-595d-48a3-b91d-5f5bacb6e497","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:30:58.167Z"}],"sources":[{"listingId":"17070665-595d-48a3-b91d-5f5bacb6e497","source":"github","sourceId":"sickn33/antigravity-awesome-skills/angular-migration","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/angular-migration","isPrimary":false,"firstSeenAt":"2026-04-18T21:30:58.167Z","lastSeenAt":"2026-04-25T00:50:25.992Z"}],"details":{"listingId":"17070665-595d-48a3-b91d-5f5bacb6e497","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"angular-migration","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34964,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-24T06:41:17Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"5b9f4d68be04224d8982ab70f6cb2cb277f4e7fa","skill_md_path":"skills/angular-migration/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/angular-migration"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"angular-migration","description":"Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration."},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/angular-migration"},"updatedAt":"2026-04-25T00:50:25.992Z"}}