Using downgradeModule to create an Angular hybrid app - angularjs

I want to upgrade my Angular JS application to use Angular (2+), and for this I want to use the downgradedModule:
app.module.js (Angular JS module)
import angular from 'angular';
import { environment } from '../environment'
import { downgradeModule } from '#angular/upgrade/static';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
const bootstrapFn = (extraProviders) => {
const platformRef = platformBrowserDynamic(extraProviders);
return platformRef.bootstrapModule(AppModule);
};
const downgradedModule = downgradeModule(bootstrapFn);
(function() {
'use strict';
angular
.module('app', [
/* my other Angular JS modules */
downgradedModule
])
..........
app.module.ts (Angular module)
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { UpgradeModule, setAngularLib } from '#angular/upgrade/static';
import { AppRoutingModule } from './app-routing.module';
import angular from 'angular';
import { MyComponent} from './example-components/my-component.component';
setAngularLib(angular);
#NgModule({
imports: [
BrowserModule,
UpgradeModule,
AppRoutingModule
],
declarations: [
MyComponent
],
entryComponents: [
MyComponent
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) { }
ngDoBootstrap() {}
}
When I run npm start, it says that angular is not defined and points to the angular.module('app',... line from the app.module.js file. If I go to the only page which contains Angular JS and also Angular components, I don't get any error, but the page is not displaying anything.
If I try to use UpgradeModule into the app.module.ts file like this:
ngDoBootstrap() {
this.upgrade.bootstrap(document.documentElement, ['app'], {
strictDi: false
});
}
The application is working but I get an error into the browser's console:
Error: [$rootScope:inprog] $digest already in progress. I understand that this can be avoided if I use downgradedModule which doesn't work for now as I explained.
Do I have to do something else to bootstrap the application, or what is the problem? Thanks!

Related

How to use Angular 2 component in Angular 1 app using #angular/upgrade

This question might be duplicate but I have tried all possible options.
I am trying to use my Angular 2 component in Angular 1 app using #angular/upgrade.
angular2 files :-
simple hello component in app/components/hello.
hello.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-hello',
templateUrl: './hello.component.html',
styleUrls: ['./hello.component.scss']
})
export class HelloComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
hello.component.html
<p>
hello works!
</p>
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { UpgradeModule } from '#angular/upgrade/static';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HelloComponent } from './components/hello/hello.component';
#NgModule({
declarations: [
AppComponent,
HelloComponent
],
imports: [
BrowserModule,
UpgradeModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent],
entryComponents: [
HelloComponent
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) { }
ngDoBootstrap() {
this.upgrade.bootstrap(document.body, ['usr']);
}
}
platformBrowserDynamic().bootstrapModule(AppModule);
In my Angular 1 app :-
app.ts (root file)
import { HelloComponent } from '../angular/src/app/components/hello/hello.component';
import { downgradeComponent } from './node_modules/#angular/upgrade/static/static'
let moduleList = ['ngCookies', 'ngSanitize','$rootScope', '$locationProvider '];
angular.bootstrap(document.body, ['usr']);
let app: any = angular.module('usr', moduleList)
.directive(
'appHello',
downgradeComponent({ component: HelloComponent })
);
index.html of Angular 1 (I have added base href and root which is default component of angular, app-root component is rendering properly in my angular 1 app)
<base href="/">
<app-root></app-root>
header.html (angular 1 header component where I want to show my angular 2 hello component)
<app-hello></app-hello>
screenshots.
Thanks in advance.
https://angular.io/api/upgrade/static/downgradeModule says
You cannot use downgradeModule() and UpgradeModule in the same hybrid
application. Use one or the other.
This means you can only use downgrade module while bootstrapping NG1

$Injector Error on Angular Upgrade from 1.6.6 to 6

I did Angular upgrade from Angular.Js 1.6.6 to Angular 6 using Webpack 4:
import 'core-js/es7/reflect';
import 'zone.js';
import 'reflect-metadata';
import 'rxjs';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { Routes, RouterModule } from '#angular/router';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule, downgradeComponent, downgradeInjectable, setAngularJSGlobal } from '#angular/upgrade/static';
import { module } from './app.module.ajs';
import './config/routes';
import AppComponent from './components/app/app';
import Application from './directives/application/application';
import { platformBrowserDynamic } from "#angular/platform-browser-dynamic";
console.log('NgModule');
const appRoutes: Routes = [
];
#NgModule({
declarations: [AppComponent, Application],
entryComponents: [AppComponent],
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(appRoutes),
UpgradeModule
]
})
class AppModule {
constructor(public upgrade: UpgradeModule) {
console.log('AppModule constructor');
}
ngDoBootstrap() {
console.log('AppModule ngDoBootstrap');
}
}
console.log('setAngularJSGlobal ...');
setAngularJSGlobal(window['angular']);
console.log('setAngularJSGlobal!!!');
import main = require('./main');
console.log('Before bootstrapModule module.name: ', module.name);
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
console.log('Hybrid mode: Angular + Angular.Js');
module.directive('appRoot', downgradeComponent({ component: AppComponent }));
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
console.log('bootstrapModule: ', module.name);
main['launchAngular']();
upgrade.bootstrap(document.body, [module.name], { strictDi: true });
});
created custom alias, added correct paths to tsconfig, Webpack build pass & produce output, but when application starts in browser, error popups in console: ngRoute is missing or incorrect ng module components
What is not done correctly & how to fix this error?
Fixed some Webpack aliases. Replaced: Angular-Resource with Axios, Angular-Route with UI-Router/Angular-Hybrid, refactored couple controllers to components/directives & services, got rid from $rootScope & Magic happened: Upgrade started to work.

