Problems with Circular Dependency and Oop in Angularjs

Problems with circular dependency and OOP in AngularJS

A circular dependency is always the sign of mixing of concerns, which is a really bad thing. Miško Hevery, one of the authors of AngularJS, explains a nice solution on his awesome blog. In short, you probably have a third service hidden somewhere, which is the only part of your code really needed by the two others.

AngularJS circular dependency

The reason Angular is complaining about a circular dependency is that...well there is one.

It is a very dangerous path to go down, but if you know what you are doing (famous last words) then there is a solution to circumvent that:

.service('LogServices', function($log, $injector) {

// ...

var Database; // Will initialize it later

this.log = function(type, message, details) {
/* Before using Database for the first time
* we need to inject it */
if (!Database) { Database = $injector.get('Database'); }

var log = {};
log.type = type
log.context = this.context;
log.message = message;
log.dateTime = moment().format('YYYY-MM-DD HH:mm:ss');
log.details = details || '';
$log[type.toLowerCase()](log);

if (type === 'ERROR' || this.logDebug) {
Database.logSave(log);
}
};

// ...
})

See, also, this short demo.

Angular - Circular dependency found

The core problem is:

  1. APIInterceptor injects ngDialog
  2. ngDialog internally injects $http
  3. $http injects APIInterceptor (Since you have added the interceptor via $httpProvider

The easiest solution is to use $injector to retrieve ngDialog manually when it is needed.

Simple example:

app.factory('APIInterceptor', function($q, $rootScope, $location, $window, $injector) {

return {

request: function(config) {

var ngDialog = $injector.get('ngDialog');

return config;
}
};
});

Angular: circular dependency of specific case

In these kind of problems there are many solutions, which depend by the way you are thinking. I prefer thinking each service (or class) has some goal and needs some other service to complete its goal, but its goal is one, clear and small. Let’s see your code by this view.

Problem 1:

GridData: Here lives the data of grid. MainService comes here to get the data that needs, so we inject this to mainService and we use the loadRowData function to get the rowData data as you do, but in $interval you inject mainService inside gridData but gridData doesn’t need mainService to end its goal (get items from server).

I solve this problem using an observer design pattern, (using $rootScope). That means that I get notified when the data are arrived and mainService come and get them.

grid-data.service.js :

angular.module("gridApp").service("gridDataService",
["$injector", "$interval", "$timeout", "$rootScope",
function ($injector, $interval, $timeout, $rootScope) {
[…]
$interval(function () {
[..]
self.newItems = [rowToAdd];

// delete this code
// var gridMainService = $injector.get('gridMainService');
// gridMainService.gridOptions.api.addItems(newItems);

// notify the mainService that new data has come!
$rootScope.$broadcast('newGridItemAvailable');

grid-main.service.js:

 angular.module("gridApp").service("gridMainService",
["$log", "$q", "gridConfigService", "gridDataService", '$rootScope',
function ($log, $q, gridConfigService, gridDataService, $rootScope) {
[..]
// new GridData data arrive go to GridData to get it!
$rootScope.$on('newGridItemAvailable', function(){
self.gridOptions.api.addItems(gridDataService.getNewItems());
})
[..]

When a real server is used, the most common solution is to use the promises (not observer pattern), like the loadRowData.

Problem 2:

gridSettingsService: This service change the settings of mainService so it needs mainService but mainService doesn’t care about gridSettings, when someone wants to change or learn mainService internal state (data, form) must communicate with mainService interface.

So, I delete grid Settings injection from gridMainService and only give an interface to put a callback function for when Grid is Ready.

grid-main.service.js:

angular.module("gridApp").service("gridMainService",
["$log", "$q", "gridConfigService", "gridDataService", '$rootScope',
function ($log, $q, gridConfigService, gridDataService, $rootScope) {
[…]
// You want a function to run onGridReady, put it here!
self.loadGridOptions = function (onGridReady) {
[..]
self.gridOptions = {
columnDefs: gridConfigService.columnDefs,
rowData: gridDataService.rowData,
enableSorting: true,
onGridReady: onGridReady // put callback here
};
return self.gridOptions;
});
[..]// I delete the onGridReady function , no place for outsiders
// If you want to change my state do it with the my interface

Ag-grid-controller.js:

    gridMainService.loadGridOptions(gridSettingsService.applyDefaults).then(function () {
vm.gridOptions = gridMainService.gridOptions;
vm.showGrid = true;
});

here the full code: https://plnkr.co/edit/VRVANCXiyY8FjSfKzPna?p=preview

AngularFire ObjectFactory Childobjects cause Circular Dependency

What am I doing wrong? Is this a bad concept/structure for angular?
Thanks!!

You are creating circular dependencies.

I can do var Comment = $injector.get('Comment'); to avoid the error,
is that the best solution?

I see two solutions:-

1) Lazy injecting solution (you suggested yourself)

This is the best solution to avoid those circular dependencies in AngularJS. Although looking at the AngularFire documentation you are into an uncharted territory as some of these things in AngularFire are experimental in nature.

Here is the working fiddle from your

var Comment = $injector.get('Comment');

You are essentially lazy injecting your references.

http://jsfiddle.net/yogeshgadge/ymxkt6up/5/

2) Module Run block:

With this option you may be able to inject those dependencies into your factories instead of using $injector.

AngularJS Services injecting each other causing infinite loop

You created circular-dependency which is wrong.

I found an article from Miško the authors of AngularJS about this problem.

in order to solve this u can use a third service which can use as mediator



Related Topics



Leave a reply



Submit