Git Product home page Git Product logo

ember-data-model-fragments's Introduction

Ember Data Model Fragments

CI NPM Version Ember Observer Score

This package provides support for sub-models that can be treated much like belongsTo and hasMany relationships are, but whose persistence is managed completely through the parent object.

โš ๏ธ Deprecated APIs have been removed. See the changelog for more information on breaking changes.

Compatibility

This project makes extensive use of private Ember Data APIs and is therefore sensitive to minor changes in new Ember Data releases, regardless of semver guarantees. Every effort is made to maintain compatibility with the latest version, but updates always take time. See the contributing section if you'd like to help out :shipit:

Use the following table to decide which version of this project to use with your app:

Ember Data Model Fragments Node.JS
>= v3.5.x < v3.12.x v4.x 10+
>= v3.13.x < v3.27.x v5.x 12+
>= v3.28.x < v4.6.x v6.x 14+
>= v4.7.x Not Compatible

Installation

To install as an Ember CLI addon:

$ ember install ember-data-model-fragments

You may then start creating fragments with:

$ ember generate fragment foo someAttr:string anotherAttr:boolean

Which will create the module app/models/foo.js which exports a Fragment class with the given attributes.

Example

// app/models/person.js

import Model from "@ember-data/model";
import {
  fragment,
  fragmentArray,
  array,
} from "ember-data-model-fragments/attributes";

export default class PersonModel extends Model {
  @fragment("name") name;
  @fragmentArray("address") addresses;
  @array() titles;
}
// app/models/name.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class NameFragment extends Fragment {
  @attr("string") first;
  @attr("string") last;
}
// app/models/address.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class AddressFragment extends Fragment {
  @attr("string") street;
  @attr("string") city;
  @attr("string") region;
  @attr("string") country;
}

With a JSON payload of:

{
  "person": {
    "id": "1",
    "name": {
      "first": "Tyrion",
      "last": "Lannister"
    },
    "addresses": [
      {
        "street": "1 Sky Cell",
        "city": "Eyre",
        "region": "Vale of Arryn",
        "country": "Westeros"
      },
      {
        "street": "1 Tower of the Hand",
        "city": "King's Landing",
        "region": "Crownlands",
        "country": "Westeros"
      }
    ],
    "titles": ["Imp", "Hand of the King"]
  }
}

The name attribute can be treated similar to a belongsTo relationship:

const person = store.peekRecord("person", "1");
const name = person.get("name");

person.get("hasDirtyAttributes"); // false
name.get("first"); // 'Tyrion'

name.set("first", "Jamie");
person.get("hasDirtyAttributes"); // true

person.rollbackAttributes();
name.get("first"); // 'Tyrion'

// New fragments are created through the store and assigned directly
person.set(
  "name",
  store.createFragment("name", {
    first: "Hugor",
    last: "Hill",
  })
);
person.get("hasDirtyAttributes"); // true

// Fragments can also be set with hashes
person.set("name", {
  first: "Tyrion",
  last: "Lannister",
});
person.get("hasDirtyAttributes"); // false

The addresses attribute can be treated similar to a hasMany relationship:

const person = store.peekRecord("person", "1");
const addresses = person.get("addresses");
const address = addresses.get("lastObject");

person.get("hasDirtyAttributes"); // false
address.get("country"); // 'Westeros'

address.set("country", "Essos");
person.get("hasDirtyAttributes"); // true

person.rollbackAttributes();
address.get("country"); // 'Westeros'

// Fragments can be created and added directly through the fragment array
addresses.get("length"); // 2
addresses.createFragment({
  street: "1 Shy Maid",
  city: "Rhoyne River",
  region: "Free Cities",
  country: "Essos",
});
addresses.get("length"); // 3
person.get("hasDirtyAttributes"); // true

// Or with arrays of objects
person.set("addresses", [
  {
    street: "1 Great Pyramid",
    city: "Meereen",
    region: "Slaver's Bay",
    country: "Essos",
  },
]);

The titles attribute can be treated as an Ember.Array:

const person = store.peekRecord("person", "1");
const titles = person.get("titles");

person.get("hasDirtyAttributes"); // false
titles.get("length"); // 2

titles.pushObject("Halfman");
titles.get("length"); // 3
person.get("hasDirtyAttributes"); // true

person.rollbackAttributes();
titles.get("length"); // 2

Default Values

Ember Data attributes support a defaultValue config option that provides a default value when a model is created through store#createRecord(). Similarly, fragment and fragmentArray properties support a defaultValue option:

// app/models/person.js

import Model from "@ember-data/model";
import {
  fragment,
  fragmentArray,
  array,
} from "ember-data-model-fragments/attributes";

export default class PersonModel extends Model {
  @fragment("name", { defaultValue: { first: "Faceless", last: "Man" } }) name;
  @fragmentArray("address") addresses;
  @array("string") titles;
}

Since JavaScript objects and arrays are passed by reference, the value of defaultValue is copied using Ember.copy in order to prevent all instances sharing the same value. If a defaultValue option is not specified, fragment properties default to null and fragmentArray properties default to an empty array. Note that this may cause confusion when creating a record with a fragmentArray property:

const person = store.createRecord('person');
const addresses = person.get('addresses'); // null

// Fails with "Cannot read property 'createFragment' of null"
addresses.createFragment({
  ...
});

Like attr, the defaultValue option can be a function that is invoked to generate the default value:

// app/models/person.js

import Model from "@ember-data/model";
import { fragment } from "ember-data-model-fragments/attributes";

export default class PersonModel extends Model {
  @fragment("name", {
    defaultValue() {
      return {
        first: "Unsullied",
        last: new Date().toString(),
      };
    },
  })
  name;
}

Serializing

Serializing records with fragment attributes works using a special Transform that serializes each fragment or fragment array. This results in fragments being nested in JSON as expected, and avoids the need for any custom serialization logic for most cases. This also means that model fragments can have their own custom serializers, just as normal models can:

// app/models/name.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class NameFragment extends Fragment {
  @attr("string") given;
  @attr("string") family;
}
// apps/serializers/name.js
// Serializers for fragments work just as with models

import JSONSerializer from "@ember-data/serializer/json";

export default class NameSerializer extends JSONSerializer {
  attrs = {
    given: "first",
    family: "last",
  };
}

Since fragment deserialization uses the value of a single attribute in the parent model, the normalizeResponse method of the serializer is never used. And since the attribute value is not a full-fledged JSON API response, JSONAPISerializer cannot be used with fragments. Because of this, auto-generated fragment serializers do not use the application serializer and instead use JSONSerializer.

If common logic must be added to auto-generated fragment serializers, apps can register a custom serializer:-fragment with the application in an initializer.

// app/serializers/fragment.js

import JSONSerializer from "@ember-data/serializer/json";

export default class FragmentSerializer extends JSONSerializer {}
// app/initializers/fragment-serializer.js

import FragmentSerializer from "../serializers/fragment";

export function initialize(application) {
  application.register("serializer:-fragment", FragmentSerializer);
}

export default {
  name: "fragment-serializer",
  initialize: initialize,
};

If custom serialization of the owner record is needed, fragment snapshots can be accessed using the Snapshot#attr method. Note that this differs from how relationships are accessed on snapshots (using belongsTo/hasMany methods):

// apps/serializers/person.js
// Fragment snapshots are accessed using `snapshot.attr()`

