EmberObject fundamentals

I’m having a hard time understanding how the base EmberObject class is supposed to be used and inherited… I could not find the relevant info in the doc…

I want to define a class C deriving from EmberObject, and I would like it to have this kind of behavior:

// create new instance and initialize it with data, which is not directly an instance property
let instance = C.create(data);

or maybe let instance = C.createFromData(data) if the create method is reserved.

Looking at how this should be done, I looked at the code in ember-file-upload here and saw something like:

export default EmberObject.extend({
  init() {
    this._super();
    // instance initialization code goes here
  },
  prop1: null,
  prop2: computed(
    // some code
  ),
  // etc.
  // other instance methods and properties
}).reopenClass({
  // that is a static method
  createInstance(data) {
    let instance = this.create();
    Object.defineProperty(instance, 'someMagicProp', {
      writeable: false,
      enumerable: false,
      value: // something which uses 'data'
    });
    return instance;
  }
});

My reaction to this code was “:scream::scream::scream: Why did I go away from Python!!!”

For instance, someMagicProp is a very important property and is not even in the main class definition block…

So the questions are:

  1. how can we modify the create and init methods?
  2. can they have arguments (at least one) which are not directly class properties?
  3. do we need to do things like Object.defineProperty like in the example above, or is it just a way to make this special property protected (writeable: false) for that particular use case?
  4. bonus question: based on this example, I tried to define a creator function either in the main class definition block, or in the reopen block, and the behavior seems to be different… Is this supposed to be so?

I just… RTFM here and it is actually documented: I completely overlooked the difference between reopen which is meant to add methods, and reopenClass which is the (only?) way to define static methods.

1 Like

What ember version are you using? If it’s new enough, you may be able to use octane / native js for this

I feel you on that, my friend!

1 Like

If you’re on Ember 3.4 or later I’d recommend using native JS classes with decorators for new code.

If you’re on Ember 3.4 or 3.5 you’ll need to ember install ember-native-class-polyfill, this is built in from Ember 3.6 on.

Then to get decorator support do ember install ember-decorators-polyfill. This will be built in from Ember 3.10 on.

You can then use native classes with decorators for things like computed properties and actions. Avoid subclassing from EmberObject unless you’re really sure that’s what you want to do, it’s not a futureproof approach, though it may mean you have to use global set(). If you do need to extend from EmberObject then you can’t use a native JS constructor() and should use init() instead (remembering to call super(...arguments) in it).

Ensure you’re on ember-cli-babel version 7.7.3 or later as well.

A native class approach would look something like this:

export default class MyClass {
  constructor() {
    // instance initialization code goes here
  }

  prop1 = null;

  @computed(/* dependency list goes here */)
  get prop2() {
    // some code
  }

  static createInstance(data) {
    let instance = new MyClass();
    Object.defineProperty(instance, 'someMagicProp', {
      writeable: false,
      enumerable: false,
      value: // something which uses 'data'
    });
    return instance;
  }
};
2 Likes

We are still on 2.18 LTS for the moment.