Agent Skills by ALSEL
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 修正のみが必要である

手順

  1. AngularJS コードベース、依存関係、マイグレーションリスクを評価します。
  2. マイグレーション戦略(ハイブリッド vs 書き換え)を選択し、マイルストーンを定義します。
  3. ngUpgrade をセットアップし、モジュール、コンポーネント、ルーティングをマイグレーションします。
  4. テストで検証し、安全なカットオーバーを計画します。

安全性

  • ロールバックとステージング検証なしで一括カットオーバーを避けてください。
  • 段階的マイグレーション中、ハイブリッド互換性テストを継続してください。

マイグレーション戦略

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: アプリ分析スクリプト

ベストプラクティス

  1. サービスから開始: サービスを最初にマイグレーション(簡単)
  2. 段階的アプローチ: 機能ごとにマイグレーション
  3. 継続的なテスト: 各ステップでテスト
  4. TypeScript を使用: 早期に TypeScript に移行
  5. スタイルガイドに従う: 初日から Angular スタイルガイドに従う
  6. 最適化は後で: まず動作させ、その後最適化
  7. ドキュメント化: マイグレーションノートを記録

よくある落とし穴

  • ハイブリッドアプリを正しくセットアップしない
  • ロジックの前に UI をマイグレーション
  • 変更検出の違いを無視
  • スコープを適切に処理しない
  • パターンを混在させる(AngularJS + Angular)
  • テストが不十分

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
henryalouf
リポジトリ
henryalouf/ruflow
ライセンス
MIT
最終更新
2026/4/30

Source: https://github.com/henryalouf/ruflow / ライセンス: MIT

本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: henryalouf · henryalouf/ruflow · ライセンス: MIT