![]()
AngularJS controllers play a crucial role in managing application logic, handling user interactions, and linking the view (HTML) with the model (data). Following best practices ensures maintainability, scalability, and better performance.
1. Use Controller As Syntax
Best Practice:
Instead of using $scope, use this with the Controller As syntax.
Example:
app.controller('MainController', function() {
var vm = this; // Using 'vm' instead of $scope
vm.title = "Welcome to AngularJS!";
});
<div ng-controller="MainController as main">
<h1>{{ main.title }}</h1>
</div>
Why?
- Improves readability and clarity.
- Avoids unnecessary reliance on
$scope. - Aligns with best practices from Angular (2+ versions).
2. Keep Controllers Thin (Fat Models, Skinny Controllers)
Best Practice:
- Controllers should focus only on view logic.
- Move business logic to services or factories.
Example (Bad Approach – Too Much Logic in Controller):
app.controller('ProductController', function($scope) {
$scope.products = [
{ name: 'Laptop', price: 50000 },
{ name: 'Phone', price: 20000 }
];
$scope.getDiscountedPrice = function(price) {
return price * 0.9; // Business logic inside controller ❌
};
});
Example (Best Approach – Move Logic to a Service):
app.service('ProductService', function() {
this.getDiscountedPrice = function(price) {
return price * 0.9;
};
});
app.controller('ProductController', function(ProductService) {
var vm = this;
vm.products = [
{ name: 'Laptop', price: 50000 },
{ name: 'Phone', price: 20000 }
];
vm.getDiscountedPrice = ProductService.getDiscountedPrice;
});
Why?
- Better maintainability → Business logic is separate.
- Reusability → Services can be reused across controllers.
3. Use Dependency Injection (DI) Properly
Best Practice:
Always use dependency injection to avoid issues during minification.
Bad Approach (Minification Will Break It):
app.controller('MyController', function($http, $scope) {
$http.get('/api/data').then(function(response) {
$scope.data = response.data;
});
});
Best Approach (Safe for Minification):
app.controller('MyController', ['$http', '$scope', function($http, $scope) {
$http.get('/api/data').then(function(response) {
$scope.data = response.data;
});
}]);
OR
app.controller('MyController', function($http, $scope) {
'ngInject'; // Works with AngularJS module bundlers
$http.get('/api/data').then(function(response) {
$scope.data = response.data;
});
});
Why?
- Prevents dependency injection errors when minifying code.
- Ensures Angular can correctly resolve dependencies.
4. Avoid Using $scope When Possible
Best Practice:
Use Controller As instead of $scope.
Bad Approach (Using $scope Everywhere):
app.controller('UserController', function($scope) {
$scope.username = "John Doe";
});
Best Approach (Use this Instead of $scope):
app.controller('UserController', function() {
var vm = this;
vm.username = "John Doe";
});
Why?
- Cleaner code.
- Avoids unnecessary
$scopedependencies.
5. Use $scope.$on() Wisely & Clean Up Listeners
Best Practice:
- Always remove event listeners to avoid memory leaks.
Example (Removing Event Listener):
app.controller('EventController', function($scope) {
var unbind = $scope.$on('customEvent', function(event, data) {
console.log("Received:", data);
});
$scope.$on('$destroy', unbind); // Clean up event listener
});
Why?
- Prevents memory leaks when controllers are destroyed.
- Avoids duplicate listeners when switching views.
6. Use Promises for Asynchronous Operations
Best Practice:
- Avoid using callbacks for HTTP requests.
- Use
$q(Promises) for better handling of async tasks.
Bad Approach (Nested Callbacks – Callback Hell 😨)
app.controller('DataController', function($scope, $http) {
$http.get('/api/data').then(function(response) {
$scope.data = response.data;
$http.get('/api/more-data').then(function(response) {
$scope.moreData = response.data;
});
});
});
Best Approach (Using $q for Promises )
app.controller('DataController', function($http, $q) {
var vm = this;
function fetchData() {
var dataPromise = $http.get('/api/data');
var moreDataPromise = $http.get('/api/more-data');
$q.all([dataPromise, moreDataPromise]).then(function(responses) {
vm.data = responses[0].data;
vm.moreData = responses[1].data;
});
}
fetchData();
});
Why?
- Better readability & maintainability.
- Handles multiple async requests efficiently.
7. Use Services for Shared Data
Best Practice:
Use a service to share data across controllers instead of $rootScope.
Bad Approach (Using $rootScope to Share Data – Avoid 🚫):
app.controller('Ctrl1', function($rootScope) {
$rootScope.sharedData = "Hello";
});
app.controller('Ctrl2', function($rootScope) {
console.log($rootScope.sharedData);
});
Best Approach (Use a Service ):
app.service('SharedService', function() {
this.data = "Hello";
});
app.controller('Ctrl1', function(SharedService) {
var vm = this;
vm.sharedData = SharedService.data;
});
app.controller('Ctrl2', function(SharedService) {
var vm = this;
vm.sharedData = SharedService.data;
});
Why?
- Encapsulates data properly.
- Avoids
$rootScopepollution.
8. Limit Usage of $watch, $digest, $apply
Best Practice:
- Avoid excessive use of $watch, $digest, and $apply to prevent performance issues.
Instead, Use:
- One-time binding (
::) for static data. - Event-based updates instead of
$watch().
Example: One-time Binding (::)
<h1>{{ ::user.name }}</h1> <!-- Won't update after first load -->
Why?
- Reduces performance overhead.
