Question

I have this little class:

var SwitchHider;

SwitchHider = {
  inputSelector: 'body.socials input',
  _showOrHideClosestSwitch: function(element) {
    var socialUrl, swich_wrapper;
    socialUrl = $(element).val();
    swich_wrapper = $(element).closest('.white-box').find('.switch');
    if (socialUrl.length > 0) {
      return swich_wrapper.show();
    } else {
      return swich_wrapper.hide();
    }
  },
  _bindCallbacks: function() {
    $(document).on('ready', function() {
      return $(self.inputSelector).each(function(index, element) {
        return self._showOrHideClosestSwitch(element);
      });
    });
    return $(document).on('change keyup input', self.inputSelector, function() {
      return self._showOrHideClosestSwitch(this);
    });
  },
  initialize: function() {
    var self;
    self = this;
    this._bindCallbacks();
  }
};

window.SwitchHider = SwitchHider;

$(function() {
  SwitchHider.initialize();
});

(it's compiled from this CoffeeScript file, hope it makes sense):

SwitchHider =
  inputSelector: 'body.socials input'

  _showOrHideClosestSwitch: (element) ->
    socialUrl = $(element).val()
    swich_wrapper = $(element).closest('.white-box').find('.switch')
    if socialUrl.length > 0
      swich_wrapper.show()
    else
      swich_wrapper.hide()

  _bindCallbacks: ->
    $(document).on 'ready', ->
      $(self.inputSelector).each (index, element) ->
        self._showOrHideClosestSwitch(element)

    $(document).on 'change keyup input', self.inputSelector, ->
      self._showOrHideClosestSwitch(@)

  initialize: ->
    self = this
    @_bindCallbacks()
    return

# Send to global namespace
window.SwitchHider = SwitchHider

# DOM Ready
$ ->
  SwitchHider.initialize()
  return

And when event is triggered - it gives this error:

TypeError: self._showOrHideClosestSwitch is not a function

Does anyone see why? It's my first attempt to write classes like that, don't fully understand what I'm doing.

Was it helpful?

Solution

self is not in scope. This is begging for a class syntax:

class SwitchHider
  @inputSelector: 'body.socials input'
  constructor: ->
    $(document).on 'ready', =>
      $(SwitchHider.inputSelector).each (index, element) =>
        @_showOrHideClosestSwitch(element)

    $(document).on 'change keyup input', SwitchHider.inputSelector, (e) =>
      @_showOrHideClosestSwitch(e.target)


  _showOrHideClosestSwitch: (element) ->
    socialUrl = $(element).val()
    swich_wrapper = $(element).closest('.white-box').find('.switch')
    if socialUrl.length > 0
      swich_wrapper.show()
    else
      swich_wrapper.hide()

# Send to global namespace
window.SwitchHider = SwitchHider

# DOM Ready
$ ->
  new SwitchHider()

I used the => to handle proper scope of this

OTHER TIPS

self is only defined as a local variable under initialize. It won't be available to other methods.

  initialize: function() {
    var self;
    self = this;
    this._bindCallbacks();
  }

You'll either have to define a local self within each method that needs it:

  _showOrHideClosestSwitch: (element) ->
    self = this
    # ...

  _bindCallbacks: ->
    self = this
    # ...

Or, you can use fat arrows to bind each callback to the surrounding context and use this or @ instead:

  _bindCallbacks: ->
    $(document).on 'ready', =>
      $(@inputSelector).each (index, element) =>
        @_showOrHideClosestSwitch(element)

    $(document).on 'change keyup input', self.inputSelector, (ev) =>
      @_showOrHideClosestSwitch(ev.currentTarget)

In the event handler, the Element that's normally the context value can still be retrieved via event.currentTarget.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top