Ember, mixin for click detection outside of view / component

I am writing Mixin to handle when the user clicks outside of the view / component.

This is mixin:

App.ClickElsewhereMixin = Ember.Mixin.create({

  onClickElsewhere: Ember.K,

  didRender: function() {
    this._super.apply(this, arguments);
    return $(document).on('click', this.get('onClickElsewhere'));
  },

  willDestroyElement: function() {
    this._super.apply(this, arguments);
    $(document).off('click', this.get('onClickElsewhere'));
  },
});

I use it in my component:

onClickElsewhere: function() {
    this.send('exitEditMode');
},

But when I run it, I get:

TypeError: this.send is not a function

How can I keep the context this?


Decision:

to facilitate the reader, here's a working mixin:

App.ClickElsewhereMixin = Ember.Mixin.create({

  onClickElsewhere: Ember.K,

  setupListener: Ember.on('didRender', function() {
    // Set an event that will be fired when user clicks outside of the component/view
    return $(document).on('click', $.proxy(this.get('onClickElsewhere'), this));
  }),

  removeListener: Ember.on('willDestroyElement', function() {
    // Clean the previously defined event to keep events stack clean
    return $(document).off('click', $.proxy(this.get('onClickElsewhere'), this));
  }),
});
+4
source share
5 answers

The current answer does not check if the click was really outside the element - clicking on the component also causes a callback.

Here's the updated version:

export default Ember.Mixin.create({
  onOutsideClick: Ember.K,

  handleOutsideClick: function(event) {
    let $element = this.$();
    let $target = $(event.target);

    if (!$target.closest($element).length) {
      this.onOutsideClick();
    }
  },

  setupOutsideClickListener: Ember.on('didInsertElement', function() {
    let clickHandler = this.get('handleOutsideClick').bind(this);

    return Ember.$(document).on('click', clickHandler);
  }),

  removeOutsideClickListener: Ember.on('willDestroyElement', function() {
    let clickHandler = this.get('handleOutsideClick').bind(this);

    return Ember.$(document).off('click', clickHandler);
  })
});
+7
source

Greg's answer has an error, so removing the clickHandler event does not work. This means that your clickevent will fire even if you destroy the component.

Here is the correct version

import Ember from 'ember';

export default Ember.Mixin.create({
    onOutsideClick: Ember.K,

  handleOutsideClick: function(event) {
    let $element = this.$();
    let $target = $(event.target);

    if (!$target.closest($element).length) {
      this.onOutsideClick();
    }
  },

  setupOutsideClickListener: Ember.on('didInsertElement', function() {

    let clickHandler = this.get('handleOutsideClick').bind(this);

    return Ember.$(document).on('click', clickHandler);
  }),

  removeOutsideClickListener: Ember.on('willDestroyElement', function() {

    let clickHandler = this.get('handleOutsideClick').bind(this);

    return Ember.$(document).off('click', Ember.run.cancel(this, clickHandler));
  })
});
+6

Ember.run.bind. .

App.ClickElsewhereMixin = Ember.Mixin.create({

  onClickElsewhere: Ember.K,

  setupListener: Ember.on('didRender', function() {
    this.set('clickHandler', Ember.run.bind(this, this.onClickElsewhere));
    Ember.$(document).click(this.get('clickHandler'));
  }),

  removeListener: Ember.on('willDestroyElement', function() {
    Ember.$(document).off('click', this.get('clickHandler'));
  }),
});
+3

:

App.ClickElsewhereMixin = Ember.Mixin.create({

  onClickElsewhere: Ember.K,

  didRender: function() {
    this._super.apply(this, arguments);
    return $(document).on('click', function(this){ return this.get('onClickElsewhere'); }(this));
  },

  willDestroyElement: function() {
    this._super.apply(this, arguments);
    $(document).off('click', function(this){ return this.get('onClickElsewhere'); }(this));
  },
});

Bind

App.ClickElsewhereMixin = Ember.Mixin.create({

  onClickElsewhere: Ember.K,

  didRender: function() {
    this._super.apply(this, arguments);
    return $(document).on('click', this.get('onClickElsewhere').bind(this));
  },

  willDestroyElement: function() {
    this._super.apply(this, arguments);
    $(document).off('click', this.get('onClickElsewhere').bind(this));
  },
});

.

, , sendAction send (http://guides.emberjs.com/v1.10.0/components/sending-actions-from-components-to-your-application/)

Edit

jQuery.proxy call/apply . . call/apply vs bind.

+2

You can use lib ember-click-outside . Worked for me.

+1
source

All Articles