web-dev-qa-db-ja.com

angular / angularjsハイブリッドアプリケーションでdowngradeInjectableと組み合わせてdowngradeModuleを使用するとエラーが発生する

angular 5.0の場合、アップグレードモジュールには、angularゾーンの外でangularjsを実行するdowngradeModuleを使用するオプションがあります。これを試してみると問題が発生しましたdowngradeInjectableを使用します。

エラーが表示されます:

キャッチされないエラー:Angularモジュールをブートストラップする前にAngularインジェクタを取得しようとしています。

Bootstrapping angular in angular jsは正常に動作します

import 'zone.js/dist/zone.js';
import * as angular from 'angular';
/**
 * Angular bootstrapping
 */
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { decorateModuleRef } from 'src/environment';
import { AppModule } from 'src/app/app.module';
import { downgradeModule } from '@angular/upgrade/static';

export const bootstrapFn = ( extraProviders ) => {
    const platformRef = platformBrowserDynamic( extraProviders );
    return platformRef
        .bootstrapModule( AppModule )
        .then( decorateModuleRef );
};

angular.module( 'app.bootstrap', [
    downgradeModule( bootstrapFn ),
] );

ただし...

Angularjsが初期化された後にブートストラップが行われるため、ダウングレードインジェクタブルを機能させることができなくなります。

ダウングレードするサービス

import { Injectable, Inject, OnInit } from '@angular/core';

@Injectable()
export class MobileService implements OnInit{
    constructor(
        @Inject( 'angularjsDependency1' ) public angularjsDependency1 : any,
        @Inject( 'angularjsDependency2' ) public angularjsDependency2 : any,
    ) {}

}

注射可能なダウングレードの試み

import * as angular from 'angular';
import { downgradeInjectable } from '@angular/upgrade/static';
import { MyService } from 'src/services/myService/myService';

export const myServiceDowngraded = angular.module( 'services.mobileService', [
    angularjsDependency1,
    angularjsDependency2,
] )

.factory(
    'mobileService',
    downgradeInjectable( MyService ),
).name;

「downgradeInjectable(MyService)を実行すると、angularがまだ起動されていないため、angularインジェクタはまだ使用できません。したがって、エラー:

キャッチされないエラー:Angularモジュールをブートストラップする前にAngularインジェクタを取得しようとしています。

誰かが私がこれをどのように修正するか考えていますか?

15
theOwl

注:以下の回答は、angular 1.xをangularjsとして呼び出し、すべてのangular 2+バージョンを単にangularとして呼び出します。 =

上記のJGoodgiveの答えを拡張すると、基本的に、downgradeModuleを使用している場合、angularモジュールは、最初のangularコンポーネント。それまでは、angularモジュールが初期化されていないため、downgradeInjectable、それらのサービスも利用できません。

回避策は、angularモジュールのブートストラップをできるだけ早く強制することです。これには、単純なコンポーネントが必要です:

import {Component} from '@angular/core';

@Component({
  selector: 'service-bootstrap'
  template: ''
})
export class ServiceBootstrapComponent {}

このコンポーネントは何もしません。次に、このコンポーネントをトップレベルで宣言しますangularモジュール。

@NgModule({
  // ...providers, imports etc.
  declarations: [
    // ... existing declarations
    ServiceBootstrapComponent
  ],
  entryComponents: [
    // ... existing entry components
    ServiceBootstrapComponent
  ]
})
export class MyAngularModule {}

次に、このコンポーネントのダウングレードしたバージョンをangularjsモジュールに追加する必要もあります。 (私はこれを私が持っていたトップレベルのangularjsモジュールに追加しました)

angular.module('MyAngularJSModule', [
  // ...existing imports
])
.directive(
  'serviceBootstrap',
  downgradeComponent({ component: ServiceBootstrapComponent }) as angular.IDirectiveFactory
)

最後に、このコンポーネントをindex.htmlに入れます。

<body>
  <service-bootstrap></service-bootstrap>
  <!-- existing body contents -->
