Angular 9 - How Do I Downgrade Multiple Modules with Components? - angularjs

I'm trying to group multiple Angular 9 Modules into a downgrade Module for AngularJS. I can get it to work fine for just one, but I'm having trouble getting it to work for multiple. I keep getting these errors:
With downgradedModule: specified
Error while instantiating component 'ExampleComponent': Unable to find the specified downgraded module.
Without downgradedModule: specified:
Error while instantiating component 'ExampleComponent': 'downgradedModule' not specified.
I've tried changed the value in downgradedModule to 'SharedModule', 'sharedModule', 'shared-module', and SharedModule with no luck. I'm not sure what else to do here.
Note: All of the manual bootstrapping is done. Not including it in my code samples because that part is working fine. Like I said, this works fine when I only downgrade one module
ngx.module.ts:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { downgradeModule, downgradeComponent } from '#angular/upgrade/static';
import { SharedModule } from './shared/shared.module';
import { ViewerModule } from './viewer/viewer.module';
import { ExampleComponent } from './shared/example/example.component';
const sharedModule = (extraProviders) => platformBrowserDynamic(extraProviders).bootstrapModule(SharedModule);
const viewerModule = (extraProviders) => platformBrowserDynamic(extraProviders).bootstrapModule(ViewerModule);
export const NgxModule = angular
.module('ngx', [
downgradeModule(viewerModule),
downgradeModule(sharedModule)
])
.directive('exampleComponent',
downgradeComponent({ component: ExampleComponent, downgradedModule: 'sharedModule' })) // error
.name;
shared.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
import { ExampleComponent } from './example/example.component';
#NgModule({
imports: [
BrowserModule,
CommonModule
],
declarations: [
ExampleComponent
],
entryComponents: [
ExampleComponent
]
})
export class SharedModule {
constructor() { }
ngDoBootstrap() {
}
}
viewer.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
#NgModule({
imports: [
BrowserModule,
CommonModule
],
declarations: [
PdfComponent
],
entryComponents: [
PdfComponent
]
})
export class ViewerModule {
constructor() { }
ngDoBootstrap() {
}
}

I was able to work on this over the weekend and found my solution from reading the documentation a little more closely:
downgradedModule?: string: The name of the downgraded module (if any) that the component "belongs
to", as returned by a call to downgradeModule(). It is the module,
whose corresponding Angular module will be bootstrapped, when the
component needs to be instantiated.
Source: https://angular.io/api/upgrade/static/downgradeComponent
One pitfall others might run into is that you can't call downgradeModule() directly when assigning it to the downgradedModule property. You have to downgrade it first and store it in a variable first, then pass it in.
const ng2BootstrapFn = (extraProviders) => platformBrowserDynamic(extraProviders)
.bootstrapModule(SharedModule);
const downgradedSharedModule = downgradeModule(ng2BootstrapFn);
export const NgxModule= angular
.module('ngx', [downgradedSharedModule ])
.directive('exampleComponent', downgradeComponent({
component: ExampleComponent,
downgradedModule: downgradedSharedModule }))
.name;

Related

PrimeNG 10.0.3 - error NG8001: 'p-tabView' is not a known element

I am using PrimeNG 10.0.3 in an angular 10 application. In it I am importing several PrimeNG components and using them without an issue. However, when I try to use the TabView module, I get error NG8001: 'p-tabView' is not a known element as well as error NG8001: 'p-tabPanel' is not a known element.
I have created a module called primng.module where I host all the PrimeNG imports:
import { NgModule } from '#angular/core';
import { ButtonModule } from 'primeng/button';
import { BlockUIModule } from 'primeng/blockui';
import { ToastModule } from 'primeng/toast';
import { DialogModule } from 'primeng/dialog';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { OverlayPanelModule } from 'primeng/overlaypanel';
import { PanelModule } from 'primeng/panel';
import { CardModule } from 'primeng/card';
import { SidebarModule } from 'primeng/sidebar';
import { InputMaskModule } from 'primeng/inputmask';
import { PasswordModule } from 'primeng/password';
import { TableModule } from 'primeng/table';
import { TabViewModule } from 'primeng/tabview';
import { ConfirmationService, MessageService } from 'primeng/api';
#NgModule({
imports: [
],
exports: [
ButtonModule,
BlockUIModule,
ToastModule,
DialogModule,
ConfirmDialogModule,
OverlayPanelModule,
PanelModule,
CardModule,
SidebarModule,
InputMaskModule,
PasswordModule,
TableModule,
TabViewModule
],
providers: [
MessageService,
ConfirmationService
]
})
export class PrimengModule { }
Then I import this module as:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { PrimengModule } from '../../primeng.module';
...
const routes: Routes = [
...
];
#NgModule({
declarations: [
...
],
imports: [
RouterModule.forChild(routes),
PrimengModule
],
exports: [
RouterModule
]
})
export class AdminRoutingModule { }
And finally, I am using the TabView as:
<p-tabView>
<p-tabPanel header="Header 1">
Content 1
</p-tabPanel>
<p-tabPanel header="Header 2">
Content 2
</p-tabPanel>
<p-tabPanel header="Header 3">
Content 3
</p-tabPanel>
</p-tabView>
I do not have trouble using any of the other modules, just TabView.
What am I missing?
Thanks.
Ok, so it turns out this had nothing to do with PrimeNG. The hosting component where I had the p-tabView markup was declared, by mistake, in a different module that knew nothing about my primeng.module.ts and naturally did not know how to handle the TabView.
Lesson learned: Be careful which module you declare each component in. Make sure the module knows about the features you need to use in the component.

$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