A different approach for communicate between Modules in AngularJS
(by Mark Qian)
Common ways to communicate between modules of your application using core AngularJS functionality include:
- Using services
- A module can be injected into another module
- Using events
- By assigning models on $rootScope
- Directly between controllers, using $parent, $$childHead, $$nextSibling, etc.
- Directly between controllers, using ControllerAs, or other forms of inheritance
- More...
There are three categories:
- global (such as rootScope) or relative (like parent)
- events
- DI
The problem with first two categories is that you have to hard code the reference of parties. This is really bad
as things changing during development and it is hard to be tested.
To target the problem, AngularJS provides a powerful mean, DI, to isolate service producers and consumers.
Even though DI provides some degree of isolation, it uses references(the machine addresses) to represent dependencies,
which reduces a lot of potential on flexibility.
My point is that to achieve the better flexibility, the application level logic including module relation such as dependencies
between modules should not reside in codes. They are the "spirit" of the application just like human being mind should be
isolated/extracted from the body (codes of modules). DI in Angular can not achieve this since all the logic is represented by references.
An ideal flexible application should be built into two parts: a container with all the modules
in your layout and the logic that make the modules work together. It will be perfect if the logic can be
extracted out of codes (container) and loaded as needed (at runtime!).
My "different approach" is point-to-point publish/subscribe with dependency out of codes as plain text.
You may say, wait a minute: Angular has built-in environment for publish/subscribe - the emit and broadcast.
If you don't use broadcast heavily, it might be OK. It will become a performance issue if you have a huge scope
hierarchy and use publish/subscribe as your major mean for communication between modules or any DOM components.
The broadcasting is really overkill for most cases. Instead, straight point to point invocation is enough for many of us.
The key points of my approach:
- It is point to point invocation without traveling in scope hierarchy
- It is built into DOM so it is so easy to access (to do publishing and subscribing)
- It also supports browser/tab synchronization:
the topic can travel to reach all browser/tab instances.
To try it out, just load current page into two browser instances side by side
and play to see topics reach in all the browser/tab instances!
- The module relation/dependencies is presented as plain text so it is very easy to be saved in configuration file.
You may ask: why I want to save the dependencies/logic into files? In some cases, you want to extract the logic out of codes
and pass it around. One example is the feature "browser synchronization" of my approach above.
The Web Storage can only store plain text so it is hard for DI to save communication info and pass it to
other browser instances.
Another example, in case of SAAS, you may deploy the same "container" to different clients with different logic in files
(which is loaded at runtime) according to the client's paid plan meaning that features are enabled/disabled
by plain text in configuration files instead of codes.
Or it can be used as access control: the container loads different logic for different roles. For example, there are
many admin-level publisher/topic but non-admin user's relation contains no subscription to the admin-topic
so can never act as an admin.
The following is a case where two directives (publisher and subscriber) communicate with publish/subscribe.
Actually, this two "sample" directives can be used as a mean to able any DOM element publish/subscribe-able:
just add the directive "publisher" (as an attribute) to a DOM element to make it publish a topic or add
the directive "subscriber" (as an attribute) to a DOM element to make it subscribe a topic (in other words,
it will carry out an action as it receives a topic).
Case 3: Communicate between Controllers and directives
The following is a case where directive and controller communicate with publish/subscribe.
The div on the left below (a directive) publish TopicB as your mouse is moved over and the right div
(a controller) in "Case 2" above subscribed the TopicB and react.
Similarly, the div on the right (a controller) publish TopicA when clicked. The right div in
"Case 1" (a directive) subscribe TopicA and react.