import JSONSerializer from "@ember-data/serializer/json";

export default JSONSerializer.extend({
  serialize(snapshot, options) {
    const json = super.serialize(...arguments);

    // Returns a `Snapshot` instance of the fragment
    const nameSnapshot = snapshot.attr("name");

    json.full_name =
      nameSnapshot.attr("given") + " " + nameSnapshot.attr("family");

    // Returns a plain array of `Snapshot` instances
    const addressSnapshots = snapshot.attr("addresses");

    json.countries = addressSnapshots.map(function (addressSnapshot) {
      return addressSnapshot.attr("country");
    });

    // Returns a plain array of primitives
    const titlesSnapshot = snapshot.attr("titles");

    json.title_count = titlesSnapshot.length;

    return json;
  },
});

Nesting

Nesting of fragments is fully supported:

// app/models/user.js

import Model, { attr } from "@ember-data/model";
import { fragmentArray } from "ember-data-model-fragments/attributes";

export default class UserModel extends Model {
  @attr("string") name;
  @fragmentArray("order") orders;
}
// app/models/order.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";
import { fragmentArray } from "ember-data-model-fragments/attributes";

export default class OrderFragment extends Fragment {
  @attr("string") amount;
  @fragmentArray("product") products;
}
// app/models/product.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class ProductFragment extends Fragment {
  @attr("string") name;
  @attr("string") sku;
  @attr("string") price;
}

With a JSON payload of:

{
  "id": "1",
  "name": "Tyrion Lannister",
  "orders": [
    {
      "amount": "799.98",
      "products": [
        {
          "name": "Tears of Lys",
          "sku": "poison-bd-32",
          "price": "499.99"
        },
        {
          "name": "The Strangler",
          "sku": "poison-md-24",
          "price": "299.99"
        }
      ]
    },
    {
      "amount": "10999.99",
      "products": [
        {
          "name": "Lives of Four Kings",
          "sku": "old-book-32",
          "price": "10999.99"
        }
      ]
    }
  ]
}

Dirty state propagates up to the parent record, rollback cascades down:

const user = store.peekRecord("user", "1");
const product = user.get("orders.firstObject.products.lastObject");

user.get("hasDirtyAttributes"); // false
product.get("price"); // '299.99'

product.set("price", "1.99");
user.get("hasDirtyAttributes"); // true

user.rollbackAttributes();
user.get("hasDirtyAttributes"); // false
product.get("price"); // '299.99'

However, note that fragments do not currently support belongsTo or hasMany properties. See the Limitations section below.

Polymorphism

Ember Data: Model Fragments has support for reading polymorphic fragments. To use this feature, pass an options object to fragment or fragmentArray with polymorphic set to true. In addition the typeKey can be set, which defaults to 'type'.

The typeKey option might be a String or a Function returning a String. If you use a function, the data and the owner will be passed as parameter.

The typeKey's value must be the lowercase name of a class that is assignment-compatible to the declared type of the fragment attribute. That is, it must be the declared type itself or a subclass. Additionally, the typeKey's value must be a field on the parent class.

In the following example the declared type of animals is animal, which corresponds to the class Animal. Animal has two subclasses: Elephant and Lion, so to typeKey's value can be 'animal', 'elephant' or 'lion'.

// app/models/zoo.js

import Model, { attr } from "@ember-data/model";
import { fragment, fragmentArray } from "ember-data-model-fragments/attributes";

export default class ZooModel extends Model {
  @attr("string") name;
  @attr("string") city;
  @fragmentArray("animal", { polymorphic: true, typeKey: "$type" }) animals;
  @fragment("animal", {
    polymorphic: true,
    typeKey: (data) => `my-model-prefix-${data.name}`,
  })
  bestAnimal;
}
// app/models/animal.js

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class AnimalFragment extends Fragment {
  @attr("string") $type;
  @attr("string") name;
}
// app/models/elephant.js

import AnimalFragment from "./animal";
import { attr } from "@ember-data/model";

export default class ElephantFragment extends AnimalFragment {
  @attr("number") trunkLength;
}
// app/models/lion.js

import AnimalFragment from "./animal";
import { attr } from "@ember-data/model";

export default class LionFragment extends AnimalFragment {
  @attr("boolean") hasManes;
}

The expected JSON payload is as follows:

{
  "Zoo": {
    "id": "1",
    "name": "Winterfell Zoo",
    "city": "Winterfell",
    "animals": [
      {
        "$type": "lion",
        "name": "Simba",
        "hasManes": false
      },
      {
        "$type": "lion",
        "name": "Leonard",
        "hasManes": true
      },
      {
        "$type": "elephant",
        "name": "Trunky",
        "trunkLength": 10
      },
      {
        "$type": "elephant",
        "name": "Snuffles",
        "trunkLength": 9
      }
    ]
  }
}

Serializing the fragment type back to JSON is not currently supported out of the box. To serialize the polymorphic type, create a custom serializer to perform manual introspection:

// app/serializers/animal.js

import JSONSerializer from "@ember-data/serializer/json";
import ElephantFragment from "app/models/elephant";
import LionFragment from "app/models/elephant";

export default class AnimalSerializer extends JSONSerializer {
  serialize(record, options) {
    const json = super.serialize(...arguments);

    if (record instanceof ElephantFragment) {
      json.$type = "elephant";
    } else if (record instanceof LionFragment) {
      json.$type = "lion";
    } else {
      json.$type = "animal";
    }

    return json;
  }
}
// app/serializers/elephant.js

import AnimalSerializer from "./animal";

export default AnimalSerializer;
// app/serializers/lion.js

import AnimalSerializer from "./animal";

export default AnimalSerializer;

TypeScript

TypeScript declarations are included out of the box. For additional type safety for createFragment, push, etc. you can index your fragment classes in the FragmentRegistry:

// app/models/address.ts

import Fragment from "ember-data-model-fragments/fragment";
import { attr } from "@ember-data/model";

export default class AddressFragment extends Fragment {
  @attr("string")
  declare street: string;

  @attr("string")
  declare city: string;

  @attr("string")
  declare region: string;

  @attr("string")
  declare country: string;
}

declare module "ember-data-model-fragments/types/registries/fragment" {
  export default interface FragmentRegistry {
    address: AddressFragment;
  }
}

Limitations

Conflict Resolution

There is a very good reason that support for id-less embedded records has not been added to Ember Data: merging conflicts is very difficult. Imagine a scenario where your app requests a record with an array of simple embedded objects, and then a minute later makes the same request again. If the array of objects has changed โ€“ for instance an object is added to the beginning โ€“ without unique identifiers there is no reliable way to map those objects onto the array of records in memory.

This plugin handles merging fragment arrays by swapping out the data of existing fragments. For example, when a record is fetched with a fragment array property, a fragment model is created for each object in the array. Then, after the record is reloaded via reload or save, the data received is mapped directly onto those existing fragment instances, adding or removing from the end when necessary. This means that reordering the array will cause fragment objects' data to swap, rather than simply reordering the array of fragments in memory. The biggest implication of this behavior is when a fragment in a fragment array is dirty and the parent model gets reloaded. If the record is then saved, the change will likely affect the wrong object, causing data loss. Additionally, any time a reference to a model fragment is held onto, reloading can give it a completely different semantic meaning. If your app does not persist models with fragment arrays, this is of no concern (and indeed you may wish to use the EmbeddedRecordMixin instead).

