Skip to content

Instantly share code, notes, and snippets.

@bgentry
Created April 12, 2018 18:16
Show Gist options
  • Save bgentry/25630f9549231656a3a90853945e9e3d to your computer and use it in GitHub Desktop.
Save bgentry/25630f9549231656a3a90853945e9e3d to your computer and use it in GitHub Desktop.
Ember identity map for ember-apollo-client models
// this is an example model
import ObjectProxy from "@ember/object/proxy";
function objectEqual(a, b) {
if (typeOf(a) === "instance" || typeOf(b) === "instance") {
return isEqual(a, b);
} else if (typeOf(a) === "object" && typeOf(b) === "object") {
return Object.entries(a).every(([propname, value]) => {
return b[propname] === value;
});
}
return a === b;
}
export default ObjectProxy.extend({
isEqual(other) {
if (other instanceof ObjectProxy) {
let otherContent = get(other, "content");
return objectEqual(get(this, "content"), otherContent);
}
return false;
},
// go nuts with computed properties, service injections, etc
});
// A route where we immediately wrap the EmberObject from ember-apollo-client in our model type:
import Route from "@ember/route";
import query from "myapp/gql/queries/batch-show";
import { Promise as RSVPPromise } from "rsvp";
import { inject as service } from "@ember/service";
export default ProtectedRoute.extend({
store: service(),
model({ id }) {
return this.get("apollo")
.watchQuery(
{
query,
variables: { id },
fetchPolicy: "cache-and-network",
},
"batch"
)
.then(result => {
return RSVPPromise.resolve(
result
? this.get("store").createRecord("batch", { content: result })
: result
);
});
},
});
// An identity map service that performs the functions often handled by the "store" service of other Ember data-related addons.
import Service from "@ember/service";
import { A } from "@ember/array";
import EmberObject from "@ember/object";
import { assert } from "@ember/debug";
import { getOwner } from "@ember/application";
function buildRecord(type, data, store) {
return createRecord(type, data, store);
}
function createRecord(type, data, store) {
let factory = factoryForType(type, store);
let primaryKey = primaryKeyForType(type, store);
assert(`No model was found for type: ${type}`, factory);
let record = factory.create(data);
let id = data[primaryKey];
identityMapForType(type, store)[id] = record;
return record;
}
function factoryForType(type, store) {
return getOwner(store).factoryFor(`model:${type}`);
}
function primaryKeyForType(type, store) {
const factory = factoryForType(type, store) || {};
// http://emberjs.com/deprecations/v2.x/#toc_migrating-from-_lookupfactory-to-factoryfor
return factory.class && (factory.class.primaryKey || "id");
}
function identityMapForType(type, store) {
let typeIdentityMap = store.get("identityMap");
let idIdentityMap = typeIdentityMap[type] || {};
typeIdentityMap[type] = idIdentityMap;
return idIdentityMap;
}
const ServiceType = Service || EmberObject;
let Store = ServiceType.extend({
init() {
this._super(...arguments);
this.reset();
},
reset() {
this.set("recompute", A());
this.set("filtersMap", {});
this.set("identityMap", {});
},
willDestroy() {
this.setProperties({
identityMap: null,
filtersMap: null,
recompute: null,
});
},
clear(type) {
if (type === undefined) {
this.reset();
}
delete this.get("identityMap")[type];
this.scheduleUpdate(type);
},
createRecord(type, data) {
return buildRecord(type, data, this);
},
});
export default Store;
// A component where we take the plain EmberObject as input, and use a computed to wrap it in our model object:
import Component from "@ember/component";
import { computed } from "@ember/object";
import { inject as service } from "@ember/service";
export default Component.extend({
batch: null, // the raw EmberObject returned by ember-apollo-client
store: service(),
// the model-ized version:
batch: computed("rawBatch", function() {
return this.get("store").createRecord("batch", {
content: this.get("rawBatch") || {},
});
}),
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment