Problem
there is a lot of boilerplate for creating vanilla classes that are properly wired up to the container.
For example:
import{cached}from'@glimmer/tracking';import{getOwner,setOwner}from'@ember/owner';import{associateDestroyableChild}from'@ember/destroyable';classMyClass{/* ... */}exportdefaultclassDemoextendsComponent{
@cachedgetmyInstance(){letinstance=newMyClass();associateDestroyableChild(this,instance);letowner=getOwner(this);if(owner){setOwner(instance,owner);}returninstance;}}Solution
this would be a new feature to ember in my ideal world, and not in a user-land package (even though it could live there)
Usage:
import{link}from'@ember/owner';// proposed;classMyClass{/* ... */}exportdefaultclassDemoextendsComponent{
@linkmyInstance=newMyClass();// alternatively, suggestion by Runspired
@link(MyClass)declaremyInstance: MyClass;}- One import instead of 3
- Two lines instead of 13
- This is a common task -- and something that's easy to mess up as the order of params swaps between
associateDestroyableChild and setOwner
Implementation:
functionlink(_prototype: object,key: string,descriptor?: Descriptor): void{if(!descriptor)return;assert(`@link can only be used with string-keys`,typeofkey==='string');let{ initializer }=descriptor;assert(`@link may only be used on initialized properties. For example, `+`\`@link foo = new MyClass();\``,initializer);letcaches=newWeakMap<object,any>();// https://github.com/pzuraq/ember-could-get-used-to-this/blob/master/addon/index.jsreturn{get(this: object){letchild=caches.get(this);if(!child){child=initializer.call(this);associateDestroyableChild(this,child);letowner=getOwner(this);if(owner){setOwner(child,owner);}caches.set(this,child);assert(`Failed to create cache for internal resource configuration object`,child);}returnchild;},}asunknownasvoid/* Thanks TS. */;}Disclaimer: I've given up with "properly typing" Stage 1 decorators, and instead assert my way in to the shapes I need.
Thoughts?
Some may wonder how you'd reactively manage args passed to MyClass, and the answer there is a Resource.
Reason being is that you can't use this pattern:
@cachedgetmyInstance(){letinstance=newMyClass(this.args.foo);link(this,instance);// overloadedreturninstance;}because you only get constructions of MyClass, and no destructions or updates when the args change.
This may be perfectly fine for some cases, but Resources are specifically for managing this destruction-or-update scenario where as the @cached getter is constructions only, until the parent class is destroyed (then all relevant destructors still remaining in memory would be called)
Sure, you could manage an "update" pattern via a local un-tracked variable such as:
_myInstance;
@cachedgetmyInstance(){if(this._myInstance){this._myInstance.update(this.args.foo);returnthis._myInstance;}letinstance=newMyClass(this.args.foo);link(this,instance);// overloadedthis._myInstance=instance;returninstance;}But wouldn't it feel much better to do this instead:
@usemyInstance=MyClass.from(()=>[this.args.foo]);// ormyInstance=MyClass.from(this,()=>[this.args.foo]);
ember-resources supports both of these APIs (with and without the decorator), and both are 100% TS safe / make sense to consumers. I need more feedback before one could be decided on -- and no matter what happens, there will be codemods <3
Problem
there is a lot of boilerplate for creating vanilla classes that are properly wired up to the container.
For example:
Solution
this would be a new feature to ember in my ideal world, and not in a user-land package (even though it could live there)
Usage:
associateDestroyableChildandsetOwnerImplementation:
Disclaimer: I've given up with "properly typing" Stage 1 decorators, and instead
assertmy way in to the shapes I need.Thoughts?
Some may wonder how you'd reactively manage args passed to
MyClass, and the answer there is a Resource.Reason being is that you can't use this pattern:
because you only get constructions of
MyClass, and no destructions or updates when the args change.This may be perfectly fine for some cases, but Resources are specifically for managing this destruction-or-update scenario where as the
@cachedgetter is constructions only, until the parent class is destroyed (then all relevant destructors still remaining in memory would be called)Sure, you could manage an "update" pattern via a local un-tracked variable such as:
But wouldn't it feel much better to do this instead:
ember-resources supports both of these APIs (with and without the decorator), and both are 100% TS safe / make sense to consumers. I need more feedback before one could be decided on -- and no matter what happens, there will be codemods <3