</body>

Angularjsがマークアップでそのコンポーネントを見つけると、そのコンポーネントをレンダリングできるようにangularモジュールを初期化する必要があります。これの意図された副作用は、プロバイダーなども初期化されて利用可能になることです通常使用できるdowngradeInjectableと組み合わせて使用​​します。

7
Akash Agrawal

これはangular githubスレッドで指摘されました。

https://github.com/angular/angular/issues/16491#issuecomment-343021511

George Kalpakasの応答:

明確にするために:downgradeInjectable()をdowngradeModule()で使用できますが、特定の制限があります。特に、Angularがブートストラップされるまで、ダウングレードされたインジェクタブルをインジェクトすることはできません。そしてAngularが(非同期に)初めてブートストラップされるとき、ダウングレードされたコンポーネントがしたがって、ダウングレードされたコンポーネント内(つまり、アップグレードされたAngularJSコンポーネント内)でのみ、ダウングレードされたサービスを安全に使用できます。

私はこれが十分に制限していることを知っているので、downgradeInjectable()をまったく使用しないことに決めるかもしれません-できることとできないことをより明確にしたかっただけです。

UpgradeModuleでアップグレードされた注射を使用する場合、同等の制限が当てはまることに注意してください。AngularJSがブートストラップされるまで使用できません。ただし、AngularJSは通常、AngularモジュールのngDoBootstrap()メソッドとAngularJS(Angularとは異なり)のブートストラップで同期的にブートストラップされるため、この制限は通常は気付かれません。

6
theOwl

このスレッドの回答は解決策を見つけるのに役立ちましたが、聖杯は含まれていません。

  1. AngularJSとは異なり、Angularは非同期で読み込まれるため、アプリのコードを除いてservice-boostrapコンポーネントを作成することはできません。同じエラーTrying to get the Angular injector before bootstrapping an Angular module.が発生します
  2. AngularJSコードをラップしたservice-bootstrapコンポーネントを作成するとうまくいきましたが、- githubのこの問題 で説明されているように、Angular composants)内の変更検出に関する問題が発生しました=。
  3. Githubの問題で、誰かが@angular/upgradeソースコードを編集してfalsetrueに変更し、コンポーネントをゾーンに強制的に作成するよう提案しました。しかし、この場合はパフォーマンスの問題が発生するようです(ユーザーイベントでngZoneのコードが複数回起動されたようです)。
  4. アプリが正しく機能するために、私は:を必要としました
    1. Angularコンポーネントを含むAngularJSコンポーネントを含むngコンポーネントは必要ありません。Angularコンポーネントを含むAngularJSのみが必要です。
    2. Angularサービスを使用するAngularJSコンポーネントが最初のangularコンポーネント、service-bootstrap

これを実現するために、少し変更したservice-bootstrapコンポーネントを作成しました。

import { Component, Output, EventEmitter, AfterViewInit } from "@angular/core";

@Component({
    selector: 'service-bootstrap',
    template: ``
})
export class ServiceBootstrapComponent implements AfterViewInit{
    @Output()
    public initialized: EventEmitter<void> = new EventEmitter();

    public ngAfterViewInit(){
        this.initialized.emit();
    }
}

AngularモジュールでこのコンポーネントをentryComponentとして宣言し、downgradeComponentを呼び出してAngularJSに登録します。

import { downgradeModule, downgradeInjectable, downgradeComponent } from '@angular/upgrade/static';

const bootstrapFn = (extraProviders: StaticProvider[]) => {
    const platformRef = platformBrowserDynamic(extraProviders);
    return platformRef.bootstrapModule(AppModule);
};

const downgradedModule = downgradeModule(bootstrapFn);

const app = angular.module('ngApp', [
    downgradedModule,
    'app'
]);

app.directive('serviceBootstrap', downgradeComponent({ component: ServiceBootstrapComponent }));

次に(そして魔法がここで発生します)、新しいAngularJSコンポーネントを作成しました。

