mls3590712
mls3590712

Reputation: 810

clearTimeout when wrapped in closure?

Updated

HTML

<div class="notification-container" data-bind="foreach: notificationArray">
     <notification params="data: $data"></notification>
</div>

JS - using KnockoutJS to create an observable array of 'notification' messages.

appViewModel.notificationArray = ko.observableArray([
    { message : 'Test 1' },
    { message : 'test 2' }
]);

Using Knockout to create a notification component

ko.components.register('notification', {
    viewModel: function(params) {
        var data = params.data;

        /* set the message to the data.message */
        this.message = data.message || null;

        /* removes the notification from the array */
        this.removeNotification = function() {
            appViewModel.notificationArray.remove(data);
        };

        /* create timer to remove notification after 5s */
        /* need to wrap in closure so that inside of the setTimeout it can know about the data object needed to send to the remove() command */
        this.timer = function(obj, timeoutLength) {
            /* adding return statement per suggestion on Stack Overflow */
            return setTimeout(function() {
                appViewModel.notificationArray.remove(obj);
            }, timeoutLength);
        };

        this.timer(data, 5000);

       /* log will output function structure */
       /* clearTimeout will not work */
        this.hover = function() {
            console.log(this.timer);
            clearTimeout(this.timer);
        }

    },
    template: '<div class="notification show-notification" data-bind="event: { mouseover: hover, fastClick: hover }">'
        +'<div class="notifications-close clickable right" data-bind="fastClick: removeNotification"><span class="icon icon-x"></span></div>'
        +'<div class="notification-text" data-bind="text: message"></div>'
        +'</div>'
});

Updating to reflect working solution

JS

appViewModel.notificationArray = ko.observableArray([
    { message : 'Test 1' },
    { message : 'test 2' }
]);


ko.components.register('notification', {
    viewModel: function(params) {

        var data = params.data;

        this.message = data.message || null;
        this.timer = null;

        this.removeNotification = function() {
            appViewModel.notificationArray.remove(data);
        };

        this.timer =  ( function(self) {
            return setTimeout(function() {
                self.removeNotification();
            }, 5000);
        })(this);

        this.hover = function () {
            clearTimeout(this.timer);
        };

        this.restart = function() {
            this.timer = ( function(self) {
                return setTimeout(function() {
                    self.removeNotification();
                }, 5000);
            })(this);
        }

    },
    template: '<div class="notification show-notification" data-bind="event: { mouseover: hover, fastClick: hover, mouseout: restart }">'
        +'<div class="notifications-close clickable right" data-bind="fastClick: removeNotification"><span class="icon icon-x"></span></div>'
        +'<div class="notification-text" data-bind="text: message"></div>'
        +'</div>'
});

Upvotes: 0

Views: 564

Answers (2)

traktor
traktor

Reputation: 19301

Edit: this answer started before that accepted answer was posted. The issues identified are similar with slightly different solutions.

1) You can replace the whole this.timer = statement with

var timerId =  setTimeout(function(){
    appViewModel.notificationArray.remove(data), 5000);
    timerId = 0;  // timer finished
};

then timerId is in function scope of the anonomous function passed to setTimeout (a.k.a. "a closure") where it, and data, can both be seen.

2) The hover function can make use of closure as well.

this.hover = function() {
    console.log("timer " + timerId ? timerId : "finished");
    if( timerId)
        clearTimeout(timerId); 
}

Upvotes: 0

Daniel A. White
Daniel A. White

Reputation: 190925

You aren't setting this.timer to the result of setTimeout. Perhaps you need return setTimeout?

Now for your 2nd problem. this.hover is invoked with this as something else. This has been solved in numerous other questions. One way is to use var self = this in the right scope for the correct this or my current preference is this.hover = function() {...}.bind(this);.

Upvotes: 4

Related Questions