How do you get access to the instance of a downgrade component in the view? [duplicate]

What I'm trying to do is to make Angular 2 simple component run inside angular 1 application. I was going through this official guide.
I've faced some issue with injection:
Unknown provider: $$angularInjectorProvider <- $$angularInjector
The stack trace is making no sense, but is obvious that error is raised somewhere deep in the angular itself :)
The structure of my current app looks like this:
ng1.module.ts (entry point):
'use strict';
import { downgradeComponent } from '#angular/upgrade/static';
const angular = require('./lib/angular-wrapper');
const app = angular.module('application', []);
import { AppComponent } from './components/app/app.component.ts';
import { Ng2Module } from './ng2.module.ts';
app.directive(
'app',
downgradeComponent({component: AppComponent}) as angular.IDirectiveFactory
);
angular.bootstrap(document.body, ['application']);
ng2.module.ts:
import 'reflect-metadata';
import '#angular/core';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './components/app/app.component.ts';
#NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
entryComponents: [ AppComponent ]
})
export class Ng2Module {
ngDoBootstrap() {}
}
And app.component.ts:
import 'reflect-metadata';
import { Component } from '#angular/core';
#Component({
selector: 'app',
template: "<h1>HELLO WORLD!</h1>"
})
export class AppComponent {}
Asking for any idea on: what can cause the described above error?
This is caused by the UpgradeModule downgraded service that you are using here:
import { UpgradeModule } from '#angular/upgrade/static';
You are using it because you want the UpgradeModule to downgrade an Angular 2 component to angular JS.
If you dig into the code of the UpgradeModule you can find that this module defines a new angular module named $$UpgradeModule.
This module registers a value provider named $$angularInjector (the one in the error above) - this $$angularInjector thing is responsible for injecting Angular modules into angular JS.
The solution is to import the module in the imports statement so that angular JS will have access to its services.
You forgot to import the UpgradeModule. Here is the answer from the official documentation:
#NgModule({
declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper],
providers: [
HeroesService,
// Register an Angular provider whose value is the "upgraded" AngularJS service
{provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']}
],
// All components that are to be "downgraded" must be declared as `entryComponents`
entryComponents: [Ng2HeroesComponent],
// We must import `UpgradeModule` to get access to the AngularJS core services
imports: [BrowserModule, UpgradeModule]
})
class Ng2AppModule {
ngDoBootstrap() { /* this is a placeholder to stop the boostrapper from complaining */
}
}
so first you need to change your code to:
ng2.module.ts:
import 'reflect-metadata';
import '#angular/core';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './components/app/app.component.ts';
#NgModule({
imports: [ BrowserModule, UpgradeModule ],
declarations: [ AppComponent ],
entryComponents: [ AppComponent ]
})
export class Ng2Module {
ngDoBootstrap() {}
}
Also in order to downgrade your component from ng2 to angular 1
You must create an AngularJS directive that will make this Angular component available inside AngularJS templates:
ng1AppModule.directive('Ng2Module', downgradeComponent({component: AppComponent}));
function downgradeComponent(info: { component: Type< This parameter is no longer used */ selectors?: string[]; }): any;
There is a very helpful post which explains in details how to create a hybrid angular application, and also the scenario when you have a v4 component and you want to use it in the v1 template.

Error in dependency injection when Angular component is downgraded to angularjs

