Cant detach functions from events via the off() API method (ES6)

Cant detach functions from events via the off() API method (ES6)

jLinuxjLinux Posts: 981Questions: 73Answers: 75
edited November 2015 in Free community support

I was able to attach a static function to events via the on(), and it works fine. But I'm having issues detaching the event using the off() method. Ive tried a few different variations of the off() method with no success.

Heres the JS ES6 class with the methods used to attach/detach the function and event, as well as the actual function that gets attached/detached

class myPlugin{
    constructor (dtSettings){
        this._dtApi      = new $.fn.dataTable.Api( dtSettings );
        this._dtSettings = dtSettings;

        dtSettings.oMyPlugin = this;
    }

    attach(){
        var eventParams = {
            dtSettings: this._dtSettings,
            dtApi:      this._dtApi
        };

        this._dtApi.on( 'draw.dt', eventParams, myPlugin.event.bind( myPlugin ) );
    }

    detach(){
        var eventParams = {
            dtSettings: this._dtSettings,
            dtApi:      this._dtApi
        };

        // None of the following seem to work..
        this._dtApi.off( 'draw.dt', eventParams, myPlugin.event.bind( myPlugin ) );
        this._dtApi.off( 'draw.dt', myPlugin.event.bind( myPlugin ) );
        this._dtApi.off( 'draw.dt', eventParams, myPlugin.event );
        this._dtApi.off( 'draw.dt', myPlugin.event );
    }

    static event(e){
        var { dtSettings, dtApi } = e.data;

        alert('Triggered draw.dt for table ID #'+dtSettings.sTableId);
    }
}

And heres how that class is getting used

((window, document, $, undefined) => {
    $( document ).on( 'init.dt', ( e, dtSettings ) =>  {
        new myPlugin( dtSettings );
    });
    
    $.fn.dataTable.Api.register( 'myPlugin.attach()', function (  ) {
        return this.iterator( 'table', function ( dtSettings ) {
            return dtSettings.oMyPlugin.attach();
        } );
    } );
    
    $.fn.dataTable.Api.register( 'myPlugin.detach()', function (  ) {
        return this.iterator( 'table', function ( dtSettings ) {
            return dtSettings.oMyPlugin.detach();
        } );
    } );
})(window, document, jQuery);

JSFiddle

Any help would be appreciated

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 61,627Questions: 1Answers: 10,091 Site admin

    The issue is that myPlugin.event.bind is returning a new function each time you call it. So what in effect is happening is:

    this._dtApi.on( 'draw.dt', eventParams, function1 );
    this._dtApi.off( 'draw.dt', function2 );
    

    Since the off line doesn't match anything, nothing is removed (events are a bu**er this way - that's way just about every events library there is wraps the calling function up into its own closure that it can keep track off).

    There are two options:

    1. Use namespaces - you've already got the .dt namespace - you can add another custom one as you can then just remove all events by passing in that event.namespace without a function.
    2. Wrap the initial function up into a closure.

    There might be some other magic ES6 way of doing it (using a fat arrow for example), but I'm not sure.

    Allan

  • jLinuxjLinux Posts: 981Questions: 73Answers: 75
    edited November 2015

    I think option #2 would be my preferred route...

    How would using a closure help though? Heres what ive tried this far: http://jsfiddle.net/2e09qzjo/2/

  • allanallan Posts: 61,627Questions: 1Answers: 10,091 Site admin
    Answer ✓

    Sorry I didn't explain that very well! Your closure would be assigned to a variable somewhere so it can be accessed again when required. It doesn't need to be an anonymous function like that - the result from bind could be used, but the key is that if you want to pass the function to off you need to pass the original function that was added - not a duplicate of it.

    Allan

  • jLinuxjLinux Posts: 981Questions: 73Answers: 75

    Hm.. this might be a little tricky. I guess i could store the function in a closure in dtSettings. Ill try!

  • allanallan Posts: 61,627Questions: 1Answers: 10,091 Site admin
    Answer ✓

    Namespaces are the way forward :-)

  • jLinuxjLinux Posts: 981Questions: 73Answers: 75
    edited November 2015

    You mean attaching them to draw.dt.something? Hm.. I suppose I could do something like draw.dt.${table_id} or something..

    This needs to work with multiple tables, meaning I cant just detach everything from draw, or else it will do it for all tables.

  • jLinuxjLinux Posts: 981Questions: 73Answers: 75
    edited November 2015

    Well actually, since the events are via the API, which is specific to the table, I suppose I dont have to use the table ID...

    This seems to work :)

    attachEvents () {
        var eventParams = {
                dtSettings: this._dtSettings
            },
            enabledConditions = this.getEnabledConditions( );
    
        if ( enabledConditions === false )
            throw new Error('No enabled conditions to attach to events');
    
        var conditions = this.conditions( enabledConditions );
    
        // Loop through all available conditions
        $.each( conditions, ( sCondition, oCondition ) =>  {
            // Attach the method that updates the hash, to the event associated with this condition
            this._dtApi.on( `${oCondition.event}.keepConditions`, eventParams, KeepConditions.structureHash.bind( KeepConditions ) );
        });
    }
    
    
    detachEvents () {
        var eventParams = {
                dtSettings: this._dtSettings
            },
            enabledConditions = this.getEnabledConditions( );
    
        if ( enabledConditions === false )
            throw new Error('No enabled conditions to attach to events');
    
        var conditions = this.conditions( enabledConditions );
    
        // Loop through all available conditions
        $.each( conditions, ( sCondition, oCondition ) =>  {
            // Check if condition is enabled in plugin settings, and if the table was initialized
            // with said setting/extension/plugin (The latter is unique per condition, so each
            // condition object has it's own method to check if table was init with condition)
            if ( ! this._isEnabled( sCondition ) || ! oCondition.isInit( ) )
            return;
    
            // Attach the method that updates the hash, to the event associated with this condition
            //this._dtApi.off( oCondition.event, KeepConditions.structureHash.bind( KeepConditions )  );
            this._dtApi.off(`${oCondition.event}.keepConditions`);
        });
    }
    

    Lines #15 and #41

This discussion has been closed.