prashn64
prashn64

Reputation: 657

Access method of jquery/coffeescript plugin from outside the plugin

I know this question has been asked several times, but all the answers confuse me, because I am unsure of how to get the coffeescript code to compile into that specified jquery code.

This is what I have so far:

pluginName = 'tagbox'

states = 
  none: 0
  typing: 1
  altering: 2        

defaults =
  editing: true
  tags: []

class Plugin
  constructor: (@element, options) ->
    @options = $.extend {}, defaults, options
    @_defaults = defaults
    @_states = states
    @state = states.none
    @_name = 'tagbox'
    @currentTag = $("<div class='ui-individualtag'></div>")

  # this is the public method I want
  setCurrentTag: (tagText) ->
    @currentTag.text(tagText)

$.fn[pluginName] = (options) ->
  @each ->
    if !$.data(this, "plugin_#{pluginName}")
      $.data(@, "plugin_#{pluginName}", new Plugin(@, options))
)(jQuery, window, document)

and then in another script, I want to be able to access the setCurrentTag method like this:

tagbox = $('#tagbox').tagbox()
tagbox.setCurrentTag("hello world")

Let me know if it will help to show what this compiles into in jquery.

Upvotes: 1

Views: 462

Answers (1)

mu is too short
mu is too short

Reputation: 434665

Your problem is that tagbox:

tagbox = $('#tagbox').tagbox()

will be a jQuery object and that won't have a setCurrentTag method. You don't want to try to change this as that will break the usual jQuery chaining behavior. One way to solve this is to give the tagbox plugin enough intelligence to parse its arguments so that you can pass a method name in:

$('#tagbox').tagbox('setCurrentTag', 'hello world')

This is the approach that jQuery-UI takes so it should be familiar to a lot of jQuery people.

All you'd need to do is modify your $.fn.tagbox to look more like this:

$.fn[pluginName] = (options = {}) ->
  if $.isPlainObject options
    @each ->
      if !$.data(@, "plugin_#{pluginName}")
        $.data(@, "plugin_#{pluginName}", new Plugin(@, options))
  else
    args = Array.prototype.slice.call(arguments);
    @each ->
      p = $.data(@, "plugin_#{pluginName}")
      p[args[0]](args[1])

Demo: http://jsfiddle.net/ambiguous/q2U7d/

Some things to note:

  1. I've added a default ((options = {}) ->) so that options will always be there.
  2. We can check if we have an options object using $.isPlainObject, if there no options then we can assume that we're doing a method call instead. This is why we want the default in 1.
  3. We use Array.prototype.slice.call as a hack to convert the Array-like arguments to a real array to make it easier to work with. We don't have to do this but it can save some confusion if we want to use Array methods (such as pop) and we need a reference to the outer arguments inside the @each callback anyway: arguments is context sensitive just like this is in (Coffee|Java)Script.

Upvotes: 2

Related Questions