Circular dependency and OOP issues in AngularJS

AngularJS + OOP is a kind of sexual function to use

Hi, I have been successfully using OOP with AngularJs for some time (first I started with angularjs with oop inheritance in action ), the approach provided allows you to define your classes as angular services, which you can later extend or inherit from this:

Application.factory('AbstractObject', [function () { var AbstractObject = Class.extend({ virtualMethod: function() { alert("Hello world"); }, abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable throw new Error("Pure abstract call"); } }); return AbstractObject; // You return class definition instead of it instance }]); Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) { var DerivedObject = AbstractObject.extend({ virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world` alert("Hey!"); this._super(); }, abstractMethod: function() { alert("Now I'm not abstract"); } }); return DerivedObject; }]); 

Plunker: http://plnkr.co/edit/rAtVGAsNYggBhNADMeoT

using the described approach gives you the opportunity to define classes that integrate perfectly with the angular infrastructure. You get all sorts of great features from two worlds - OOP and AngularJs. Injection injection is free for your classes, and this makes your classes simple, allows you to put a lot of the code of the template controller into some base class, which can be reused later.

However

AngularJs infrastructure blocks the previously described approach from spreading its wings at 100%. The problem arises when you try to define recursive class definitions (e.g. recursive aggregation), for example, you have two class definitions such as Blog and Tag

 Application.factory('Blog', ['Tag', function (Tag) { var Blog = Class.extend({ tags: function() { return this.tags; } }); return Blog; }]); Application.factory('Tag', ['Blog', function (Blog) { var Tag = Class.extend({ Blogs: function() { return this.blogs; } }); return Tag; }]); 

This will not work, because both Blog and Tag refer to themselves, causing a circular dependency.

PS

The last thing I found was a kind of ugly solution that solves my problem in my particular case, but does not work at all, and, as I said, this is ugly:

 Application.factory('BlogNamespace', [function () { var Blog = Class.extend({ tags: function() { return this.tags; } }); var Tag = Class.extend({ Blogs: function() { return this.blogs; } }); return { Tag: Tag, Blog: Blog }; }]); 

Question

The above fix will not work, as namespaces can also be subject to circular dependency. This means that this is not a solution to the described problem, but a problem at a level below the level.

Any suggestions on how to solve the described problem in the general case?

+51
javascript angularjs oop
Oct 13 '13 at 10:37
source share
3 answers

Circular dependence is always a sign of "strong" mixing of problems, which is very bad. Mishko Hevery, one of the authors of AngularJS, explains a nice solution on his amazing blog . In short, you probably have some third service, which is the only part of your code that the other two really need.

+63
Oct 13 '13 at 12:42 on
source share

I answer my question only because I found a technical way to solve the problem that I already wrote about. But before that, I highly recommend you use the Blackhole proposal, as it allows you to solve a wider range of problems that poor architecture usually causes. Please use his approach first and return to the current one if you know what you are doing.

So here it is:

You can use the $injector service and enter the required definitions at runtime, which is technically legal, but according to this post (it's hard to imagine that it was written in 2008), it looks like black magic, do it, and she will hit you:

 Application.factory('Blog', ['$injector', function ($injector) { var Tag = $injector.get('Tag'); // Here is your tag ... }]); Application.factory('Tag', ['Blog', function (Blog) { ... }]); 



Edit

It turned out that the current approach is an example of the Locator Service template, which is the IoC Antipattern.

+46
Oct 14 '13 at 20:05
source share

LAST RESORT: NOT SECURE

In my case, the best way to get around a circular dependency problem like this one in angular is to call calling functions via $rootScope -broadcasts . Then another service can listen to this broadcast and respond to the desired function call. This may not be the most elegant solution, but in some cases where the interaction between the services is mostly unidirectional, this may be a reasonable alternative. (note that this also allows returning return values ​​back to the broadcast function only through callbacks)




A pseudo-example of this:

 angular.module('myApp').factory('service1', ["$rootScope", function($rootScope) { function func1() { // do something } $rootScope.$broadcast("callFunc2"); // calls func2 from service 1 return { func1: func1 } } ]); angular.module('myApp').factory('service2', ["service1", "$rootScope", function(service1, $rootScope) { function func2() { // do something } service1.func1(); // calls func1 from service 2 $rootScope.on("callFunc2", func2); } ]); 
0
Jan 24 '17 at 10:25
source share



All Articles