angular.module("app")
    .directive('ng1App', ng1AppDirective);

function ng1AppDirective(){
    return {
        template: `
            <service-bootstrap (initialized)="onInit()"></service-bootstrap>
            <section ng-if="initialized">
              <!-- Your previous app's code here -->
            </section>
        `,
        controller: ng1AppController,
        scope: {},
    };
}

ng1AppController.$inject = ['$scope'];
function ng1AppController($scope){
    $scope.onInit = onInit;
    $scope.initialized = false;

    function onInit(){
        $scope.initialized = true;
    }
}

次に、私のindex.htmlはこのコンポーネントのみを参照しました

<body>
  <ng1-app></ng1-app>
</body>

このアプローチでは、AngularJSコンポーネントをAngularコンポーネント(Angularコンポーネント)の変更検出を壊します)内にネストしていませんが、最初の= Angularコンポーネントは、Angularプロバイダーにアクセスする前にロードされます。

5
ghusse

私も同じ問題を抱えていましたが、その理由は上の答えで説明されています。

ダウングレードされたangular $ injector を使用してサービスを動的に注入することにより、これを修正しました。

手順

  • ダウングレードしたサービスをangularjsモジュールに登録します

    angular.module('moduleName', dependencies)    
    angular.factory('service', downgradeInjectable(Service));
    
  • 注入$injectorをコントローラーに送信し、これを使用してダウングレードされたサービスを取得します

    const service = this.$injector.get('service');
    service.methos();
    
1
Ninjaneer

ハイブリッドアプリでも同じエラーが発生しました。次のバージョンを使用しています。

  • AngularJS 1.7.x
  • Angular 7.3.x

この回答で述べたように、Angularのブーストラッピングを強制するために<ng2-bootstrap>というダミーコンポーネントも使用しました。次に、Angularがブートストラップされているかどうかを確認するAngularJSサービスを作成しました。

// tslint:disable: max-line-length
/**
 * This service can be used in cases where Angular fails with following error message:
 *
 * `Error: Trying to get the Angular injector before bootstrapping the corresponding Angular module.`
 *
 * Above error occurs because of how `downgradeModule` works.
 */

/*@ngInject*/
export class Ng2BootstrapDetectionService {
  private bootstrapDone = false;
  constructor(private $q: ng.IQService) {}

  public whenBootstrapDone(): ng.IPromise<void> {
    if (this.bootstrapDone) {
      return this.$q.resolve();
    }

    const deferred = this.$q.defer<void>();

    angular.element(document).ready(() => {
      const intervalId = setInterval(() => {
        const el = document.querySelector('ng2-bootstrap');
        if (el && el.outerHTML.includes('ng-version=')) {
          this.bootstrapDone = true;
          clearInterval(intervalId);
          deferred.resolve();
        }
      }, 500);
    });

    return deferred.promise;
  }
}

Ng2BootstrapDetectionServiceは以下のように使用できます:

import {NotificationService} from 'ng2-app/notification.service';

// This can be used in cases where you get following error:
// `Error: Trying to get the Angular injector before bootstrapping the corresponding Angular module.`

// You will need access to following
// $injector: AngularJS Injector
// Ng2BootstrapDetectionService: our custom service to check bootsrap completion
this.Ng2BootstrapDetectionService
  .whenBootstrapDone()
  .then(() => {
    const notificationService = this.$injector
      .get<NotificationService>('ng2NotificationService');
    notificationService.notify('my message!');
  });

このソリューションの詳細 は、このブログ投稿の最後にあります

0
pankaj28843

私は同じ問題を抱えていましたが、これを見つけるまでに数時間かかりました。私の回避策は、ダウングレードする必要があるすべてのサービスを注入するだけで、何もしないServiceBootstrapComponentを作成することでした。

次に、そのコンポーネントをダウングレードし、@ NgModuleのenエントリとしてマークして、index.htmlに追加します。

私のために働く。

0
JGoodgive