Search code examples
javascriptjqueryperformancebackbone.jsdry

Trying to understand DRY principles in Javascript


I am currently trying to sharpen my refactoring skills, I have a block of code that I have written that has two methods that are very similar, and I am trying to wrap my head around simplifying my bloated code, any suggestions would be welcome.

As you can see the two methods are very similar, and the only real difference is the URL the POST to.

authenticateA : function( e ) {
  var $this = $( e.target ).closest( '[data-fn]' )
  ,   text = $this.text()
  ,   that = this;

  $this.text( 'Authenticating...' ).addClass("auth-button-disable")

  $.ajax({
    type : 'POST',
    url : '/A_authentications/update/',
    data : { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() },
    complete: function( xhr ) {

      if ( xhr.status === 200 )
        that.relocate();
      else {
        $this.text( text ).removeClass("auth-button-disable");
        that.handleError( xhr.status );
      }
    },
    dataType : 'json'
  });
},

authenticateB : function( e ) {
  var $this = $( e.target ).closest( '[data-fn]' )
  ,   text = $this.text()
  ,   that = this;

  $this.text( 'Authenticating...' ).addClass("auth-button-disable")

  $.ajax({
    type : 'POST',
    url : '/B_authentications/',
    data : { otp : this.$el.find( '#B-input' ).val() },
    complete: function( xhr ) {
      if ( xhr.status === 200 )
        that.relocate();
      else {
        $this.text( text ).removeClass("auth-button-disable");
        that.handleError( xhr.status )
      }
    },
    dataType : 'json'
  });
}

I call the methods as click functions in an events block:

'click [data-fn="authenticate-A"]' : 'authenticateA',
'click [data-fn="authenticate-B"]' : 'authenticateB'

I think these could be refactored into one method or two slimmer methods, I'm just not sure where to start, thanks again in advance.


Solution

  • You can have a function that generates those functions:

    generateAuthFunction : function( authDetails) {
      return function (e) {
        var $this = $( e.target ).closest( '[data-fn]' )
        ,   text = $this.text()
        ,   that = this;
    
        $this.text( 'Authenticating...' ).addClass("auth-button-disable")
    
        $.ajax({
          type : 'POST',
          url : authDetails.url,
          data : authDetails.dataFunc(this),
          complete: function( xhr ) {
    
            if ( xhr.status === 200 )
              that.relocate();
            else {
              $this.text( text ).removeClass("auth-button-disable");
              that.handleError( xhr.status );
            }
          },
          dataType : 'json'
        });
      };
    }
    

    Then you generate it with:

    var authDetailsA = {
      url :  '/A_authentications/update/',
      dataFunc : function (this) {
        return { _method : 'PUT', sms_token : this.$el.find( '#sms-input' ).val() };
      }
    };
    var authDetailsB = {
      url :  '/B_authentications/',
      dataFunc : function (this) {
        return { otp : this.$el.find( '#B-input' ).val() };
    };
    authenticateA : generateAuthFunction(authDetailsA);
    authenticateB : generateAuthFunction(authDetailsB);
    

    You could call it like before:

    'click [data-fn="authenticate-A"]' : 'authenticateA',
    'click [data-fn="authenticate-B"]' : 'authenticateB'
    

    I think this might even introduce unnecessary complexity, but it is more DRY.