I'm trying to upgrade an angularjs directive to use it my angular component. I've gotten the hybrid (ng1 + ng2) environment to work. I can also inject angularjs services in angular and use them in angular components (I actually got this working even with angularjs 1.4.x).
Now I'm trying to use an existing angularjs directive in angular, but not working.
For reference, are some snippets of my codes.
[index.html] (my-app is the Angular 4 root component)
...
<body>
<div class="banner">Angular Migration</div>
<my-app>Loading...</my-app>
...
</body>
...
[main.ts] (bootstrapping code)
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { UpgradeModule } from '@angular/upgrade/static';
import { Router } from '@angular/router';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
let upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.documentElement, ['sampleApp']);
platformRef.injector.get(Router).initialNavigation();
});
[app.module.ts] (Angular 4 module)
import { NgModule, Component } from '@angular/core';
import { HashLocationStrategy, LocationStrategy, CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';
import { RouterModule, Routes, UrlHandlingStrategy } from '@angular/router';
import { HybridUrlHandlingStrategy } from './hybridUrlHandlingStrategy';
import { AppComponent } from './app.component';
export const routes: Routes = [
...
];
@NgModule({
imports: [
CommonModule,
BrowserModule,
UpgradeModule,
RouterModule.forRoot([], { useHash: true, initialNavigation: false })
],
providers: [
{ provide: LocationStrategy, useClass: HashLocationStrategy },
{ provide: UrlHandlingStrategy, useClass: HybridUrlHandlingStrategy },
{ provide: 'appService', useFactory: (i: any) => i.get('appService'), deps: ['$injector'] },
{ provide: 'mdToHtml', useFactory: (i: any) => i.get('mdToHtml'), deps: ['$injector'] }
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule {
ngDoBootstrap() { }
}
[app.component.ts] (Angular 4 component)
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<router-outlet></router-outlet>
<div ng-view></div>
`,
})
export class AppComponent { }
[app.js] (AngularJs app)
'use strict';
angular.module('sampleApp', [
'ngRoute',
'app.components.services.appService'
])
.config(function ($routeProvider) {
$routeProvider
.otherwise({
template: ''
});
});
[myStars.js] (AngularJs directive)
'use strict';
angular.module('app.components.directives.myStars', [])
.controller('MyStarsCtrl', function() {
console.log("**** in MyStarsCtrl ")
})
.component('myStars', {
restrict: 'E',
bindings: {
count: '=count'
},
template: '<div>***** M Y S T A R S *****</div>'
});
[myStars.ts] (an attempt to migrate myStars directive to Angular 4)
import { Directive, ElementRef, Injector, Input, Output, EventEmitter } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';
@Directive({
selector: 'my-stars'
})
export class MyStarsDirective extends UpgradeComponent {
@Input() count: number;
@Output() clicked: EventEmitter<number>;
constructor(elementRef: ElementRef, injector: Injector) {
super('myStars', elementRef, injector);
}
}
[test.module.ts] (Angular 4 test module)
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes} from '@angular/router';
import { TestComponent } from './test.component';
// importing the new MyStarsDirective)
import { MyStarsDirective } from '../../../components/directives/myStars/myStars';
const thisRoute: Routes = [
{
path: 'test/:id',
component: TestComponent
}
];
@NgModule({
declarations: [
TestComponent,
MyStarsDirective, // <<<< adding directive here
],
providers: [],
imports: [
CommonModule,
RouterModule.forChild(thisRoute)
],
exports: [
RouterModule
]
})
export class TestModule {}
[test.component.ts] (Angular 4 test component)
import { Component, Inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { HybridUrlHandlingStrategy } from '../../../hybridUrlHandlingStrategy';
import { MyStarsDirective } from '../../../components/directives/myStars/myStars';
@Component({
templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
routeParams: any
myService: any
mdToHtml: any
constructor(
@Inject('myService') myService: any,
@Inject('mdToHtml') mdToHtml: (str: string) => string,
private activatedRoute: ActivatedRoute
) {
this.myService = myService;
this.mdToHtml = mdToHtml;
}
ngOnInit() {
this.routeParams = this.activatedRoute.params.subscribe(params => {
console.log("params", params['groupId'])
...
}
ngOnDestroy() {
this.routeParams.unsubscribe();
}
}
[test.component.html] (Angular 4 html)
...
<my-stars></my-stars> <!-- <<<< using directive here -->
...
Note: I've also upgrade angularjs version to 1.5 just to make this hybrid app works.
Here's the error when I browser the test component page:
Any help would be greatly appreciated.
Thanks.
The Component is used to break up the application into smaller components. That is why components are widely used in later versions of Angular to make things easy and build a total component-based model. The Directive is used to design reusable components, which are more behavior-oriented.
In a hybrid application you run both versions of Angular at the same time. That means that you need at least one module each from both AngularJS and Angular. You will import UpgradeModule inside the NgModule, and then use it for bootstrapping the AngularJS module.
A component is a single unit that encapsulates both view and logic whereas directives are used to enhance the behavior of components or dom elements and it doesn't have any templates. Component extends directive so every component is a directive.
You can put commonly used directives, pipes, and components into one module and then import just that module wherever you need it in other parts of your application. Notice the following: It imports the CommonModule because the module's component needs common directives.
For anyone interested, here's the solution to this error.
[test.module.ts] (Angular 4 test module)
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes} from '@angular/router';
import { TestComponent } from './test.component';
// importing the new MyStarsDirective)
import { MyStarsDirective } from '../../../components/directives/myStars/myStars';
const thisRoute: Routes = [
{
path: 'test/:id',
component: TestComponent
}
];
@NgModule({
declarations: [
TestComponent,
MyStarsDirective, // <<<< adding directive here
],
providers: [
{
provide: '$scope',
useFactory: i => i.get('$rootScope'),
deps: ['$injector']
}, // <<<< added this section to fix the No provider for $scope error
],
imports: [
CommonModule,
RouterModule.forChild(thisRoute)
],
exports: [
RouterModule
]
})
export class TestModule {}
I found the answer in this link https://github.com/angular/angular/issues/14993 (inspired by BrunoPoeta) See comment by gkalpak on March 10.
Now let's see if I can upgrade a regular directive and use 1.4.x.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With