Filtered Record Arrays

Another consequence of id-less records is that an ID map of all fragment instances of a given type is not possible. This means no store.all('<fragment_type>'), and no ability to display all known fragments (e.g. names or addresses) without iterating over all owner records and manually building a list.

Relationships to Models

Currently, fragments cannot have normal belongsTo or hasMany relationships. This is not a technical limitation, but rather due to the fact that relationship management in Ember Data is in a state of flux and would require accessing private (and changing) APIs.

Testing

Building requires Ember CLI and running tests requires Test 'Em, which can all be installed globally with:

$ yarn global add ember-cli

Then install NPM packages and start the development test server:

$ yarn
$ ember test --server

It is also possible to run the tests in a headless fashion. This requires PhantomJS 2 to be installed.

$ ember test

# Using `yarn test` will invoke `ember try:testall`.
# This will test each version of ember supported by this addon.
$ yarn test

Contributing

When reporting an issue, follow the Ember guidelines. When contributing features, follow Github guidelines for forking and creating a new pull request. All existing tests must pass (or be suitably modified), and all new features must be accompanied by tests to be considered.

ember-data-model-fragments's People

Contributors

basz avatar bobisjan avatar charlesfries avatar chrisvariety avatar cibernox avatar cohitre avatar deanmarano avatar dependabot[bot] avatar dwickern avatar gavinjoyce avatar igort avatar jackweinbender avatar jakesjews avatar jkusa avatar josephhalter avatar kielni avatar knownasilya avatar louy avatar markhayden avatar nire0510 avatar patocallaghan avatar pauln avatar richgt avatar rondale-sc avatar runspired avatar rwjblue avatar slindberg avatar techn1x avatar vincentmolinie avatar workmanw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-data-model-fragments's Issues

Fragments RFC and model fragments

Have you all seen the RFC for fragments? Wondering if the authors are aware of this project and the potential confusion about names? I am increasingly feeling that model fragments is an indispensable / inseparable piece of the Ember Data ecosystem, for those of us consuming complex JSON documents with ID-less subcomponents. Has there been any discussion about pulling this functionality into core Ember Data? Honestly, I feel it is unlikely we would be using Ember Data at Avalara if not for this project.

`createFragment` is undefined in service unit test

I have a simple service:

// my-service.js
import Ember from 'ember';

export default Ember.Service.extend({
  store: Ember.inject.service(),

  doSomething: function() {
    this.get('store').createFragment(...);
  }
});

All is well in my app, but when I try to unit test the service, the store is injected but createFragment is undefined. From looking at your code I think itโ€™s because the main module isnโ€™t executed in the context of a unit test. I just canโ€™t figure out a way to execute it since no module is exposed to ember-cli.

Any idea how I could fix this?
Thank you!

hasOne fragment remains dirty after parent save

Hi! Thanks for this extension, I hope it will be added to the core.

But I have an issue with saving when hasOne fragment is used (or I'm doing something wrong):

var person = store.getById('person', '1');
var name = person.get('name');

person.get('isDirty'); // false
name.get('first'); // 'Tyrion'

name.set('first', 'Jamie');
person.get('isDirty'); // true

person.save();
name.get('isDirty'); // true (BUG?)
person.get('isDirty'); // false

Shouldn't name.get('isDirty') be false?

Difficulty creating new fragments for a hasManyFragments attribute

Hi There,

I know this is not the best place for "usage" issues, but with the module still being in its infancy - I figured I would have a much better chance of a response here and i'm really stumped!!

I am trying to essentially replicate the "addresses" part of the example in the readme except using Ember CLI and names appropriate for my App. I have no trouble loading data into the fragments and later accessing it, but I am having a lot of difficulty creating new fragments into a hasManyFragments attribute.

When following the "addresses" part of the example in my App, to create a new fragment, I receive a Cannot read property 'createFragment' of null error when attempting to call createFragment on my hasManyFragments attribute. When using the Ember Chrome inspector, my hasManyFragments attribute is indeed set to null. Should this be null?

// /models/collection.js
import DS from 'ember-data';

export default DS.Model.extend({
    rev: DS.attr('string'),
    type: DS.attr('string'),
    name: DS.attr('string'),
    products: DS.hasManyFragments('collection-products'),
    meta: DS.hasOneFragment('collection-meta'),
    credentials: DS.hasManyFragments()
});
// /models/collection-products.js
import DS from 'ember-data';

export default DS.ModelFragment.extend({
  productId: DS.attr('string'),
  productTitle: DS.attr('string'),
  order: DS.attr('string')
});
// /routes/collection.js
import Ember from 'ember';

export default Ember.Route.extend({
  model: function(params) {
      var collection = this.store.createRecord('collection');
      return collection;
  }
});
// /controllers/collection.js
import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    addToCollection: function(product){
      var products = this.get('model.products');

      products.createFragment('collection-products', {productId: product.get('id'), productTitle: product.get('title')});
      console.log('Added product');
    }
  }
});

Appreciate any help ๐Ÿ˜…

Simplify fragment API naming

DS.ModelFragment, DS.hasOneFragment, and DS.hasManyFragments are all mouthfulls. The tradeoff is always clarity vs. brevity, and the current naming may be a little too heavy on the clarity side. Potential simplifications:

DS.ModelFragment โ†’ DS.Fragment
DS.hasOneFragment โ†’ DS.fragment, DS.fragmentObject
DS.hasManyFragments โ†’ DS.fragments, DS.fragmentArray

Moving away from likeness to DS.belongsTo and DS.hasMany may be desirable once all relationship properties return promises, which fragment properties will never do.

Fragment resolution in CLI unit tests

I'm hoping to write unit tests for my fragment-infused models. I am have placed the fragments related to a given model into a subfolder, so the model references them with e.g.

fragment: DS.hasOneFragment('baseModel/fragment', {defaultValue: {name: 'New fragment'}})

Attempting to unit test these definitions yields a reference error,

Setup failed on it exists: Attempting to register an unknown factory: `model:baseModel/fragment`

See https://github.com/artzte/ember-data.model-fragments-unit-testing-questions/blob/master/tests/unit/models/base-model-test.js

These fragments resolve fine in the app, but not in the tests. Am I missing something?

Handling errors

Is there any way to assign errors to a fragment? We have a api that will return errors for the nested model fragments and would like to be able to show those errors, but I haven't been able to find any good examples of this anywhere.

Allow fragment attribute setters to accept object/array literals

It may be convenient to avoid the step of calling store#createFragment before assigning a new fragment to a DS.hasOneFragment or DS.hasManyFragments property, e.g.

// 'names' is a `DS.hasManyFragments` property
record.set('names', [ { first: 'Rob', last: 'Stark' } ]);

// 'names' is a `DS.hasOneFragment` property
record.set('name', { first: 'Rob', last: 'Stark' });

vs.

// 'names' is a `DS.hasManyFragments` property
record.set('names', [
  store.createFragment('name', { first: 'Rob', last: 'Stark' })
]);

// 'names' is a `DS.hasOneFragment` property
record.set('name', store.createFragment('name', { first: 'Rob', last: 'Stark' }));

Implications TBD.

Can't remove an item that has never been added

Using store.createRecord and then modifying the defaults. Upon save of the model I get this error:

Can't remove an item that has never been added

