Rewriting AngularJS directives as Angular components

Loading

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

FeatureAngularJS DirectivesAngular Components
Definitiondirective() function@Component decorator
StructureComplex and flexibleSimpler and component-based
TemplatesDefined using template or templateUrlUses inline HTML or templateUrl
Scope$scope (isolated or inherited)Uses class properties (this)
Bindings@, =, & for attribute bindingsUses @Input() and @Output()
ControllersDefined 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 &.

Leave a Reply

Your email address will not be published. Required fields are marked *