0

I'm implementing a basic "shopping Cart" where you can change product and it recalculates the total price.

I would like to access both the instance and jQuery's this instance within the same method, in particular product_change().

class Cart
  constructor: ->
    @bind_listeners()
  bind_listeners: ->
    $('td.product').on 'change', 'select',@product_change
  update_prices: ->
    # For each row of items get span.subtotal and sum
    # Replace content of "total" field with result
  product_change: ->
    # Get new product's price. I need jQ's 'this'
    new_price = $(this).find ':selected'
    # Replace subtotal field
    $('span.subtotal').html new_price
    # Update all prices. I need instance's 'this'
    @update_prices()

My working solution right now is to call update_prices as another binded method to the change event, using fat arrow =>. However I'd rather have a prettier alternative.

class Cart
  constructor: ->
    @bind_listeners()
  bind_listeners: ->
    $('td.product').on 'change', 'select',@product_change
    # Call update_prices here
    $('td.product').on 'change', 'select',@update_prices
  update_prices: ->
    # For each row of items get span.subtotal and sum
    # Replace content of "total" field with result
  product_change: ->
    # Get new product's price. I need jQ's 'this'
    new_price = $(this).find ':selected'
    # Replace subtotal field
    $('span.subtotal').html new_price
    # Update all prices. I need instance's 'this'
    #@update_prices()

1 Answer 1

1

Instead of using the value of this that jQuery sets when it calls an event handler, use the target jQuery passes to the event handler. They're both the same object: The DOM element that triggered the event.

So your code becomes:

class Cart
  constructor: ->
    @bind_listeners()
  bind_listeners: ->
    $('td.product').on 'change', 'select', @product_change.bind(@)
  update_prices: ->
    # For each row of items get span.subtotal and sum
    # Replace content of "total" field with result
  product_change: (e) ->
    # Get new product's price. I need jQ's 'this'
    new_price = $(e.currentTarget).find ':selected'
    # Replace subtotal field
    $('span.subtotal').html new_price
    @update_prices()

(Note that I've used .bind to prevent the value of this from being overridden when jQuery calls product_change. You could, alternatively, define the method with => to accomplish the same thing.)

Using this in methods to consistently refer to the object those methods are attached to, rather than something else, will make your life as a CoffeeScripter much easier.

Sign up to request clarification or add additional context in comments.

6 Comments

What version of jQuery is this? jQuery passes the event object as the first param; you can get the target from the event object...?
Fantastic answer, thanks. Your advice about this usage makes a lot of sense to me, thanks! However, regarding the use of => to accomplish the same thing as .bind, I guess I'd need to pass the jquery object to the product_change method? e.g. $('td.product').on 'change', 'select', product_change($('td.product select')). So that in the scope of the method, e equals the DOM element?
@vint-i-vuit Sorry, the confusion is my fault. You want to use e, the event object that jQuery passes as the first argument to the event handler. That has the DOM element that triggered the change event attached, as e.target/e.currentTarget.
@vint-i-vuit You don't have to pass the jQuery object ($('td.product select')) in to the event handler, because you can create a new jQuery object that wraps around the same DOM element with $(e.currentTarget) within the event handler. e—the first arg passed to the event handler—is an event object, as documented here.
If you really want to use the same jQuery object (to prevent the small performance cost of creating a new one with $(e.currentTarget)), you could instead attach that object as a property on the Cart instance. So for instance, if you set @$select = $('td.product select') in bind_listeners, then you could use @$select within product_change.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.