Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active December 15, 2020 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sukima/5dd49c598fd687b0f45bcf282bdfe348 to your computer and use it in GitHub Desktop.
Save sukima/5dd49c598fd687b0f45bcf282bdfe348 to your computer and use it in GitHub Desktop.
Change tracking
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
class Thing {
@tracked foobar = 'Foobar';
constructor(label) {
this.label = label;
}
toJSON() {
return {
label: this.label,
value: this.foobar,
changes: changeSummary(this)
};
}
}
export default class ApplicationController extends Controller {
example = new Thing('example');
exampleChangable = trackChanges(this.example);
@tracked output = '[]';
@action
updateValue(thing, { target: { value } }) {
thing.foobar = value;
console.log(this.example.foobar, this.exampleChangable.foobar);
this.output = JSON.stringify(changeSummary(this.exampleChangable), null, 2);
}
}
/* ===== */
const wrappers = new WeakMap();
const trackedChanges = new WeakMap();
function trackChanges(obj) {
let wrapper = new Proxy(obj, {
// Why is this required for tracked properties?
// (see console with and without this commented)
get(target, prop) { return Reflect.get(target, prop); },
set(target, prop, value) {
let originalValues = trackedChanges.get(target) ?? new Map();
if (originalValues.get(prop) === value) {
originalValues.delete(prop);
} else if (!originalValues.has(prop)) {
originalValues.set(prop, Reflect.get(target, prop));
}
trackedChanges.set(target, originalValues);
return Reflect.set(target, prop, value);
}
});
wrappers.set(wrapper, obj);
return wrapper;
}
function changeSummary(obj) {
let reference = wrappers.get(obj) ?? obj;
let changes = trackedChanges.get(reference) ?? new Map();
return [...changes].map(([prop, from]) => {
return { prop, from, to: reference[prop] };
});
}
<fieldset>
<legend>Example</legend>
<label for="example">Foobar</label>
<input id="example" value={{this.exampleChangable.foobar}} {{on "input" (fn this.updateValue this.exampleChangable)}}>
<pre>{{this.output}}</pre>
</fieldset>
{
"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"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment