Skip to content

Instantly share code, notes, and snippets.

@runspired
Last active March 25, 2024 22:23
Show Gist options
  • Save runspired/ed0352e1a6123d735fc4a93c917f1dce to your computer and use it in GitHub Desktop.
Save runspired/ed0352e1a6123d735fc4a93c917f1dce to your computer and use it in GitHub Desktop.
Dynamic Model
import Controller from '@ember/controller';
import { getOwner } from '@ember/application';
import { inject as service } from '@ember/service';
import attr from 'ember-data/attr';
const NEW_ATTRS = {
age: { kind: 'attribute', name: 'age', type: 'number', options: {} }
};
function upgradeModelSchema(owner, type, newAttrs) {
const store = owner.lookup('service:store');
const Klass = store.modelFor(type);
// extending the class will ensure that static schema lookups
// regenerate
const NewKlass = class extends Klass {}
for (const [key, schema] of Object.entries(newAttrs)) {
const decorator = attr(schema.type, schema.options);
const descriptor = decorator(NewKlass.prototype, key, {});
Object.defineProperty(NewKlass.prototype, key, descriptor);
}
// bust cache in ember's registry
owner.unregister('model:' + type);
owner.register('model:' + type, NewKlass);
// bust cache in EmberData's model lookup
delete store._modelFactoryCache[type];
// bust cache in schema service
// const schemas = store.getSchemaDefinitionService?.();
// if (schemas) {
// delete schemas._relationshipsDefCache[type];
// delete schemas._attributesDefCache[type];
// }
}
export default class ApplicationController extends Controller {
appName = 'Ember Twiddle';
data = null;
@service store;
get modelName() {
return this.data?.constructor.modelName ?? '<empty>';
}
get attributeNames() {
const type = this.data?.constructor.modelName ?? null;
if (!type) return '';
const schema = this.store.modelFor(type);
return Array.from(schema.attributes.keys()).join(', ');
}
constructor(...args) {
super(...args);
const owner = getOwner(this);
// early access model attributes to ensure our approach is properly busting the cache
this.store.modelFor('dynamic').attributes;
upgradeModelSchema(owner, 'dynamic', NEW_ATTRS);
this.data = this.store.push({
data: {
type: 'dynamic',
id: '1',
attributes: {
name: 'Chris',
age: 42
}
}
});
}
}
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
name: attr(),
});
<h1>Welcome to {{this.appName}}</h1>
<h2>Greetings {{this.data.name}}, {{this.data.age}}</h2>
<h3>{{this.modelName}} has attributes: {{this.attributeNames}}</h3>
<br>
<br>
{{outlet}}
<br>
<br>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0",
"ember-data": "3.18.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment