AngularJS is a structural JavaScript framework for building dynamic web applications. It extends HTML with additional attributes and uses two-way data binding to synchronize the model and view automatically. Unlike traditional frameworks, it uses the MVC (Model-View-Controller) architecture and promotes modular, testable code.
Directives are special tokens in the DOM that tell AngularJS to extend HTML functionality. Built-in directives include ng-model
, ng-bind
, and ng-repeat
.
<input type="text" ng-model="name">
<p>Hello, {{name}}!</p>
$scope
is an object that binds the controller and view, enabling data exchange. It serves as a “glue” between them, where any properties or functions on $scope
become accessible in the view.
app.controller('MainController', function($scope) {
$scope.greeting = "Hello, World!";
});
Two-way data binding allows the model and view to be updated automatically in sync. When data in the model changes, it reflects in the view, and vice versa.
AngularJS is built on the MVC pattern, where:
ng-app
initializes an AngularJS application and sets the root element for Angular components.
<div ng-app="myApp">
<!-- AngularJS app content -->
</div>
ng-repeat
is used to repeat an element for each item in a collection, commonly for displaying lists.
<ul>
<li ng-repeat="item in items">{{item.name}}</li>
</ul>
Services are singleton objects in AngularJS used for logic that needs to be reused across the app, such as data sharing, network requests, and more.
Dependency injection in AngularJS is a way to manage dependencies between components by injecting services into controllers or other components, making code modular and testable.
$http
is an AngularJS service for making HTTP requests, allowing data retrieval from external APIs.
$http.get('/api/data').then(response => {
$scope.data = response.data;
});
Custom directives are user-defined DOM elements or attributes that extend HTML functionality. They’re registered with the directive
method.
app.directive('myDirective', function() {
return {
template: '<p>This is a custom directive</p>'
};
});
$watch
is used to monitor changes to a variable on the $scope
. When the variable changes, a callback is triggered.
$scope.$watch('name', function(newVal, oldVal) {
console.log('Name changed from', oldVal, 'to', newVal);
});
$digest
updates the bindings, while $apply
triggers $digest
and also allows execution of external functions inside Angular’s scope.
$routeProvider
enables routing, allowing different views and controllers based on URL routes.
app.config(function($routeProvider) {
$routeProvider
.when('/home', { templateUrl: 'home.html', controller: 'HomeController' });
});
ng-submit
binds a form submission to an AngularJS expression or function in the controller.
<form ng-submit="submitForm()">
<!-- Form fields -->
</form>
Filters format data for display, such as uppercase, lowercase, currency, and date filters.
<p>{{ price | currency }}</p>
$timeout
is a wrapper around JavaScript’s setTimeout
, ensuring execution within Angular’s digest cycle.
$timeout(function() {
$scope.message = "Hello, AngularJS!";
}, 1000);
$compile
is a service that compiles HTML strings or DOM elements into a template, linking it with a scope. It’s often used in custom directives to add dynamic behavior to elements.
const compiledElement = $compile('<div>{{message}}</div>')($scope);
ng-include
allows you to include external HTML files within a template. This is useful for breaking down large templates into smaller, reusable components.
<div ng-include="'header.html'"></div>
ng-init
initializes variables in the view, useful for simple values. However, $scope
initialization in the controller is better for complex data and logic.
<div ng-init="count=1">{{count}}</div>
$broadcast
sends an event down the scope hierarchy, reaching all child scopes. $emit
sends an event up, notifying only ancestor scopes. Both are used for inter-component communication.
$q
is AngularJS’s promise service for handling asynchronous operations, similar to JavaScript promises, but integrated within Angular’s digest cycle, making it ideal for chaining.
$q.when(data).then(result => { console.log(result); });
$timeout
is Angular’s version of setTimeout
and is integrated into the digest cycle, ensuring AngularJS data binding is updated after timeout.
$resource
is a service for interacting with RESTful APIs. It simplifies HTTP requests by creating a higher-level abstraction over $http
.
const User = $resource('/api/users/:id');
Use $scope.$evalAsync()
to delay expressions until after the current digest cycle, preventing redundant cycle triggers in custom directives.
$location
manages the URL in a single-page application (SPA) and facilitates navigation without reloading the page, handling path, hash, and search parameters.
$location.path('/newPath');
$parse
converts AngularJS expressions into functions, allowing dynamic evaluation of strings as expressions in custom directives and controllers.
const parsedFn = $parse('user.name');
$provide
is responsible for configuring, defining, and injecting services, constants, and factories in an AngularJS application, making it a key service in dependency injection.
$scope.$applyAsync
schedules an expression to be run after the current digest cycle, reducing excessive digest cycles and enhancing performance.
Interceptors intercept and modify HTTP requests or responses. They’re used for tasks like adding headers, handling errors, or logging.
$httpProvider.interceptors.push('myInterceptor');
AngularJS promises with $q
are used for asynchronous operations. $q
allows the creation, resolution, and chaining of promises in Angular applications.
const deferred = $q.defer();
$watchCollection
monitors changes in collections (arrays or objects) without tracking individual properties. It’s useful for detecting additions or deletions in arrays.
Lazy loading loads components only when needed, improving initial load speed. This can be implemented by dynamically injecting modules or using route-based lazy loading.
$cacheFactory
creates caches for storing data, reducing redundant API calls and improving performance by reusing cached results.
const cache = $cacheFactory.get('$http');
Use $exceptionHandler
to handle exceptions globally. Override this service to capture and log errors, providing centralized error management.
$evalAsync
schedules expressions for the next digest cycle without triggering a new cycle. It’s used to prevent multiple $digest
calls.
ng-annotate
automates adding dependency annotations in AngularJS, especially useful for minification, ensuring Angular services are injected correctly in minimized code.
AngularJS uses dependency injection (DI) to inject services, factories, or values into components, enhancing modularity and testability. In production, DI optimization involves minification-safe annotations like ng-annotate
or array-style syntax to prevent errors from variable renaming. This helps avoid runtime failures and keeps the code maintainable.
angular.module('myApp', [])
.controller('MainController', ['$scope', 'DataService', function($scope, DataService) {
// Controller code here
}]);
A digest cycle checks scope variables for changes to update the DOM. Overuse of $watch
or unnecessary cycles degrade performance, especially with large datasets. Optimizing cycles with $applyAsync
or $evalAsync
in custom directives can minimize performance issues and improve responsiveness in AngularJS applications.
$q
allows creating and chaining custom promises, suitable for handling complex async tasks. By using $q.defer()
, we can manage promise resolution or rejection and streamline complex async workflows with then()
, all()
, and catch()
for handling errors.
function getData() {
var deferred = $q.defer();
$http.get('/api/data').then(response => {
deferred.resolve(response.data);
}, error => {
deferred.reject(error);
});
return deferred.promise;
}
$timeout
and $interval
are Angular wrappers for setTimeout
and setInterval
, integrated with the digest cycle. They ensure model updates are reflected in the DOM. To prevent memory leaks, use $timeout.cancel()
and $interval.cancel()
for clean-up, especially for ongoing tasks.
var interval = $interval(function() {
console.log("Repeating task");
}, 1000);
// Cancel the interval
$interval.cancel(interval);
$applyAsync
defers expressions until the next digest cycle, reducing digest cycle triggers and consolidating updates. It’s valuable in scenarios with frequent updates, as it optimizes performance by minimizing digest overload, making applications more responsive.
$scope.incrementAsync = function() {
$scope.$applyAsync(() => {
$scope.counter++;
});
};
$http
interceptors intercept requests/responses, useful for adding auth tokens or handling errors globally. For API authentication, an interceptor can attach JWT tokens to headers, enhancing maintainability by centralizing auth handling.
app.factory('authInterceptor', function() {
return {
request: function(config) {
config.headers.Authorization = 'Bearer token';
return config;
}
};
});
$httpProvider.interceptors.push('authInterceptor');
Prevent memory leaks by deregistering $watch
listeners, cancelling $timeout
and $interval
, and managing $scope.$on
listeners with $destroy
. Chrome DevTools and profiling tools help monitor active listeners and retained objects, making it easier to identify and fix memory leaks.
var unwatch = $scope.$watch('variable', function(newValue) {
// Watcher code
});
$scope.$on('$destroy', unwatch);
$broadcast
sends events to child scopes, $emit
sends events to ancestor scopes, and $on
listens for these events. They enable cross-controller communication, though overuse can impact performance and readability. Using services for shared data may be more efficient in large applications.
$scope.$emit('eventName', data); // Emit to ancestor scopes
Lazy loading delays module loading until needed, improving performance by reducing initial load time. Using libraries like ocLazyLoad
or route-based lazy loading are effective strategies, ensuring only required modules load, speeding up single-page application initialization.
$routeProvider.when('/lazy', {
templateUrl: 'lazy.html',
resolve: {
load: function($ocLazyLoad) {
return $ocLazyLoad.load('lazyModule.js');
}
}
});
$compileProvider
adjusts directives’ behavior, allowing configuration like transclusion, priority, and scope. It optimizes performance by setting $compileProvider.debugInfoEnabled(false)
in production, reducing data binding overhead and enhancing directive efficiency.
$cacheFactory
caches data, reducing redundant API calls and resource usage by storing frequently accessed data. This approach lowers server load and improves responsiveness by ensuring that cached results are reused instead of fetched repeatedly.
var cache = $cacheFactory('myCache');
cache.put('key', 'value');
$provide.decorator
modifies existing services by wrapping them, adding functionality like logging or custom behavior while preserving original functionality. This is useful for adjusting services, adding logging or profiling, without changing source code, enhancing flexibility and control.
$provide.decorator('$log', function($delegate) {
return function(msg) {
$delegate(msg + ' decorated');
};
});
ngModelOptions
provides advanced control over model updates, including debounce
for delayed updates, getterSetter
for custom get/set functions, and allowInvalid
for validation handling. This fine-grained control improves form performance, especially with high-frequency data changes.
<input type="text" ng-model="name" ng-model-options="{ debounce: 500 }">
$rootScope.$digest
manually starts a digest cycle, updating all scopes. Triggering it manually can be useful in third-party library events or complex custom directives, but overuse can degrade performance, so $apply
is often preferable for scope-limited updates.
$rootScope.$on
listens for events across the entire app, useful for global event handling. However, extensive use can lead to memory leaks and scope pollution, so it’s better to use it selectively and manage listeners to prevent performance issues.
$rootScope.$on('event', function(event, data) {
console.log(data);
});
Services are singletons instantiated with new
, factories return objects, and providers offer the most configuration flexibility, available during the config phase. Providers are ideal for complex setups, while services and factories are better for general dependency injection and logic reuse.
$provide.constant
defines unmodifiable values accessible across an app, useful for config data like API URLs. Unlike services, constants are accessible in config blocks, making them available early for consistent app-wide settings.
app.constant('API_URL', 'https://api.example.com');
AngularJS modules encapsulate code for better organization, using dependencies to include other modules automatically. Dependencies allow seamless integration of shared features, reducing duplication and facilitating module-based code reuse and maintenance across large projects.
One-time binding (using ::
) binds data once, updating the view only once, ideal for static or rarely changed data. By reducing $watch
listeners, it optimizes performance, particularly in data-heavy views or applications with extensive scope bindings.
<p>{{ ::data }}</p>
$httpBackend
mocks HTTP requests, enabling testing without actual server calls. It’s used to simulate $http
calls, allowing predictable tests and error handling. With expectGET()
and .flush()
, $httpBackend
enables robust, isolated tests for HTTP-dependent components.
$httpBackend.expectGET('/api/data').respond(200, { data: 'mockData' });
$httpBackend.flush(); // Triggers the mock response