I have a hot RxJS Observable that I want to respond to in different ways depending on the context of the application. The Subject emits a new event based on some global action intercepted by a directive, but then I want
If a child component is subscribing to the Subject, then the child should handle the event
Otherwise, use a global handler
I can get the number of subscribers from the Subject and then tell the global handler to ignore if there are multiple subscribers, but it's not part of the API, so it seems like it may not be the right way to handle it. So what is the right way to do this?
Also, should the global event handler be part of the directive, the service, or should that be in a new component?
You can put global event subject in a global app.service and inject it in other component for subscription.
Although the ideal component should just have its own service maybe to handle complex event, but sometimes I feel directly inject a global service keeps the code cleaner. Otherwise, if you really want complete isolation or the component should be widely reusable e.g UI dropdown list, I suggest to use #Output to fire events (btw angular EventEmitter inherit Subject) and #Input to take in variables.
Related
I have a component that will have a set of states, in which each state has a set of UI events and response methods. Some of the UI events may not be included in all the states.
My current idea is to create a controller interface including methods for each possible UI event that the component can then call in response to each event. Each state will then have a corresponding implementation of the interface. The main issue here is that many of the concrete controllers will have empty methods being called.
To avoid this issue, the only solution I can think of is to not even use Angular directives and have each concrete controller create its own event listeners. While this would avoid the previous issue, I am wondering whether there's a better approach relying on Angular features.
I study angular, sorry for the stupid question.
I need to make a service for windows with a single instance of the service or be able to send broadcast events and listen to them in each component.
Using #input and #output is not like There is a different nesting of components.
How to solve this issue?
One way to implement this is to use a service for that.
Inside the service create an Subject member variable (you need RxJS for it, which angular already installed because it's a dependency).
The Subject can be subscribed by any component outside the service (you'll need to inject the service in the component constructor).
Then, whenever you want to trigger that event you call .next() on the Subject and each subscriber will activate its own callback.
When subscription is done and not needed anymore it's important to .unsubscribe() from the Subject, otherwise there's a risk of memory leak (since the subscriber does not unsubscribe itself).
There are many examples out there, here is simple one.
I'm looking for some guidance with regards to managing state in a real-time messaging/chat app built with VueJS 2.
The app consists of several components which are outlined in the following diagram:
So far, I've implemented displaying (fake) conversations. The App component contains an array with conversation objects. For each child component, the relevant data is passed using props. This is really simple and works like a charm.
Now, I have to deal with actions/mutations from components deeply nested in the tree. For example, sending a message and appending it to the corresponding array of messages.
I figured it would be as easy as dispatching a (global) event in the AppConversationChatWindowInput component and handling it in the App component. Boy was I wrong. Apparently, this functionality was removed when Vue 2.0 was introduced in favor of Vuex. I'm not sure why it was removed, because in some situations this could be a perfectly reasonable way to deal with events.
I guess there are a couple of possible solutions:
Passing the websocket connection to each child component. This could technically work. The App would connect to the websocket server and pass this connection to its child components using props. When the user sends a message, it is echoed by the websocket server. The App component can listen for the message and append it to the array of messages.
Regardless of the technical feasability, this feels like a crappy and hard-to-maintain archicture to me. In my opinion, no component other than the App should be aware of the websocket connection, let alone its concrete implementation.
Manually bubbling up the event in each component in the chain.
This seems like a complete pain to maintain. Introduces a lot of needless complexity and failure points.
Using a global event bus.
This is possible, but why should an input field depend upon a global event bus? I don't like unnecessary dependencies and coupling. It adds complexity and makes things harder to test.
Using a global data store (Vuex).
See #3. Another dependency and added complexity. Also, if I would settle for Vuex, how would I retrieve data in my components? Do I pass it down using components (like I do now) or would a component deep down in the tree just grab it from the store directly? To me, it feels like the component knows a lot more than it should this way.
Any thoughts? What's the best way to handle state in my situation?
There's a bit of a disconnect between "I wanted to dispatch a global event" and "I don't want to use a global event bus." A global event bus is how you dispatch/broadcast a global event. It is, as you note, a good solution in some situations. It is not hard to set up when you need it, so there's no strong reason for it to be in core Vue.
You can create the bus as an instance property on Vue so it is available to every component:
Vue.prototype.$globalEventBus = new Vue();
Where you would have had vm.$dispatch(...) you would do vm.$globalEventBus.$emit(...) and the receiving component can set up vm.$globalEventBus.$on(...).
Alternatively, you could create a bus at the top level and pass it through the children as a prop. This avoids globals, and you don't have to worry about bubbling.
Finally, as I noted in my comment, native events bubble, and you can catch them at any component higher up the chain. You could catch the event(s) that send messages, or even roll your own events to catch.
I am new to Javascript and AngularJS. Both seem to me as an event listener. What are the differences? Can I use them interchangeably? How?
$scope.$on will catch events that are.$broadcast() on the $scope whereas addEventListener listens to any events on the page. They're similar but not interchangeable.
If working in an angular app, I would definitely use $scope.$on unless otherwise needed, i.e. catching events from outside of the angular app. You will gain testability if you're writing unit tests and will only be looking for events from your own code, which is probably what you're looking for.
No. They operate on different objects, and are not interchangeable, although they are very similar in what they do. Both add an event listener to an object which can emit events, but scopes are not HTMLElements (like document), and different events are fired on scopes than on HTMLElements.
$scope.$on should be used wherever possible. Usually if there is an Angular way to do something, you should do it that way.
$scope.$on is used in conjunction with $scope.$emit which sends data to all parent controllers, and $scope.$broadcast which sends data to all child controllers. This gives you greater control over the flow of data through the app. Furthermore, handling events inside of a native event listener will break 2-way data binding.
I am using angular-js. I have a service that need to trigger event each time something occurs. For doing it I need an object that will act as event aggregator.
Do I need to build one? Or should I use the $rootScope?
In case I should use $rootScope, how can I ensure that there is no event names conflicts?
Is it efficient to use $rootScope for events that don't need them to propagate to child scopes?
I modeled and implemented the following mechanism in a web project for tablets:
Define notifications in your services. I did not want to use the term events since I did not want it to be confused with DOM events by other developers in my team. And a semi-typed name for a notification is easier for IDE with intellisense support and for debugging. For example, I have one Device service that would $broadcast(Device.Notification.OrientationDidChange) when a tablet device's orientation is changed.
Use Scope objects to $broadcast or $emit notifications, depending on your need. For example,
For global notifications like the previous one, I do: $rootScope.$broadcast(Device.Notification.OrientationDidChange). So all listeners can listen on their own scope without injecting $rootScope.
For local notifications that could possibly only affect the current scope (and its children), such as a notification that a scope needs to tell all its child scopes that the layout of the current controller is changed, usually I do: scope.$broadcast(UI.Notification.NeedsLayout), where UI is a predefined service to hold UI related constants, and scope is the current scope.
For certain notifications that a child scope needs to send upwards, for example, a slider directive that tells ascendant scopes that the rangeStart value has changed (in addition to regular two way binding), I use: scope.$emit(Slider.Notification.RangeStartDidChange), where scope is the current scope.
This approach is a bit verbose in a small project. You might want to stick to using $rootScope.$emit(Notification) all the time, and let all listeners to do $rootScope.$on(Notification, callback) to receive these notifications.
In some situations, you might want to define these notifications in a centralized service in order to more easily avoid name conflicts. It really based upon your project's naming convention.
The implementation (actual values) of these notifications may vary. I prefer using strings.
With $broadcast or $emit you can also actually pass additional arguments to the listeners, for example, $broadcast(Notification, arg1, arg2)... Angular's documentation is quite detailed.
Take a look at http://docs.angularjs.org/api/ng.$rootScope.Scope#$broadcast.
Using $rootScope as event aggregator is perfectly fine unless you are triggering events outslide digest cycle or triggering multiple (100+) events at the same time where some other solutions may be more appropriate .
One accepted practice would be to use namespacing (namespace:event - pattern used by Backbone.Marionette)
Use $emit on a child scope instead of $broadcast on $rootScope - $emit propagates only upwards, where $broadcast propagates downwards - to all children