How can two event handlers coexist?

Sure. I would have expected mixins to be a bit more like black boxes, or at least attempt to. If I have a mixin, and I use it on my code, I would have thought I didn’t need to think “oh, but now I have to do this._super() here because MyMixin handles this action too”.

The case of Ember.View that you mention exposes a new problem: if I use several mixins together, now I have to start thinking about what handlers they may implement, whether there’s a correct order for their inclusion, and even if they can be used together at all. Of course, it’s ok that a given pair of mixins are incompatible, but this limitation adds what I think is an additional, artificial constraint.

Of course, my use case may be wrong, and I should be addressing it differently. I’ll explain it again in more detail.

I have a hamburger menu. You click on it and links to different sections appear. When you click on one of these links, the menu should close and the user should be led to the selected section.

I’m implementing it as follows. The menu belongs in the Application route, and shows/hides depending on the value of a property showingNav:

# app/routes/application.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return Ember.Object.create({
      showingNav: false,
    });
  },

  actions: {
    closeNav() {
      const model = this.modelFor('application');
      model.set('showingNav', false);
    },

    openNav() {
      const model = this.modelFor('application');
      model.set('showingNav', true);
    },
  }
});

The closeNav action is triggered on willTransition of each individual route. Since that’s something I’ll have to add to each affected route, I thought of putting it on a mixin:

# app/routes/navigable-route.js
import Ember from 'ember';

export default Ember.Mixin.create({
  actions: {
    willTransition() {
      this.send('closeNav');
    },
  },
});

But there are routes that have their own reasons for handling willTransition. To be specific, this is as explained Issue with Ember Data and Creating a Record - #3 by pablobm for which, incidentally, I’d also love some feedback!

So now I am using the mixin, but I have to be aware that it relies on willTransition, and I have to explicitly say this._super() where appropriate:

import Ember from 'ember';
import NavigableRoute from 'my-app/routes/navigable-route';

export default Ember.Route.extend(NavigableRoute, {
  model() {
    return this.store.createRecord('player');
  },

  actions: {
    save() {
      const record = this.modelFor('players.new');
      record.save()
        .then(() => {
          this.transitionTo('root.show');
        })
        .catch(error => {
          console.error("Error saving player", error);
        });
    },

    willTransition() {
      this._super();
      const record = this.controllerFor('players.new').get('model');
      if (record.get('isNew')) {
        return record.destroyRecord();
      }
    },
  },
});

Months later, I come back and I make a change to the mixin. Maybe there’s been a deprecation, or I have found a better way to implement it. Now it doesn’t require willPaginate, but something else. I make the change and now I have those this._super() calls scattered around that serve no purpose, and may confuse developers to come later, wondering why that code is there in the first place. Or the new implementation relies on a different action, and now I have to go to the routes that use it, and make sure that there isn’t a new interference.

And hence my hoping that mixins were be black boxes.

I found something that would be helpful at javascript - Multiple Mixins with same events in Ember.js - Stack Overflow . It would work like this:

# app/routes/navigable-route.js
import Ember from 'ember';

export default Ember.Mixin.create({
  sendCloseNav: Ember.on('willTransition', function() {
    this.send('closeNav');
  }),
});

That appears to promise to do that I ask, attaching several actions to the same event, same as several DOM event handlers can be respond to the same trigger together. But it doesn’t work! It’s from early 2014, so I’m going to assume it’s deprecated functionality of some sort, or route events work differently, or something like that.

All right, I hope that makes sense? Thank you for your interest, and for reading this far.