Search code examples
javascriptinheritanceember.jsecmascript-6prototypal-inheritance

How can I provide dynamic values for an 'extend'? (or 'how does extend work in this case' ?)


I'm looking at a class (this one). The doco for it suggests you can extend the class to change a property of the class. That is true, it works for me.

However what I want to do is dynamically provide the values for the setting.

I've got two questions.

First

How can I adapt the demo so that I can dynamically supply the values used.

Second

I realise when I look at the class, this seems to be the significant part of the code ...

  ajax (url, data = {}, method = this.method) {
    const ajaxSettings = assign(
      {},
      {
        contentType: false,
        processData: false,
        xhr: () => {
          const xhr = $.ajaxSettings.xhr();
          xhr.upload.onprogress = (event) => {
            this.didProgress(event);
          };
          this.one('isAborting', () => xhr.abort());
          return xhr;
        },
        url,
        data,
        method
      },
      get(this, 'ajaxSettings')
    );

    return this.ajaxPromise(ajaxSettings);
  },

... I'm not confident that I understand how the 'extend' is doing what it's doing . ajaxSettings is used within the ajax function so how does the extend reach within the function and merge the value provided in the extend into the hardcoded values in the function ?

Hope the question makes sense ... I'll happily settle for an answer to 'First' if you can't manage 'Second' without writing a book ;-)


In response to comments


Solution

    1. It looks like you can simply make ajaxSettings a computed property if you want to set it dynamically:
    import Uploader from 'ember-uploader/uploaders/uploader';
    import { computed } from '@ember/object';
    
    export default Uploader.extend({
      ajaxSettings: computed('someProperty', function() {
        // do your logic to set the options dynamically in here
        return {
          headers: {
            'X-Application-Name': 'Uploader Test'
          }
        };
      })
    });
    
    1. That indeed is where the ajaxSettings property is being used and the key line of code is this one:
    get(this, 'ajaxSettings')
    

    Which retrieves the ajaxSettings property from the class (thus, if ajaxSettings was set on the base class your subclass will override the base class settings -- see here for more info on extends).

    That line is used in conjunction with Object.assign() (technically Ember's polyfill) to build the options. This part provides some defaults:

    {
      contentType: false,
      processData: false,
      xhr: () => {
        const xhr = $.ajaxSettings.xhr();
        xhr.upload.onprogress = (event) => {
          this.didProgress(event);
        };
        this.one('isAborting', () => xhr.abort());
          return xhr;
        },
        url,
        data,
        method
      }
    }
    

    but since the retrieval of ajaxSettings occurs after the defaults, any properties specified in your class' ajaxSettings object which overlap with the above defaults will take precedence and override them.

    So if you defined contentType in your class' ajaxSettings like so:

    ajaxSettings: computed('someProperty', function() {
      return {
        contentType: true, // (or some other value besides false)
        ...
      };
    })
    

    That would overlap with the contentType: false specified in the default values above and since it would get merged through assign() after the defaults, it would take precedence.