Frage

Intro: I'm working on a web app using ui-router, and using nested views/controllers. There are several UI elements across the app that need to communicate, and who's actions may be dependent on another one's state.

Example: For example, I have a "dropdown" element that functions like a typical dropdown menu. A "dropdown" could exist in any controller, but only one should be open at any given time. That is, if the user opens a dropdown menu, it should also close all other dropdown menus.

Proposed Solution: My current solution is to $emit an event when a "dropdown" is clicked on, listen for it in the top-most parent controller, and $broadcast an even to close all dropdowns, then catch that event in every other controller.

There are other elements whose state I would like to keep track of, such as the main sidebar (expanded, or collapsed), with a more complicated array of events (size of screen, clicking on an item in the sidebar, clicking on a "menu" button) it seems...messy to have to bounce events around.

tl;dr: Is the standard way to communicate a UI element's state (visible/invisible, expanded/collapsed) across controllers to $emit and $broadcast events?

Edit: Another option I thought of is to create a $watch in the children controllers to keep track of a global "ui element state" variable in the top most parent controller. Similar to what I described above, but without the $broadcast.

War es hilfreich?

Lösung 2

Experience and new best practices have led me to different solutions:

I eventually solved this many years later with a Service dedicated to broadcasting events. Rather than using $rootScope everywhere, this service gets $injected and used instead.

This service can then send custom events to any controllers listening, which allows you to close menus based on.


I have also solved this with a global click event listener. Each dropdown creates a listener, and will close itself if it notices a click that is outside of its element's $scope.

Andere Tipps

I think you're going down the right track. I'm not a huge fan of throwing events all over the place so I like to avoid it when I can. One thing you could do is try to hook into the browser's built-in eventing. For example, with the dropdown example, instead of the user opening another dropdown, you could just listen to a document.click event and hide the dropdown when the document is clicked.

Regardless, another thing I would recommend is to make sure that when you're emitting the event or listening to it, to write out the full string instead of building it. For example:

Don't do this:

var postEventBase = 'post.';
$rootScope.$on(postEventBase + 'created', function() {});
$rootScope.$on(postEventBase + 'deleted', function() {});
$rootScope.$on(postEventBase + 'updated', function() {});

Do this instead

$rootScope.$on('post.created', function() {});
$rootScope.$on('post.deleted', function() {});
$rootScope.$on('post.updated', function() {});

Because one of the real issues with events going everywhere is you wind up with things becoming dependent on events or reacting to events when you don't expect it to. You need to make it so that it's grep-able. Just a general tip on events. It'll help you avoid nightmares.

Anyway, as far as your philosophical question, I think that when you have to, using events is a great way to decouple code while still communicating things throughout your app. Just be careful with it.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top