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:
APIInterceptor
injectsngDialog
ngDialog
internally injects$http
$http
injectsAPIInterceptor
(Since you have added the interceptor via$httpProvider
$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
You are creating circular dependencies.What am I doing wrong? Is this a bad concept/structure for angular?
Thanks!!
I see two solutions:-I can do var Comment = $injector.get('Comment'); to avoid the error,
is that the best solution?
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
How to Use Promise in Foreach Loop of Array to Populate an Object
If (Key in Object) or If(Object.Hasownproperty(Key)
JavaScript Time Zone Is Wrong for Past Daylight Saving Time Transition Rules
Convert Unix Timestamp to Date Time (Javascript)
Efficient Way to Insert a Number into a Sorted Array of Numbers
Using the Haversine Formula in JavaScript
How to Build a Loop in JavaScript
Why Doesn't Nodelist Have Foreach
What Is the JavaScript Mime Type for the Type Attribute of a Script Tag
How to Sum the Values of a JavaScript Object
Why Is Window (And Unsafewindow) Not the Same from a Userscript as from a <Script> Tag
JavaScript Replace() Method Dollar Signs
Single Quote Escape in JavaScript Function Parameters