I am in the process of upgrading angularjs app to angular 5 as outlined in this guide
I have the hybrid app bootstrapped from angular code, that went well. As a next step I created an angular5 component which depends on an angular5 service. I have downgraded the component and declared as directive in angularjs. The problem I see is the service doesn't get injected into the component. If I remove the service dependency from the component it works fine.
Here is my code and the error
Component
#Component({
selector: 'test-detail',
template: `
<h2>Windstorm details! {{test}}</h2>
<div><label>id: </label>1</div>
`
})
export class TestComponent {
private test:string;
constructor( private testService:TestService){
}
ngOnInit(){
this.test = this.testService.test();
}
}
Service:
import { Injectable } from '#angular/core';
#Injectable()
export class TestService{
test(){
return "hello";
}
}
NG5 Module
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { UpgradeModule } from '#angular/upgrade/static';
import {TestComponent} from '../ng5/directives/test.directive';
import {TestService} from '../ng5/services/test.service';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
imports: [
BrowserModule,
UpgradeModule,
HttpClientModule
],
declarations:[
TestComponent
] ,
entryComponents: [
TestComponent
],
providers:[
TestService
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) {
}
ngDoBootstrap() {
this.upgrade.bootstrap(document.documentElement, ['MyApp']);
}
}
AngularJS Module:
angular.module("MyApp").directive('testDetail', downgradeComponent({ component: TestComponent }) as angular.IDirectiveFactory);
The error I get when launching the page is
uncaught Error: Can't resolve all parameters for TestComponent: (?).
at syntaxError (compiler.js:485)
at CompileMetadataResolver.webpackJsonp.529.CompileMetadataResolver._getDependenciesMetadata (compiler.js:15699)
at CompileMetadataResolver.webpackJsonp.529.CompileMetadataResolver._getTypeMetadata (compiler.js:15534)
at CompileMetadataResolver.webpackJsonp.529.CompileMetadataResolver.getNonNormalizedDirectiveMetadata (compiler.js:15019)
at CompileMetadataResolver.webpackJsonp.529.CompileMetadataResolver._getEntryComponentMetadata (compiler.js:15847)
at compiler.js:15317
at Array.map (<anonymous>)
at CompileMetadataResolver.webpackJsonp.529.CompileMetadataResolver.getNgModuleMetadata (compiler.js:15317)
at JitCompiler.webpackJsonp.529.JitCompiler._loadModules (compiler.js:34404)
at JitCompiler.webpackJsonp.529.JitCompiler._compileModuleAndComponents (compiler.js:34365)
I am answering my own question. Declaring service the following way resolved the issue
providers:[
{ provide: 'TestService', useClass: TestService }
]

Angular2 Component in Angular1 App

What I'm trying to do is to make Angular 2 simple component run inside angular 1 application. I was going through this official guide.
I've faced some issue with injection:
Unknown provider: $$angularInjectorProvider <- $$angularInjector
The stack trace is making no sense, but is obvious that error is raised somewhere deep in the angular itself :)
The structure of my current app looks like this:
ng1.module.ts (entry point):
'use strict';
import { downgradeComponent } from '#angular/upgrade/static';
const angular = require('./lib/angular-wrapper');
const app = angular.module('application', []);
import { AppComponent } from './components/app/app.component.ts';
import { Ng2Module } from './ng2.module.ts';
app.directive(
'app',
downgradeComponent({component: AppComponent}) as angular.IDirectiveFactory
);
angular.bootstrap(document.body, ['application']);
ng2.module.ts:
import 'reflect-metadata';
import '#angular/core';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './components/app/app.component.ts';
#NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
entryComponents: [ AppComponent ]
})
export class Ng2Module {
ngDoBootstrap() {}
}
And app.component.ts:
import 'reflect-metadata';
import { Component } from '#angular/core';
#Component({
selector: 'app',
template: "<h1>HELLO WORLD!</h1>"
})
export class AppComponent {}
Asking for any idea on: what can cause the described above error?
This is caused by the UpgradeModule downgraded service that you are using here:
import { UpgradeModule } from '#angular/upgrade/static';
You are using it because you want the UpgradeModule to downgrade an Angular 2 component to angular JS.
If you dig into the code of the UpgradeModule you can find that this module defines a new angular module named $$UpgradeModule.
This module registers a value provider named $$angularInjector (the one in the error above) - this $$angularInjector thing is responsible for injecting Angular modules into angular JS.
The solution is to import the module in the imports statement so that angular JS will have access to its services.
You forgot to import the UpgradeModule. Here is the answer from the official documentation:
#NgModule({
declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper],
providers: [
HeroesService,
// Register an Angular provider whose value is the "upgraded" AngularJS service
{provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']}
],
// All components that are to be "downgraded" must be declared as `entryComponents`
entryComponents: [Ng2HeroesComponent],
// We must import `UpgradeModule` to get access to the AngularJS core services
imports: [BrowserModule, UpgradeModule]
})
class Ng2AppModule {
ngDoBootstrap() { /* this is a placeholder to stop the boostrapper from complaining */
}
}
so first you need to change your code to:
ng2.module.ts:
import 'reflect-metadata';
import '#angular/core';
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './components/app/app.component.ts';
#NgModule({
imports: [ BrowserModule, UpgradeModule ],
declarations: [ AppComponent ],
entryComponents: [ AppComponent ]
})
export class Ng2Module {
ngDoBootstrap() {}
}
Also in order to downgrade your component from ng2 to angular 1
You must create an AngularJS directive that will make this Angular component available inside AngularJS templates:
ng1AppModule.directive('Ng2Module', downgradeComponent({component: AppComponent}));
function downgradeComponent(info: { component: Type< This parameter is no longer used */ selectors?: string[]; }): any;
There is a very helpful post which explains in details how to create a hybrid angular application, and also the scenario when you have a v4 component and you want to use it in the v1 template.

Resources