The server response looks normal, but I get this error and the fragments disappear from my template view.

Fragment

export default DS.ModelFragment.extend({
  key: attr('string'),
  name: attr('string'),
  description: attr('string'),
  kind: attr('string'),
  abilities: hasOneFragment('ability', {
    defaultValue: {
      view: true
    }
  }),

  componentName: computed('kind', function () {
    const kind = this.get('kind');
    return `permission-${kind}`;
  })
});

It seems like it thinks that abilities is an array that was changed..

Stack Trace

Uncaught Error: Can't remove an item that has never been added.
(anonymous function) @ ember.debug.js:34134
SubArray._findOperation @ ember.debug.js:34159
SubArray.removeItem @ ember.debug.js:34122
options.removedItem @ ember.debug.js:26075
DependentArraysObserver.flushChanges @ ember.debug.js:25267
DependentArraysObserver.itemPropertyDidChange @ ember.debug.js:25251
DependentArraysObserver.createPropertyObserver.observerContext.observer @ ember.debug.js:25118
apply @ ember.debug.js:18092
sendEvent @ ember.debug.js:12432
ObserverSet.flush @ ember.debug.js:14801
endPropertyChanges @ ember.debug.js:15533
notifyProperties @ ember-data.model-fragments.js:1293
CoreModel.extend.setupData @ ember-data.model-fragments.js:1189
(anonymous function) @ ember-data.model-fragments.js:209
map @ ember.debug.js:11793
StatefulArray.extend._processData @ ember-data.model-fragments.js:194
Ember.ArrayProxy.extend.setupData @ ember-data.model-fragments.js:435
(anonymous function) @ ember-data.model-fragments.js:749
ComputedPropertyPrototype.get @ ember.debug.js:10404
get @ ember.debug.js:15664get @ ember.debug.js:24962
(anonymous function) @ ember.debug.js:25423
forEach @ ember.debug.js:11807
(anonymous function) @ ember.debug.js:25419
DependentArraysObserver.suspendArrayObservers @ ember.debug.js:25056
recompute @ ember.debug.js:25418
Queue.invoke @ ember.debug.js:869
Queue.flush @ ember.debug.js:936
DeferredActionQueues.flush @ ember.debug.js:741
Backburner.end @ ember.debug.js:166
Backburner.run @ ember.debug.js:221
run @ ember.debug.js:15972
ember$data$lib$system$adapter$$Adapter.extend.ajax.Ember.RSVP.Promise.hash.success @ rest-adapter.js:784
jQuery.Callbacks.fire @ jquery.js:3143
jQuery.Callbacks.self.fireWith @ jquery.js:3255
done @ jquery.js:9309
jQuery.ajaxTransport.send.callback @ jquery.js:9713
XMLHttpRequest.send (async)jQuery.ajaxTransport.send @ jquery.js:9659
jQuery.extend.ajax @ jquery.js:9210
ember$data$lib$system$adapter$$Adapter.extend.ajax @ rest-adapter.js:792
initializePromise @ ember.debug.js:47024
Promise @ ember.debug.js:48652
ember$data$lib$system$adapter$$Adapter.extend.ajax @ rest-adapter.js:776
ember$data$lib$system$adapter$$Adapter.extend.createRecord @ rest-adapter.js:532
ember$data$lib$system$store$$_commit @ store.js:2014
(anonymous function) @ store.js:1270
forEach @ ember.debug.js:11807
ember$data$lib$system$store$$Service.extend.flushPendingSave @ store.js:1252
Queue.invoke @ ember.debug.js:871
Queue.flush @ ember.debug.js:936
DeferredActionQueues.flush @ ember.debug.js:741
Backburner.end @ ember.debug.js:166
Backburner.run @ ember.debug.js:221
run @ ember.debug.js:15972
handleRegisteredAction @ ember.debug.js:18481
(anonymous function) @ ember.debug.js:38175
jQuery.event.dispatch @ jquery.js:4665
jQuery.event.add.elemData.handle

Introduce alternate syntax for in-line fragments

Fragments are designed to be reusable and flexible, but that comes at the cost of a verbose API. For the simplest use cases, it should not be necessary to declare a new DS.ModelFragment class. This could be possible by introducing some sugar on top of the current API, which would allow for reuse/flexibility when needed.

@silvinci's proposed a concise shorthand syntax in his comment on the epic thread that started this all:

App.Customer = DS.Model.extend({
  // Shorthand for `DS.hasOneFragment`
  name: DS.obj({
    first: DS.attr("string"),
    last: DS.attr("string")
  }),

  // Shorthand for `DS.hasManyFragments`
  invoices: DS.arr({
    sum: DS.attr("number"),
    items: DS.hasMany("items")
  }),

  // Shorthand for `DS.hasManyFragments` that accepts 'primitive' type
  loginDates: DS.arr("date")
});

It should be trivial to auto-generate anonymous DS.ModelFragment sub classes in the DS.obj and DS.arr property helpers, however this syntax raises a few issues:

  1. These fragments could not be created through DS.Store#createFragment, which means they would need to be created via literals proposed in #10 (or DS.FragmentArray#createFragment).
  2. What serializers do anonymous fragments use? I doubt it is acceptable to be locked into using DS.JSONSerializer. Perhaps a default fragment serializer can be introduced that can be overridden in the container? Naming the generated fragment using a combination of the model type and attribute name seems wrong.
  3. DS.arr and DS.hasManyFragments cannot be used interchangeably since passing a string to DS.arr would introduce ambiguity, e.g. is the string an attribute type or a fragment type? This may be a good thing.

The idea of anonymous model classes is a dubious one, however I cannot readily point to why. Another downside to this syntax is that it's not immediately obvious from the naming what the expanded form is, e.g. obj doesn't have readily apparent connection to hasOneFragment.

Warning in Ember-cli 0.2.1

When I do any action on the command line with ember, running 0.2.1 I get the following:

The package `ember-data-model-fragments` is not a properly formatted package, we have used a fallback lookup to resolve it at `/Users/username/Web/service/webui/node_modules/ember-data-model-fragments`. This is generally caused by an addon not having a `main` entry point (or `index.js`).

Default value for `hasManyFragments`

Currently when a defaultValue option isn't specified for a hasManyFragments attribute, it is set to null. While null is a valid value of the property, it is more intuitive that the default is an empty fragment array, and is more consistent with how DS.hasMany works already. In fact there currently is no way to force the creation of a fragment array once a record has been created. However this is a minor breaking change, so should probably slated for a future major version bump.

Refs #36.

EDIT: a fragment array property can be set explicitly to an array-like object, which can either be empty or contain valid fragment models.

Dependent properties in FragmentArrays aren't updated on reload

When a fragment array is reloaded, properties that depend on its content (e.g. length) aren't updated. This can lead to some fairly nasty data corruption, as seen here (add a couple children, reload the model, then add a couple more).

I believe it's this line that's the culprit, but given the comment right above it, I'd guess the solution may not be simple :(

Move 'primitive' support from `DS.hasManyFragments` into a new property type

Arrays of non-fragment types (usually primitives) are currently supported by omitting the fragment type from the DS.hasManyFragments definition. This is 1) misleading since primitives are not DS.ModelFragment instances, and 2) because a primitive type (and therefore transform) cannot be specified and enforced.

