0

I have created a Javascript object that adds a click listener onto certain classes which then in turn calls a PHP function to generate results from a MYSQL/PHP driven backend. The backend is working fine (and the results are being returned correctly). I am having an issue where the instance thinks that a property (a string) of the instance is empty, even after I have directly assigned values to that property.

Here is my code:

//Workout Videos Class
var Workout = function(offset, limit, element, categoryId) {
    this.offset = offset;
    this.html = "";
    this.limit = limit;
    this.element = element;
    this.categoryId = categoryId
    var self = this;

    jQuery(this.element).on("click", function(e) {
        e.preventDefault();
        self.seeMoreClick(this, self.videoCallBack, self)
    });
}

Workout.prototype.seeMoreClick = function(item, videoCallBack, thisArg) {
    jQuery.ajax({
        type      :   'POST',
        dataType  :   'json',
        url       :   'api/get-videos',
        data      :   'category_id=' + this.categoryId + "&offset=" + this.offset + "&limit=" + this.limit,
        success   : function(data) {
            videoCallBack.call(thisArg, data, item);
        }
    });
}

Workout.prototype.videoCallBack = function(response, item) {
    this.offset = response.offset;
    if(response.videos) {   
        jQuery(response.videos).each(function(key, video) {                     
            this.html += "<div class='col-md-4' data-related-video='" + video.videoId + "'>";
                this.html += '<a href="#" class="dashboard-video"></a>';
                this.html += '<div class="video-information">';
                    this.html += '<h5 class="tk-bebas-neue">' + video.videoName + '</h5>';
                    this.html += '<p>' + video.videoDesc + '</p>';
                    this.html += '<p>Equipment: ';
                    jQuery(video.equipment).each(function(equipKey, equipmentItem) {
                        this.html += '<a href="#" class="equipment-item" data-equipment-id="' + equipmentItem.equipmentId + '">' + equipmentItem.equipmentName + '</a>';
                    });
                    this.html += "</p>";
                    this.html += '<div class="video-rating" data-rel="' + video.videoId + '" data-user-id="' + video.userId + '">'; 


                    this.html += "</div>";
                this.html += '</div>';
            this.html += "</div>";              
        });
    } else {
        this.html += "<div class='col-md-12'>No more videos to show</div>";
    }
    console.log(this.html);
    jQuery("#" + jQuery(this.element).attr("data-related") + " .col-md-8").append(this.html);
}

The problem I am having is in the 'videoCallBack' function which I am using to pass to the AJAX function in order to ensure it resolves before all the code is run. The 'this.html' property is showing as empty when I log it in the console directly after setting it. Can anyone see why this may be happening? Thanks

Jim
  • 2,494
  • 5
  • 33
  • 72
  • watch your uses of `this`. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this – Daniel A. White Nov 18 '14 at 14:53
  • possible duplicate of [JavaScript "this" keyword](http://stackoverflow.com/questions/3127429/javascript-this-keyword) – Artjom B. Nov 18 '14 at 15:23
  • Ok, found a better duplicate: http://stackoverflow.com/questions/4886632/what-does-var-that-this-mean-in-javascript – Artjom B. Nov 18 '14 at 15:41

3 Answers3

3

This is a result of this changing in your code. Inside the forEach block 'this' refers to a different object, so you're adding all that HTML to a completely different object.

A simple fix is to save off a reference to this:

Workout.prototype.videoCallBack = function(response, item) {
    var self = this;
    self.offset = response.offset;
    if(response.videos) {   
        jQuery(response.videos).each(function(key, video) {                     
            self.html += "<div class='col-md-4' data-related-video='" + video.videoId + "'>";
           ...
sma
  • 8,798
  • 7
  • 46
  • 76
2

When you have a function execution, you need to be mindful of the value of this since it'll change based on how the function is called. It's never automatically inherited from its enclosing environment.

So in your case, the callback you pass to .each() has a this value that's different from the outer environment. You'll need to bind the this value or store it in a varaible and reference the variable inside the callback.

To bind it, you can use $.proxy().

 // -----------------------------v
jQuery(response.videos).each($.proxy(function(key, video) {   

    this.html += "<div class='col-md-4' data-related-video='" + video.videoId + "'>";
    // ...and so on...       

}, this));
//   ^

or the native .bind() method in modern browsers.

jQuery(response.videos).each(function(key, video) {   

    this.html += "<div class='col-md-4' data-related-video='" + video.videoId + "'>";
    // ...and so on...       

}.bind(this));
//  ^

Both of these will return a new function that has the this value permanently bound to the value you provided. This avoids having to add another variable to the enclosing scope.

six fingered man
  • 2,300
  • 9
  • 15
1

Because you're not accessing the right 'this'. Within the scope of your each function another 'this' is present. Avoid this by setting a variable

var self = this;

And use that instead to preserve your context.

this.html to self.html
Viktor
  • 257
  • 1
  • 5