When migrating an AngularJS application to Angular, one major challenge is converting AngularJS directives into Angular components. Since Angular components are a modern replacement for directives, understanding this transition is crucial.
1. Key Differences: AngularJS Directives vs. Angular Components
Feature | AngularJS Directives | Angular Components |
---|---|---|
Definition | directive() function | @Component decorator |
Structure | Complex and flexible | Simpler and component-based |
Templates | Defined using template or templateUrl | Uses inline HTML or templateUrl |
Scope | $scope (isolated or inherited) | Uses class properties (this ) |
Bindings | @ , = , & for attribute bindings | Uses @Input() and @Output() |
Controllers | Defined inside directive (controller ) | Uses a class-based approach |
2. Step-by-Step Conversion
Let’s go step by step and convert an AngularJS directive into an Angular component.
Step 1: AngularJS Directive (Old Code)
Consider an AngularJS directive that displays user details.
AngularJS Directive (user-card.directive.js
)
angular.module('myApp')
.directive('userCard', function() {
return {
restrict: 'E',
scope: {
user: '='
},
template: `
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
</div>
`,
controller: function($scope) {
console.log('User:', $scope.user);
}
};
});
Uses restrict: 'E'
(Element directive).
Defines an isolated scope (user: '='
).
Uses a controller inside the directive.
Step 2: Convert to an Angular Component
Angular Component (user-card.component.ts
)
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>Email: {{ user.email }}</p>
</div>
`,
styleUrls: ['./user-card.component.css']
})
export class UserCardComponent {
@Input() user: { name: string; email: string };
constructor() {
console.log('User:', this.user);
}
}
Uses @Component
decorator instead of directive()
.
Replaces AngularJS $scope
with a class property @Input() user
.
Uses TypeScript and a class-based approach.
Step 3: Using the Angular Component in an Angular Template
AngularJS Usage (Old)
<user-card user="currentUser"></user-card>
Angular Usage (New)
<app-user-card [user]="currentUser"></app-user-card>
Uses property binding ([user]
) instead of AngularJS user="currentUser"
.
3. Handling Transclusion (ng-transclude
)
In AngularJS, transclusion (ng-transclude
) allows injecting content inside directives.
AngularJS Directive with Transclusion
angular.module('myApp')
.directive('card', function() {
return {
restrict: 'E',
transclude: true,
template: `
<div class="card">
<h2>Card Title</h2>
<div ng-transclude></div>
</div>
`
};
});
Angular Equivalent Using ng-content
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
template: `
<div class="card">
<h2>Card Title</h2>
<ng-content></ng-content>
</div>
`,
styleUrls: ['./card.component.css']
})
export class CardComponent {}
Uses <ng-content></ng-content>
instead of ng-transclude
.
4. Handling Two-Way Binding (=
in AngularJS)
In AngularJS, two-way binding is done using =
.
scope: {
user: '='
}
In Angular, use @Input()
for input binding and @Output()
with EventEmitter
for two-way binding.
Angular Component with Two-Way Binding
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-input',
template: `
<input [(ngModel)]="userName" (ngModelChange)="onUserNameChange()" />
`
})
export class UserInputComponent {
@Input() userName: string;
@Output() userNameChange = new EventEmitter<string>();
onUserNameChange() {
this.userNameChange.emit(this.userName);
}
}
Uses @Output()
and EventEmitter
for two-way binding.
5. Handling Event Binding (&
in AngularJS)
In AngularJS, &
is used for passing functions.
scope: {
onClick: '&'
}
In Angular, use @Output()
and EventEmitter
.
AngularJS Directive
<button ng-click="onClick()">Click Me</button>
Angular Component
@Component({
selector: 'app-click-button',
template: `<button (click)="handleClick()">Click Me</button>`
})
export class ClickButtonComponent {
@Output() onClick = new EventEmitter<void>();
handleClick() {
this.onClick.emit();
}
}
Uses EventEmitter instead of &
.