A possible solution is to introduce a DS.hasManyPrimitives property type, however this is also misleading since the types this property would support aren't limited to primitives (think date type). The distinction here is that there is no need for special state handling on the nested value. This loosely translates to when the value is serialized to a primitive for JSON transport. So then what to name it?

This also potentially impacts simplified naming in #11.

ember-cli blueprint not working correctly when --pod is set

The ember-cli generator names the file incorrectly when run with the --pod flag.

For example, when running ember generate fragment hello-world --pod creates the file in

app/pods/hello-world/fragment.js

instead of

app/pods/hello-world/model.js

Additionally the blueprint always generates .js files instead of .coffee when ember-cli-coffeescript is installed.

Weird error on save

My API returns this JSON:

{
  "models": {
    ...
    "hpmmeta" {
      "a": "foo",
      "b": "bar"
    }
  }
}

My primary model:

Model = DS.Model.extend({
  ...
  hpmmeta : DS.hasOneFragment('hpmmeta')
}

Fragment:

Hpmmeta = DS.ModelFragment.extend({
  a : DS.attr('string'),
  b : DS.attr('string')
}

When I'm trying to save Model, I get this error:

Uncaught Error: No model was found for 'hpmmetum' vendor.js:71743
Ember.Object.extend.modelFor vendor.js:71743
Ember.Object.extend.serializerFor vendor.js:72117
Ember.Object.extend.serialize vendor.js:70720
Ember.Object.extend.serialize vendor.js:67137
Transform.extend.serialize vendor.js:96762
__exports__.default.Ember.Object.extend.serializeAttribute vendor.js:64114
(anonymous function) vendor.js:64042
(anonymous function) vendor.js:66271
(anonymous function) vendor.js:26953
OrderedSet.forEach vendor.js:26795
Map.forEach vendor.js:26951
Model.reopenClass.eachAttribute vendor.js:66270
Model.reopen.eachAttribute vendor.js:66327
__exports__.default.Ember.Object.extend.serialize vendor.js:64041
apply vendor.js:30437
superFunction vendor.js:27104
__exports__.default.JSONSerializer.extend.serialize vendor.js:65301
apply vendor.js:30437
superWrapper vendor.js:30014
__exports__.default.JSONSerializer.extend.serializeIntoHash vendor.js:65327
apply vendor.js:30438
superWrapper vendor.js:30014
__exports__.default.Adapter.extend.updateRecord vendor.js:62453
_commit vendor.js:72375
(anonymous function) vendor.js:71598
forEach vendor.js:25379
Ember.Object.extend.flushPendingSave vendor.js:71583
DeferredActionQueues.invoke vendor.js:12690
DeferredActionQueues.flush vendor.js:12740
Backburner.end vendor.js:12203
Backburner.run vendor.js:12258
apply vendor.js:30436
run vendor.js:29055
handleRegisteredAction vendor.js:30898
(anonymous function) vendor.js:49197
jQuery.event.dispatch vendor.js:4527
elemData.handle vendor.js:4213

Ember-Data lines where the error is thrown:

      /**
        Returns a model class for a particular key. Used by
        methods that take a type key (like `find`, `createRecord`,
        etc.)

        @method modelFor
        @param {String or subclass of DS.Model} key
        @return {subclass of DS.Model}
      */
      modelFor: function(key) {
        var factory;

        if (typeof key === 'string') {
          factory = this.container.lookupFactory('model:' + key);
          if (!factory) {
            throw new Ember.Error("No model was found for '" + key + "'");
          }
          factory.typeKey = factory.typeKey || this._normalizeTypeKey(key);
        } else {
          // A factory already supplied. Ensure it has a normalized key.
          factory = key;
          if (factory.typeKey) {
            factory.typeKey = this._normalizeTypeKey(factory.typeKey);
          }
        }

        factory.store = this;
        return factory;
      },

Project Info:

  • Ember-CLI: 0.1.2
  • ember: 1.7.0
  • ember-data: 1.0.0-beta.10
  • ember-data.model-fragments: 0.2.1

Stop deferring fragment deserializing until first access

Due to the fact that no meta information about the particular property being deserialized is passed to DS.JSONSerializer#applyTransforms, it is not currently possible to create fragment objects upon deserializing the record, since the fragment type must be know in order to instantiate the correct serializer. This means that the record's _data attribute is initially populated with the property's raw JSON, until it is first accessed through a DS.hasOneFragment or DS.hasManyFragments computed property. This adds complexity to those attribute definitions, leading to lower maintainability/grokability and higher risk of weird edge-case bugs.

On the flip side, deferring object creation until access can have (minor) memory performance benefits. Still probably not worth the cost though.

Assign type key on createFragment of a polymorphic model

A few disclaimers:

  1. I see in the readme that:

    Model Fragments has support for reading polymorphic fragments

    I'll assume that creating a fragment doesn't fall within, 'reading'.

  2. I see the App.AnimalSerializer example in the readme, this is related.

However ๐Ÿ˜„ ...

I just used createFragment to create a polymorphic model, let's say an Elephant in the readme example, but the $type property was not set.

I depend on the type key attribute else where in my app (via a $type: DS.attr('string') in the model), and worked around this by putting in the definition of Elephant:

  init: function() {
    this._super();
    this.set('$type', 'elephant');

So it works now, but I have a few questions:

  1. Is this a bad idea?
  2. If not, is it outside the scope of the project, or just not implemented yet?

Decide when to use `Ember.assert` vs. throwing an error

Basically, what is the true intent of Ember.assert assertions? It seems that it is used to give helpful errors when developing, but cut down on the library payload size when in production.

However, not making an assertion in production means 1) app behavior is different between environments making production bugs harder to track down, and 2) the helpful error messages are gone in production, making bugs harder to track down.

Is the < 1K size savings worth that cost? Is there another reason to use Ember.assert that I am missing?

Ember-CLI addon

Would love an ember-cli addon/blueprint with ember g fragment [name].

Error: You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.

Hi guys,

First off: โค๏ธ this ember data addon, it seems to exactly solve my problem.

However, I'm running into this error:

Error: You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.
    at new Error (native)
    at Error.Ember.Error (https://localhost/vendor.js:17227:19)
    at Function.Model.reopenClass.create (https://localhost/vendor.js:65944:15)
    at Function.superWrapper (https://localhost/vendor.js:17606:16)
    at Store.reopen.buildFragment (https://localhost/vendor.js:71217:21)
    at https://localhost/vendor.js:70904:30
    at Array.map (native)
    at Ember.EnumerableUtils.map (https://localhost/vendor.js:18232:30)
    at PrimitiveArray.extend.setupData (https://localhost/vendor.js:70900:16)
    at superWrapper [as setupData] (https://localhost/vendor.js:17606:16)

Digging in a bit, the backtrace is pointing to these lines:

https://github.com/lytics/ember-data.model-fragments/blob/master/packages/model-fragments/lib/fragments/model.js#L14-L20

...and the assertion is coming from: https://github.com/emberjs/data/blob/7258c8234e33d26d38f82645ba27612690d3c5ba/packages/ember-data/lib/system/model/model.js#L1056-L1058

Changing those lines to simply:

buildFragment: function(type) {
  return this.createRecord(type, {});
}

seems to work, but I'm not 100% sure the implications there, so I figured I'd create an issue instead of a pull.

ps. as an aside, not sure if you have considered storing the dist in your git repo? I know it's generally a faux pas to store compiled files in git, but this seems to be the most common way to install via bower, cf. https://github.com/components/ember-data

RangeError: Maximum call stack size exceeded

There appears to be an infinite recursion problem with DS.hasManyFragments. So far, I'm pretty sure that this only occurs when model A hasManyFragments of B which hasManyFragments of C.

I've been able to consistently reproduce this error in my application by performing the following steps:

Start off with a model A that has several Bs. Pick one B that has 2 Cs. Remove the second C and save model A. Add a new C to the B we picked earlier and save the model.

From what I can tell after debugging for a while, it seems like the code is doing the following:

  1. hasManyFragments calls setupData
  2. setupData sends the pushedData event
  3. The pushedData event causes the fragment to transitionTo('loaded.saved')
  4. Fragment.RootState.loaded.saved.setup calls get(record, 'C.isDirty')
  5. get(record, 'C.isDirty') invokes hasManyFragments; looping us back around.

Any ideas?

hasMany access fragment by index in a template

Hi. How can I access an item from a hasMany relation, using the index, in a template.
For example, I can use this with array in templates:
array.[0].name
That doesn't work if array is a hasMany relation of the model, like:
model.array.[0].name

Rolling back doesn't reset hasManyFragments fields with Ember Data Beta 12

Hello, it looks like properties within hasManyFragments objects aren't being reset during a rollback. We are using ember data beta 12 so this may be the cause of the problem.

The project's tests pass with beta 7 but when I update to beta 12 there are several test failures which seem related to the issue.

It's possible this is caused by breaking changes in Beta 12 related to observing the model's data property described here.

Here's a link to a JSbin showing the problem.

Deleting fragments

Is there a way of deleting a fragment from a record?

I tried @model.fragmented_attr.deleteRecord(), but I get a Uncaught TypeError: undefined is not a function. Same thing happens when I try to just set the attr to null.

Uncaught TypeError: undefined is not a function app.em.js:1037
(anonymous function) app.em.js:1037
applyStr ember.js:24509
sendEvent ember.js:18970
notifyObservers ember.js:22322
propertyDidChange ember.js:22140
iterDeps ember.js:22185
dependentKeysDidChange ember.js:22167
propertyDidChange ember.js:22137
chainsDidChange ember.js:22243
propertyDidChange ember.js:22139
ComputedPropertyPrototype.set ember.js:16882
set ember.js:22603
Ember.ObjectController.extend.actions.removeMjPreset app.em.js:1037
Mixin.create.send ember.js:36645
runRegisteredAction ember.js:25071
Backburner.run ember.js:2277
apply ember.js:24491
run ember.js:22804
handleRegisteredAction ember.js:25068
(anonymous function) ember.js:47501
jQuery.event.dispatch jquery.js:3671
elemData.handle jquery.js:3301

Unit test failure using Mocha : Error: Assertion Failed: (subclass of DS.ModelFragment) does not appear to be an ember-data model

Using Ember-cli setup with mocha/chai as my test runner and assertions lib (using ember-cli-mocha), I have a simple model class looking like this :

models/Address.js

import DS from "ember-data";

export default DS.ModelFragment.extend({
    civicNumber: DS.attr('number'),
    streetName: DS.attr('string'),
    city: DS.attr('string'),
    provinceOrState: DS.attr('string'),
    country: DS.attr('string'),
    postalZipCode: DS.attr('string'),
    building: DS.belongsTo('building', {embedded: 'always'})
});

With the following test code :

/* jshint expr:true */
import { expect } from 'chai';
import {
  describeModel,
  it
} from 'ember-mocha';

describeModel(
  'address',
  'Address',
  {
    // Specify the other units that are required for this test.
      needs: []
  },
  function() {
    // Replace this with your real tests.
    it('exists', function() {
      var model = this.subject();
      // var store = this.store();
      expect(model).to.be.ok;
    });
  }
);

Which fails with the following error :
not ok 14 PhantomJS 1.9 - Address exists
---
message: >
Assertion Failed: (subclass of DS.ModelFragment) does not appear to be an ember-data model
stack: >
Error: Assertion Failed: (subclass of DS.ModelFragment) does not appear to be an ember-data model
at http://localhost:7357/assets/vendor.js:14489
at http://localhost:7357/assets/vendor.js:74951
at http://localhost:7357/assets/vendor.js:73546
at http://localhost:7357/assets/test-support.js:2092
at http://localhost:7357/assets/vendor.js:10749
at run (http://localhost:7357/assets/vendor.js:27187)
at http://localhost:7357/assets/test-support.js:2093
at http://localhost:7357/assets/test-support.js:2299
at http://localhost:7357/assets/frontend.js:1104
at http://localhost:7357/assets/test-support.js:1686
at invoke (http://localhost:7357/assets/test-support.js:13702)
at http://localhost:7357/assets/test-support.js:13767
at http://localhost:7357/assets/test-support.js:7004
at http://localhost:7357/assets/test-support.js:7423
at http://localhost:7357/assets/test-support.js:7514
at next (http://localhost:7357/assets/test-support.js:7348)
at http://localhost:7357/assets/test-support.js:7358
at next (http://localhost:7357/assets/test-support.js:7297)
at http://localhost:7357/assets/test-support.js:7320
at done (http://localhost:7357/assets/test-support.js:6983)
at http://localhost:7357/assets/test-support.js:7003
at complete (http://localhost:7357/assets/test-support.js:13729)
at http://localhost:7357/assets/test-support.js:13707
at tryCatch (http://localhost:7357/assets/vendor.js:60990)
at invokeCallback (http://localhost:7357/assets/vendor.js:61002)
at publish (http://localhost:7357/assets/vendor.js:60973)
at http://localhost:7357/assets/vendor.js:39112
at http://localhost:7357/assets/vendor.js:11403
at http://localhost:7357/assets/vendor.js:11468
at http://localhost:7357/assets/vendor.js:11273
at http://localhost:7357/assets/vendor.js:10698
at http://localhost:7357/assets/vendor.js:11101
Log: >
...

Am I missing something or it's a real issue?
Any help will be highly appreciated!
Thanks

Unable to install model fragments w/ 0.1.5 and ember-data beta.14.1

If I create a blank project with ember-cli v0.1.5 and update ember-data to beta.14.1 the app will load up just fine. However, after running:

ember install:addon ember-data-model-fragments

I get this error in the browser:

Uncaught ReferenceError: define is not defined

Not sure if I'm the only one getting this or if this is happening to others?

ID attribute not available for nested fragments?

We have a nested JSON document with a structure similar to:

{
  "rates": {
    "name": "An example name",
    "updated_at": "2014-01-01",
    "artifacts": [
      {"id": "3242423423423", "name": "Artifact Name"}
      {"id": "2309943211455", "name": "Second Artifact"}
    ]
  }
}

In our template we are trying to iterate over the records and create a link to the nested artifact based on it's ID.

each artifact in model.artifacts
  link-to "documents" model artifact

We can access all the other properties in the template, such as artifact.name, but whenever we call artifact.id we get undefined.

Any ideas how to access the ID property of the nested fragment object? I have verified that the ID property is getting returned in the data.

ember-data beta 7 restriction

Are there plans to increase the ember data version restriction, we have come across many bugs in our app that require newer version and this is the one reason many fixes have to get worked around.

This project is not yet compatible with Ember Data v1.0.0-beta.12

Could the readme possibly expand on:

This project is not yet compatible with Ember Data v1.0.0-beta.12

I.e. what at the implications of this, and how do we avoid them.

I'm fairly new to ember, and I've got an ember-cli app with ember-data at "1.0.0-beta.14.1" (in both my package.json and bower.json, but I'm not sure how to downgrade correctly.

Fragment values reset on changes after first save

Basically if you change a nested fragment's value, and then save the parent model it works. Now if you go and make another change, and save, it resets the value to the previous value upon save.

Here's an example setup:

// model
export default DS.Model.extend({
  name: attr('string'),
  subdomain: attr('string'),
  settings: oneFragment('community-setting')
});

// community-setting fragment
export default DS.ModelFragment.extend({
  disclaimer: oneFragment('settings-disclaimer'),
  contact: oneFragment('settings-contact'),
  links: manyFragments('settings-link'),
  map: oneFragment('settings-map')
});

// settings-disclaimer fragment
export default DS.ModelFragment.extend({
  showOnFirstVisit: attr('boolean'),
  message: attr('string')
});

In the above code, if I modify community.settings.disclaimer.message, and community.save() it works fine. Now if I don't refresh the page, and change the value again, and hit save, the value is changed back to what it was last before this change.

So It's kind hard to create a jsbin with an ember-cli addon, so I hope this is enough.

Adding `hasOne` fragment to unsaved record?

Hi,

Happy Easter.

I'm trying to add a fragment to an unsaved record like so:

this.store.createRecord('comment', {user: {email: '[email protected]'}})

This blows up with: Error: Assertion Failed: You can only assign a 'user' fragment to this property

OK, so I can create a fragment like so:

this.store.createRecord('comment', {
  user: this.store.createFragment('user', {email: '[email protected]'})
})

This works, but blows up after save on this line: https://github.com/lytics/ember-data.model-fragments/blob/master/packages/model-fragments/lib/fragments/states.js#L63

record is undefined because the _owner of the fragment doesn't get set above. I can set it with user.setProperties({_owner: comment, _name: 'user'}); but this feels wrong to me.

TL;DR What is the best way to add a hasOne fragment to an unsaved record?

Thanks!!

Multiple rollbacks don't work starting with 0.2.4

See this JSBIN: http://emberjs.jsbin.com/yemimobire/5/edit

This is working with 0.2.3, but broken with 0.2.4 and 0.2.6 (didn't bother checking 0.2.5).

Edit: Sorry for the poor description.

The first time you call rollback on a model, it works correctly (restoring the original value and setting isDirty back to false), the second time you call rollback on that same model it sets isDirty back to false, but does no restore the value.

Relationships within a fragment

Great work on the fragments library so far; I've found it very helpful in upgrading from the old Ember Data to the new 1.0 Beta series.

However, I am wondering if we could re-enable relationships inside of fragments. There are several instances in my data-model where my fragments "point to" other models. Before, they would use a "belongsTo" relationship; but now that doesn't seem to work. I've found workarounds, but they haven't been seamless for all use-cases.

This is the scenario where I'm most frequently running into this issue:

A many-to-many relationship between models A and B where A has a list of fragments, C. C has the "belongsTo" relationship with B along with other fields that "describe" the relationship. B, meanwhile, doesn't know anything about A or C.

My current work-around looks like this:

App.Transaction = DS.Model.extend
  purchases: DS.hasManyFragments 'purchase'
  ...

App.Purchase = DS.ModelFragment.extend
  product_id: DS.attr 'string'
  cost: DS.attr 'number'
  quantity: DS.attr 'number'
  product: (() ->
    @store.find('product', @get('product_id'))
  ).property('product_id')

App.Product = DS.Model.extend
  name: DS.attr 'string'
  ...

However, if Transaction needs to compute a property based on the Products' name (to list all products purchased, for instance), then I'm suddenly forced to implement a much more obscure work-around to get the Transaction's computed property to update in templates. I'm pretty sure that this is due to the fact that store.find returns a Promise.

If someone has a much better work-around to this issue, please let me know. Right now, it seems to me that allowing Fragments to have "belongsTo" relationships is the easiest way to correct the problem while keeping DS.ModelFragment's usage similar to DS.Model's current usage.

"Attempting to register an unknown factory: `transform:fragment`" in serializer test

I'm using ember-cli 0.2.3 and model-fragments 0.3.1. I have a serializer test for a model with fragments.

The test is currently just the default generated by ember-cli with added needs, namely

import {
    moduleForModel,
    test
} from 'ember-qunit';

moduleForModel('invite-submission', {
    needs: [
        'serializer:invite-submission',
        'transform:fragment',
        'model:invite-review',
        'model:album-fragment',
        'model:artist-fragment'
    ]
});

// Replace this with your real tests.
test('it serializes records', function(assert) {
    var record = this.subject();

    var serializedRecord = record.serialize();

    assert.ok(serializedRecord);
});

If I include 'transform:fragment' in my needs array as it is above, I get an error Attempting to register an unknown factory: 'transform:fragment'. If I leave out the transform declaration from the needs array, I get Assertion Failed: Unable to find transform for 'fragment'.

I'm not totally sure about the correct path forward other than stripping out the test. Nor do I know if this is an issue with model-fragments or a shortcoming of ember-cli's test suite, but I thought I'd start here. I appreciate any insight.

Issues using the ember data fragments while trying to load child data fragments on demand.

I am working on a use case something like this :

var Contact = DS.Model.extend({
 firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  addresses : DS.hasManyFragments('address'),
  phones : DS.hasManyFragments('phone'),
  interactions : DS.hasManyFragments('interaction')
 });

Contact can have one or more addresses , phones and interactions.
However my use case restricts me from load the whole contact record at one ago.

what i am trying to do is load addresses , phones and interaction on demand.
So initially only the contact record with firstname and lastname gets loaded.

I make an call from my Contact controller like : return this.store.find('contact',1);
which returns me
{"contacts":{"id":1,"firstName":"Joe","lastName":"Smith"}}.

Now later i want to get all the addresses related to contacts/1.
var addresses = this.store.find('address',{contactId:1});
I get a json response

{"addresses":[{"id":"200","street":"6262 Sunset Drive","city":"Miami","state":"Zip"}]}

as soon as it gets this response back in controller the application errors out.

I get this error in the logs:

  Error: Assertion Failed: Error: Assertion Failed: `interval-intl-ember-clii@model:address:` does not appear to be an ember-data model.

My question is it possible to use data Fragments to get child objects on demand and attach to the parent object or do i have to get the whole contact object at one go (which i am reluctant to do as i might impact performance )

I am using data fragments as persistence of the child objects is managed completely through the parent object

Cant access hasManyFragment array from model computed property

Am I right in assuming that we cannot access fragment content from a computed property on the parent model?

I removed my actual logic and simplified the return to just the array length,and with the following code I get 0 returned when the lines property on the order model is populated.

// /models/order.js
import DS from 'ember-data';

export default DS.Model.extend({
    rev: DS.attr('string'),
    lines: DS.hasManyFragments('order-line',{defaultValue: []}),
    comment: DS.hasOneFragment('order-comment'),
    type: DS.attr('string', {defaultValue: 'order'}),
    origin: DS.attr(),
    orderTotal: function(){
      var lines = this.get('lines.length');
      return lines;
    }.property('lines')
});


// /models/order-lines.js
import DS from 'ember-data';

export default DS.ModelFragment.extend({
  productCode: DS.attr('string'),
  productTitle: DS.attr('string'),
  quantity: DS.attr('string'),
  quantityBalance: DS.attr('string'),
  price: DS.attr('string'),
  discount: DS.attr('string'),
  status: DS.attr('string'),
  notes: DS.attr('string'),
  revised: DS.attr('string', {
    defaultValue: function() { return Date.now (); }
  }),
  lineTotal: function(){
    var total = parseFloat(this.get('price')) * parseFloat(this.get('quantity'));
    return total.toFixed(2);
  }.property('price', 'quantity')
});
<!-- /templates/order-summary.hbs -->
          <tfoot>
            <tr>
              <td></td>
              <td></td>
              <td></td>
              <td colspan="2"><strong>Order Total</strong></td>
              <td colspan="2">{{controllers.customers.currentOrder.orderTotal}}</td>
            </tr>
          </tfoot>

n.b. currentOrder is a saved Order record.

Ember-cli throws warning for ember-data-model-fragments

The package `ember-data-model-fragments` is not a properly formatted package, we
 have used a fallback lookup to resolve it at `c:\Projects\GitHub\jordan-hawker\
node_modules\ember-data-model-fragments`. This is generally caused by an addon n
ot having a `main` entry point (or `index.js`).

ember-cli install fails

Here's the error after ember install:addon ember-data-model-fragments:

version: 0.1.5
Installing packages for tooling via npm.sh: ./node_modules/bower/bin/bower: No such file or directory
[email protected] postinstall: `./node_modules/bower/bin/bower install`
Exit status 127
Error: [email protected] postinstall: `./node_modules/bower/bin/bower install`
Exit status 127
    at EventEmitter.<anonymous> (/Users/iradchenko/workspace/mapgeo2/dashboard/node_modules/ember-cli/node_modules/npm/lib/utils/lifecycle.js:212:16)
    at EventEmitter.emit (events.js:98:17)
    at ChildProcess.<anonymous> (/Users/iradchenko/workspace/mapgeo2/dashboard/node_modules/ember-cli/node_modules/npm/lib/utils/spawn.js:14:12)
    at ChildProcess.EventEmitter.emit (events.js:98:17)
    at maybeClose (child_process.js:743:16)
    at Process.ChildProcess._handle.onexit (child_process.js:810:5)

Error on save() when observing hasManyFragments property

Live example (hit save): http://jsbin.com/yibuniqami/1/edit?js,console,output

This happens when observing a hasManyFragments array, e.g.

whoops: function() {}.observes('hasManyFragmentProperty.[]')

My REST API responds with the server's version of the model. The client chokes with this error when it tries to update its version

TypeError: Cannot read property 'destroy' of undefined
    at ContainerView.extend.arrayWillChange (http://builds.emberjs.com/tags/v1.9.0/ember.js:40448:20)
    at applyStr (http://builds.emberjs.com/tags/v1.9.0/ember.js:19670:29)
    at sendEvent (http://builds.emberjs.com/tags/v1.9.0/ember.js:14106:13)
    at __exports__.default.Mixin.create.arrayContentWillChange (http://builds.emberjs.com/tags/v1.9.0/ember.js:30434:9)
    at EmberObject.extend.arrangedContentArrayWillChange (http://builds.emberjs.com/tags/v1.9.0/ember.js:34301:14)
    at applyStr (http://builds.emberjs.com/tags/v1.9.0/ember.js:19670:29)
    at sendEvent (http://builds.emberjs.com/tags/v1.9.0/ember.js:14106:13)
    at Array.__exports__.default.Mixin.create.arrayContentWillChange (http://builds.emberjs.com/tags/v1.9.0/ember.js:30434:9)
    at Array.Mixin.create.replace (http://builds.emberjs.com/tags/v1.9.0/ember.js:35815:14)
    at StatefulArray.extend.replaceContent (https://rawgit.com/lytics/ember-data.model-fragments/master/dist/ember-data.model-fragments.js:312:37)

If I change from observing [] to @each I get this error instead:

RangeError: Maximum call stack size exceeded
    at GETTER_FUNCTION [as _parentView] (http://builds.emberjs.com/tags/v1.9.0/ember.js:17067:38)
    at null.<anonymous> (http://builds.emberjs.com/tags/v1.9.0/ember.js:41363:26)
    at Descriptor.ComputedPropertyPrototype.get (http://builds.emberjs.com/tags/v1.9.0/ember.js:12122:25)
    at get (http://builds.emberjs.com/tags/v1.9.0/ember.js:17570:21)
    at CoreView.extend.destroy (http://builds.emberjs.com/tags/v1.9.0/ember.js:43560:36)
    at apply (http://builds.emberjs.com/tags/v1.9.0/ember.js:19652:32)
    at superWrapper [as destroy] (http://builds.emberjs.com/tags/v1.9.0/ember.js:19226:15)
    at ContainerView.extend.arrayWillChange (http://builds.emberjs.com/tags/v1.9.0/ember.js:40448:21)
    at applyStr (http://builds.emberjs.com/tags/v1.9.0/ember.js:19670:29)
    at sendEvent (http://builds.emberjs.com/tags/v1.9.0/ember.js:14106:13)

I can work around it by removing the fragments from the response in the adapter.

DS is not defined

After following the standard install instructions I get the following error in the development console

Uncaught ReferenceError: DS is not defined

This seems to be because ember-data isn't necessarily loaded before ember-data.model-fragments is loaded.

A quick fix is adding the following into Brocfile.js, after importing ember data:
app.import('bower_components/ember-data.model-fragments/dist/ember-data.model-fragments.js');

After working this bug through with @igorT he has suggested to do the above as part of the ember generate step.

hasManyFragments ignoring array defaultValue

Hi,

I posted an issue related to defaultValue awhile back (#36). Using a empty array as the defaultValue solved the issue at the time.

Having now updated the project to 0.3.1, I receive the following error which I have tracked down to the initialisation of the "collection-product" fragment:

Uncaught Error: Assertion Failed: A fragment array property can only be assigned an array or null

Any ideas? Code below:

// /models/collection.js
import DS from 'ember-data';

export default DS.Model.extend({
    rev: DS.attr('string'),
    type: DS.attr('string'),
    name: DS.attr('string'),
  products: DS.hasManyFragments('collection-product', {defaultValue: []}),
    meta: DS.hasOneFragment('collection-meta'),
  credentials: DS.hasManyFragments()
});
// /models/collection-product.js
import DS from 'ember-data';

export default DS.ModelFragment.extend({
  product: DS.attr('string'),
  productCode: DS.attr('string'),
  productTitle: DS.attr('string'),
  order: DS.attr('string')
});
// /controllers/collections-modal.js
    createCollection: function(){
      var collection = this.store.createRecord('collection');
      collection.set('meta', this.store.createFragment('collection-meta'));
      //error occurs on the line below
      collection.set('products', this.store.createFragment('collection-product'));
      collection.set('type', 'collection');
      collection.set('meta.creator', 'user');
      collection.set('meta.username', this.get('controllers.application.currentUser.object.user.name'));

      this.set('newUserCollection', {
        enabled: true,
        object: collection
      });
    },

Ember CLI: 0.2.2
Ember Data: 1.0 Beta 16
Ember: 1.11.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.