Getting The Scope of Another Module to Dispatch Events;
Acyclical Directed Communication Network
EDIT: THIS QUESTION IS UNDER REFORMATION.
Intention
What I'm looking to do is fairly straight-forward. I have an Application Core module which looks like this:
var app, application = app = angular.module('app', ['session', 'validation', 'summary', 'participants', 'ngRoute']);
...where each dependency is a module with its own $rootScope (SEE COMMENTS BELOW).
I intend on implementing a Mediator -- not an EventHub. EventHubs are simply a skinny-waist to which subsystems can publish and subscribe to broadcast channels on a specific medium. What I want is a true Mediator -- which, according to GoF, actually manages security of how and where modules communicate, in a unicast model.
That said, I cannot solely rely on using $rootScope.$broadcast/$emit as this will allow any & every module to listen to events from other modules -- without any control or intervention (mediation) from a centralized mechanism.
Problem
The problem arises when there is one module firing an event, say, 'excuseFileLog' and other listening for 'excuseFileLog' -- while the mediator is listening to this event from one module and dispatching it for another. Mediator is listening for one module to say "please excuse the 'FileLog'" and telling another that "an excuseFile has been logged". This is very semantic, as excuse, File, and Log are each, both a verb and a noun. So when Mediator dispatches this same event that it, itself, is listening for -- we enter an Infinite Loop:
// mediator is the sole medium for channels
mediator.on('excuseFileLog', function(){
mediator.fire('excuseFileLog');
});
Solvent
Put simply, I need to be able to access each module's $rootScope and fire events on that module alone -- hopfully, without the need of creating a service that will have to be injected into every module; which will encumber the developer with having to remember to implement the service and creating slightly tighter coupling. If a service is involved, then I may as well implement my own mediator/eventing-system, as apposed to leveraging Angular's eventing system. As the rest of the team is somewhat novice, I would prefer to keep 'chores' to a minimum.
Any help is very appreciated :)
Related
Can I create a pub/sub message message queue with RxJS inside angulajs application. I have for example two modules:
ModuleA
ModuleB
They exists as separate npm package but connected in ModuleC - it's the main shell.
I don't want to create a dependency as ModuleD and create a tight coupling between the modules. So my thought is to create a message bus using RxJS.
Is it possible?
I presume an API will look like that:
RxQueue.subscribe("name:of:the:queuemessage", handler => { handler.result } );
RxQueue.create("name:of:the:queuemessage", (observer) => {
// implementation of usual Rx subscribtion
})
I think this might be closest to what you are asking for:
rxmqjs/rxmq.js: JavaScript pub/sub library based on RxJS
https://github.com/rxmqjs/rxmq.js
https://www.npmjs.com/package/rxmq
Not angular specific, but I consider that a good thing.
It might not be exactly what you are looking for but for sharing state and data between different components and modules, you could use redux/ngrx-store.
Redux is an architecture in which you can send state to a store. The store will update itself and notify everyone listening to it if something has changed.
So your modules could both subscribe to the store and listen to events. If they want to communicate, they could send a message to the store. The store would then notify everyone listening if something has changed.
One difference is that this store object will actually store this object as a temp database would. This isn't really queue behaviour.
Checkout http://redux.js.org/ for more information.
I am trying to understand singleton pattern.
I write a lot of code in angular and recently I wanted to refactor some of my code and move it to a common place which can be shared by different controllers.So I moved all common utils to my services.
Now the documentation over here says that angular services are singletons but I want to understand the reason behind having singleton pattern here ? Why not have multiple instances of the service object instead of passing references to controllers ?
It all falls in the name "Service". Service, acts like a medium to communicate between controllers or even directives for that matter. Not only for communication, you can add a set of utility functions in your service, which can be used throughout the module/app. This does not need to have multiple instances to serve the purpose. Hence, singleton.
Angular services are the recommended way to exchange data and communicate between controllers. In order therefore to allow this data exchange, services are singleton objects which means that you are guaranteed to have the same service reference between all your controllers.
As an example, imagine that you have an Angular application that displays a list of messages received from another user in the center of the page, as well as the number of received messages or new message notifications in the header. The header and the page content will most probably be under different scopes and be handled by different controllers. In order for these two controllers to have the same view of messages received and be able to display them consistently they will need to use a singleton object holding this information. This object is an Angular service.
I was wondering what is the preferred way to make different part of an app interact with each other.
For instance let's say we have a directive A that display a product in the shopping basket of a user. This directive has access to a persistence service that allows CRUD operations on the item.
Let's also say we have a directive B that displays a general message.
Now the user decides to delete a product from his basket. Is it acceptable to make it publish an event this way?:
$scope.$emit("item-deleted");
and then have B listening on that type of event:
$scope.$on("data-received", function(event, next, current) {
// show up and tell "item deleted succesfully"
});
Is it a good way to achieve the result? It definitely is in other frameworks and in UI development in general. I was just wondring if it is a viable way in angular.
Thanks
You should use services instead of events propagation. With the dependency injection, it is really easy to use. It is the best way to make two controllers to talk to each other.
More on services : https://docs.angularjs.org/guide/services
create a service for directive B which will provide data for general messages.
inject that service into the underlying service of directive A. then call that service with proper message.
Directive A should auto display this data, because of 2 way data binding of angularjs.
I have two services and a mediator. If I want the mediator to call AND be called by both services, I think that I need to make them dependencies of each other. This causes a circular dependency error.
Person Depends on Mediator to call Mediator methods
Mediator Depends on Person to call Person methods
Is the only solution to use events or promises? Am I implementing this pattern correctly?
this is a bit of a problem that extends beyond angular and into javascript imho.
the standard way to structure mediators to avoid circular dependencies is to use interfaces, but javascript doesn't have interfaces.
in your situation, if you have the mediator injected into the service and vice versa, yes there will be an error.
assuming your mediator is also a service, a work around is not to have dependencies injected into the mediator, but rather have the mediator initialized before it is ever used with the 2 services that you need it to mediate for.
the alternative is not to use a mediator object, but use the publisher/subscriber pattern which is built into angular with $broadcast and $on. imho, this gives even looser coupling than the mediator pattern, so it has my thumbs up
Only your services which utilize Mediator should depend on Mediator. A service which publishes events injects Mediator. A service which subscribes to events injects Mediator and registers callbacks contained within the service. The Mediator should not depend on either service, it should only contain map a publishing events to a list of callback references.
See the Mediator in Angular example I wrote here
I have an application which has some specific (non-trivial) initialization requirements, and it's not really clear what the best practice solution to this is. Sorry for the wall of text. The question itself is not that complex, but I need to make sure my reasoning is clear.
First, the application itself:
It has user authentication, though it is only forced at two points in time:
The first time the application is loaded (the very first time). I'll just call this requirement (1) through the rest of the question.
On a need-to basis when interacting with server-side. This part I have already solved with something similar to http://ngmodules.org/modules/http-auth-interceptor, though a custom solution (which is required because the application needs to use some services that I don't want to be Angular dependent). I'll call this requirement (2) through the rest of the question.
There are two controllers relevant to this question:
A navigation bar controller (fixed, not bound to the view).
The controller applied to the view used (ng-view).
It is started manually using angular.bootstrap.
This question is about the user authentication handling. Requirement (2), where a user has to authenticate on a need-to basis, is already solved. It is currently handled like the following:
Some server-side request is performed by one of my Angular service modules. The request can potentially result in a 401 response if the applied authentication token has expired (or doesn't exist all-together).
The application service module which made the request discovers the 401 response and applies a $rootScope.$broadcast('app:auth').
The authentication broadcast is picked up by some code using $scope.$on('app:auth'), shows a modal authentication dialog, and then makes sure the original service request promise is resolved / rejected (rejected if the user presses cancel in dialog).
The only differences between requirement (1) and (2) is that (1) should be a forced authentication dialog (the user cannot simply reject it with 'cancel' or 'esc'-button) and that (1) should happen as early in application initialization as possible.
Now, my issue is with requirement (1), really, and Angular best practices. There are a couple of ways to do this that I can see:
Perform this one-time authentication outside of Angular completely. The downside here is obviously that I have to write essentially duplicate logic for both the modal dialog box and the initialization. Some of this can be shared, but not all.
Perform this one-time authentication in some special (fixed) controller of the application (like the navigation bar controller).
Perform this one-time authentication in angular.module.run.
The aim here is obviously to "force" an authentication on the user before he (or the application) can trigger something else in the application.
I would love to use number (3), since I would then be able to re-use all code already in use by requirement (1). However, you then instead run into the question of where to place the event-listening code. No controllers / parts of the application are yet started at this point (only the injections are complete).
If I place the logic for authentication events in an application controller, that controller won't even have started at that point, and thus won't have been able to register with the event. If I place the $rootScope.$broadcast inside a $timeout with 0 delay, my navigation bar controller have started, but not my view-bound controller. If I place the $rootScope.$broadcast inside a $timeout with 100 ms delay, both my controllers have started (on MY computer).
The issue obviously being that the amount of delay I need to use is dependent on the computer and exactly what scope the event handler code is placed in. It's also probably dependent on exactly in which order Angular initialize the controllers found through-out the DOM.
An alternative version of (3) might also be to do the $rootScope.$broadcast in angular.module.run, and have the event-listener attached to the $rootScope itself. I'm leaning towards this being the most straith-forward way to do it.
See the following plunker (which tries to higlight the timing issue only): http://plnkr.co/edit/S9q6IwnT4AhwTG7UauZk
All of this boils down to the following best-practice question, really:
Where should application-wide code and non-trivial application initialization code really be placed? Should I consider the $rootScope as the actual "application"?
Thanks!
The short answer :
Application wide code should be in a service.
Application initialization code should be in the run block.
Longer answer :
Application wide code like your Authentication should be defined in a service. This service should expose API's which the rest of your application can interact with in order to achieve that task. Ofcourse the job of the service is to hide the implementation details. The service itself should take care of where it fetches the authentication information from ( initially ) - perhaps from cookies, perhaps from your local storage or session storage.. Or perhaps it even does a http call. But all this gets encapsulated into that Authentication Service.
Because now you have written a separate service and you can inject stuff into your run block you are good to go. You dont really need the $rootScope. The $rootScope is another injected service. But because it participates in the dirty checking mechanism and seemingly this service need not.. you dont need to over burden $rootScope with this additional task. Its not its job and perhaps it can be delegated to some other service whose only task is authentication. Because your service is also a singleton it is amazing at maintaining states as well. You could perhaps set a flag , something like isAuthenticated which can be checked later if need be.
Oh, between your modal should also be a service.. See the $dialog service in Angular UI if you havent already. Which means that authentication can directly work with the $dialog service.
You should put application-wide non-trivial initialization code in providers. Providers offer the most flexibility with regards to initialization, because they can be used to configure the service before the instance of the service is actually created by the $injector.
app.provider('service', function() {
// add method to configure your service
this.configureService = function() { ... };
this.$get = function (/*injectibles*/) {
// return the service instance
return {...};
};
});
The config block is your opportunity to initialize your providers. Inject your provider into your config function (notice the required 'Provider' suffix) and perform any initialization code that you need to setup your provider. Remember, that the provider is not the service - it is the thing that the $injector will use to create your service.
app.config(function(serviceProvider) {
serviceProvider.configureService();
serviceProvider.setTimeout(1000);
serviceProvider.setVersion('1.0);
serviceProvider.setExternalWebService('api/test');
... more configuration ...
};
There are several reasons why providers and config blocks are suitable for initialization:
config blocks are called only once and very early in the application life cycle
providers are configurable - meaning you can initialize the provider before actually creating the service.
The main purpose of the config block is initialization. It supports injection of providers as an opportunity to perform the initialization.
Providers are singletons (like factories and services) - meaning that one service instance is created by the $injector and then shared between all controllers, directives, etc - basically any where that the service is injected.
Now for requirements (1) and (2) - I think you're on the right track. I suggest creating an authLogin directive that shows or hides a modal login dialog based on an "IsAuthenticated" property that is being watched on the scope. This would take care of the requirement to show the login modal dialog when the application starts up. Once the user authenticates successfully, set the IsAuthenticated property to true (which would then hide the dialog).
The second requirement is handled through an HTTP interceptor. When a request is made and the user is not authenticated, the service would broadcast the event starting from the $rootScope downwards towards the child scopes. You can have the same authLogin directive listen for the event and handle it by setting the IsAuthenticated property to false. Since IsAuthenticated is a watched property, it would trigger the modal login dialog so the user can log in again.
There are many ways you could implement requirements (1) and (2). I offered a slight variation on your approach, but in general it is the same approach.