OpenAIソフトウェア開発⭐ リポ 6品質スコア 68/100
angular-migration
AngularJSからAngularへの移行に対応し、ハイブリッドアプリ、コンポーネント変換、依存性注入の変更、ルーティング移行など、幅広い移行シナリオに対応できます。
description の原文を見る
Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.
SKILL.md 本文
Angular マイグレーション
ハイブリッドアプリ、コンポーネント変換、依存性注入の変更、ルーティングマイグレーションを含む、AngularJS から Angular へのマイグレーションをマスターします。
このスキルを使用する場合
- AngularJS (1.x) アプリケーションを Angular (2+) にマイグレーションする
- ハイブリッド AngularJS/Angular アプリケーションを実行する
- ディレクティブをコンポーネントに変換する
- 依存性注入を最新化する
- ルーティングシステムをマイグレーションする
- 最新の Angular バージョンに更新する
- Angular のベストプラクティスを実装する
このスキルを使用しない場合
- AngularJS から Angular へマイグレーションしていない
- アプリが既に最新の Angular バージョンである
- フレームワーク変更なしで小さな UI 修正のみが必要である
手順
- AngularJS コードベース、依存関係、マイグレーションリスクを評価します。
- マイグレーション戦略(ハイブリッド vs 書き換え)を選択し、マイルストーンを定義します。
- ngUpgrade をセットアップし、モジュール、コンポーネント、ルーティングをマイグレーションします。
- テストで検証し、安全なカットオーバーを計画します。
安全性
- ロールバックとステージング検証なしで一括カットオーバーを避けてください。
- 段階的マイグレーション中、ハイブリッド互換性テストを継続してください。
マイグレーション戦略
1. ビッグバン(完全書き換え)
- Angular でアプリ全体を書き換え
- 並列開発
- 一度に切り替え
- 適合: 小規模アプリ、グリーンフィールドプロジェクト
2. 段階的(ハイブリッドアプローチ)
- AngularJS と Angular を並行実行
- 機能ごとにマイグレーション
- 相互運用のための ngUpgrade
- 適合: 大規模アプリ、継続的デリバリー
3. 垂直スライス
- 1 つの機能を完全にマイグレーション
- 新機能は Angular で、旧機能は AngularJS で保持
- 段階的に置き換え
- 適合: 中規模アプリ、個別の機能
ハイブリッドアプリセットアップ
// main.ts - ハイブリッドアプリのブートストラップ
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app/app.module';
platformBrowserDynamic()
.bootstrapModule(AppModule)
.then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule);
// AngularJS のブートストラップ
upgrade.bootstrap(document.body, ['myAngularJSApp'], { strictDi: true });
});
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';
@NgModule({
imports: [
BrowserModule,
UpgradeModule
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) {}
ngDoBootstrap() {
// main.ts で手動でブートストラップされます
}
}
コンポーネントマイグレーション
AngularJS コントローラ → Angular コンポーネント
// 変更前: AngularJS コントローラ
angular.module('myApp').controller('UserController', function($scope, UserService) {
$scope.user = {};
$scope.loadUser = function(id) {
UserService.getUser(id).then(function(user) {
$scope.user = user;
});
};
$scope.saveUser = function() {
UserService.saveUser($scope.user);
};
});
// 変更後: Angular コンポーネント
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user',
template: `
<div>
<h2>{{ user.name }}</h2>
<button (click)="saveUser()">Save</button>
</div>
`
})
export class UserComponent implements OnInit {
user: any = {};
constructor(private userService: UserService) {}
ngOnInit() {
this.loadUser(1);
}
loadUser(id: number) {
this.userService.getUser(id).subscribe(user => {
this.user = user;
});
}
saveUser() {
this.userService.saveUser(this.user);
}
}
AngularJS ディレクティブ → Angular コンポーネント
// 変更前: AngularJS ディレクティブ
angular.module('myApp').directive('userCard', function() {
return {
restrict: 'E',
scope: {
user: '=',
onDelete: '&'
},
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<button ng-click="onDelete()">Delete</button>
</div>
`
};
});
// 変更後: Angular コンポーネント
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<button (click)="delete.emit()">Delete</button>
</div>
`
})
export class UserCardComponent {
@Input() user: any;
@Output() delete = new EventEmitter<void>();
}
// 使用法: <app-user-card [user]="user" (delete)="handleDelete()"></app-user-card>
サービスマイグレーション
// 変更前: AngularJS サービス
angular.module('myApp').factory('UserService', function($http) {
return {
getUser: function(id) {
return $http.get('/api/users/' + id);
},
saveUser: function(user) {
return $http.post('/api/users', user);
}
};
});
// 変更後: Angular サービス
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {}
getUser(id: number): Observable<any> {
return this.http.get(`/api/users/${id}`);
}
saveUser(user: any): Observable<any> {
return this.http.post('/api/users', user);
}
}
依存性注入の変更
Angular → AngularJS へダウングレード
// Angular サービス
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class NewService {
getData() {
return 'data from Angular';
}
}
// AngularJS で使用可能にする
import { downgradeInjectable } from '@angular/upgrade/static';
angular.module('myApp')
.factory('newService', downgradeInjectable(NewService));
// AngularJS で使用
angular.module('myApp').controller('OldController', function(newService) {
console.log(newService.getData());
});
AngularJS → Angular へアップグレード
// AngularJS サービス
angular.module('myApp').factory('oldService', function() {
return {
getData: function() {
return 'data from AngularJS';
}
};
});
// Angular で使用可能にする
import { InjectionToken } from '@angular/core';
export const OLD_SERVICE = new InjectionToken<any>('oldService');
@NgModule({
providers: [
{
provide: OLD_SERVICE,
useFactory: (i: any) => i.get('oldService'),
deps: ['$injector']
}
]
})
// Angular で使用
@Component({...})
export class NewComponent {
constructor(@Inject(OLD_SERVICE) private oldService: any) {
console.log(this.oldService.getData());
}
}
ルーティングマイグレーション
// 変更前: AngularJS ルーティング
angular.module('myApp').config(function($routeProvider) {
$routeProvider
.when('/users', {
template: '<user-list></user-list>'
})
.when('/users/:id', {
template: '<user-detail></user-detail>'
});
});
// 変更後: Angular ルーティング
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
フォームマイグレーション
<!-- 変更前: AngularJS -->
<form name="userForm" ng-submit="saveUser()">
<input type="text" ng-model="user.name" required>
<input type="email" ng-model="user.email" required>
<button ng-disabled="userForm.$invalid">Save</button>
</form>
// 変更後: Angular (テンプレート駆動)
@Component({
template: `
<form #userForm="ngForm" (ngSubmit)="saveUser()">
<input type="text" [(ngModel)]="user.name" name="name" required>
<input type="email" [(ngModel)]="user.email" name="email" required>
<button [disabled]="userForm.invalid">Save</button>
</form>
`
})
// またはリアクティブフォーム(推奨)
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
template: `
<form [formGroup]="userForm" (ngSubmit)="saveUser()">
<input formControlName="name">
<input formControlName="email">
<button [disabled]="userForm.invalid">Save</button>
</form>
`
})
export class UserFormComponent {
userForm: FormGroup;
constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
}
saveUser() {
console.log(this.userForm.value);
}
}
マイグレーションタイムライン
フェーズ 1: セットアップ(1~2 週間)
- Angular CLI をインストール
- ハイブリッドアプリをセットアップ
- ビルドツールを構成
- テストをセットアップ
フェーズ 2: インフラストラクチャ(2~4 週間)
- サービスをマイグレーション
- ユーティリティをマイグレーション
- ルーティングをセットアップ
- 共有コンポーネントをマイグレーション
フェーズ 3: 機能マイグレーション(変動)
- 機能ごとにマイグレーション
- 徹底的にテスト
- 段階的にデプロイ
フェーズ 4: クリーンアップ(1~2 週間)
- AngularJS コードを削除
- ngUpgrade を削除
- バンドルを最適化
- 最終テストを実施
リソース
- references/hybrid-mode.md: ハイブリッドアプリのパターン
- references/component-migration.md: コンポーネント変換ガイド
- references/dependency-injection.md: DI マイグレーション戦略
- references/routing.md: ルーティングマイグレーション
- assets/hybrid-bootstrap.ts: ハイブリッドアプリテンプレート
- assets/migration-timeline.md: プロジェクト計画
- scripts/analyze-angular-app.sh: アプリ分析スクリプト
ベストプラクティス
- サービスから開始: サービスを最初にマイグレーション(簡単)
- 段階的アプローチ: 機能ごとにマイグレーション
- 継続的なテスト: 各ステップでテスト
- TypeScript を使用: 早期に TypeScript に移行
- スタイルガイドに従う: 初日から Angular スタイルガイドに従う
- 最適化は後で: まず動作させ、その後最適化
- ドキュメント化: マイグレーションノートを記録
よくある落とし穴
- ハイブリッドアプリを正しくセットアップしない
- ロジックの前に UI をマイグレーション
- 変更検出の違いを無視
- スコープを適切に処理しない
- パターンを混在させる(AngularJS + Angular)
- テストが不十分
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- henryalouf
- リポジトリ
- henryalouf/ruflow
- ライセンス
- MIT
- 最終更新
- 2026/4/30
Source: https://github.com/henryalouf/ruflow / ライセンス: MIT