
Security News
Next.js Patches Critical Middleware Vulnerability (CVE-2025-29927)
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
ember-data-model-fragments
Advanced tools
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.
:warning: This project is not compatible with Ember Data v1.0.0-beta.12, due to a bug in that version. Use the following table to decide which version of this project to use with your app:
Ember Data | Model Fragments |
---|---|
> v1.0.0-beta.7 <= v1.0.0-beta.11 | v0.2.3 |
> v1.0.0-beta.14 | > v0.2.4 |
To install as an Ember CLI addon:
$ ember install:addon ember-data-model-fragments
Or if you are using Ember CLI < 0.1.5:
$ npm install ember-data-model-fragments --save-dev
$ ember generate 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 DS.ModelFragment
class with the given attributes.
App.Person = DS.Model.extend({
name : DS.hasOneFragment('name'),
addresses : DS.hasManyFragments('address'),
titles : DS.hasManyFragments()
});
App.Name = DS.ModelFragment.extend({
first : DS.attr('string'),
last : DS.attr('string')
});
App.Address = DS.ModelFragment.extend({
street : DS.attr('string'),
city : DS.attr('string'),
region : DS.attr('string'),
country : DS.attr('string')
});
With a JSON payload of:
{
"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:
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.rollback();
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('isDirty'); // true
The addresses
attribute can be treated similar to a hasMany
relationship:
var person = store.getById('person', '1');
var addresses = person.get('addresses');
var address = addresses.get('lastObject');
person.get('isDirty'); // false
address.get('country'); // 'Westeros'
address.set('country', 'Essos');
person.get('isDirty'); // true
person.rollback();
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('isDirty'); // true
The titles
attribute can be treated as an Ember.Array
:
var person = store.getById('person', '1');
var titles = person.get('titles');
person.get('isDirty'); // false
titles.get('length'); // 2
titles.pushObject('Halfman');
titles.get('length'); // 3
person.get('isDirty'); // true
person.rollback();
titles.get('length'); // 2
Ember Data attributes support a defaultValue
config option that provides a default value when a model is created through store#createRecord()
. Similarly, DS.hasOneFragment
and DS.hasManyFragments
properties support a defaultValue
option:
App.Person = DS.Model.extend({
name : DS.hasOneFragment('name', { defaultValue: { first: 'Faceless', last: 'Man' } }),
addresses : DS.hasManyFragments('address', { defaultValue: [] }),
titles : DS.hasManyFragments(null, { defaultValue: [] })
});
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, both DS.hasOneFragment
and DS.hasManyFragments
properties will default to null
. Note that this may cause confusion when creating a record with a DS.hasManyFragments
property:
var person = store.createRecord('person');
var addresses = person.get('addresses'); // null
// Fails with "Cannot read property 'createFragment' of null"
addresses.createFragment({
...
});
Like DS.attr
, the defaultValue
option can be a function that is invoked to generate the default value:
App.Person = DS.Model.extend({
name: DS.hasOneFragment('name', {
defaultValue: function() {
return {
first: 'Unsullied',
last: Ember.uuid()
}
}
})
});
Nesting of fragments is fully supported:
App.User = DS.Model.extend({
name : DS.attr('string'),
orders : DS.hasManyFragments('order')
});
App.Order = DS.ModelFragment.extend({
amount : DS.attr('string'),
products : DS.hasManyFragments('product')
});
App.Product = DS.ModelFragment.extend({
name : DS.attr('string'),
sku : DS.attr('string'),
price : DS.attr('string')
});
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:
var user = store.getById('user', '1');
var product = user.get('orders.firstObject.products.lastObject');
user.get('isDirty'); // false
product.get('price'); // '299.99'
product.set('price', '1.99');
user.get('isDirty'); // true
user.rollback();
user.get('isDirty'); // false
product.get('price'); // '299.99'
However, note that fragments do not currently support DS.belongsTo
or DS.hasMany
properties. See the Limitations section below.
Ember Data: Model Fragments has support for reading polymorphic fragments. To use this feature, pass an options object to hasOneFragment
or hasManyFragments
with polymorphic
set to true. In addition the typeKey
can be set, which defaults to 'type'
.
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.
In the following example the declared type of animals
is animal
, which corresponds to the class App.Animal
. App.Animal
has two subclasses: App.Elephant
and App.Lion
,
so to typeKey
's value can be 'animal'
, 'elephant'
or 'lion'
.
App.Zoo = DS.Model.extend({
name: DS.attr("string"),
city: DS.attr("string"),
animals: DS.hasManyFragments("animal", { polymorphic: true, typeKey: '$type' }),
});
App.Animal = DS.ModelFragment.extend({
name: DS.attr("string"),
});
App.Elephant = Animal.extend({
trunkLength: DS.attr("number"),
});
App.Lion = Animal.extend({
hasManes: DS.attr("boolean"),
});
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.AnimalSerializer = DS.JSONSerializer.extend({
serialize: function(record, options) {
var json = this._super(record, options);
if (record instanceof App.Elephant) {
json.$type = 'elephant';
} else if (record instanceof App.Lion) {
json.$type = 'lion';
} else {
json.$type = 'animal';
}
return json;
}
});
App.ElephantSerializer = App.AnimalSerializer;
App.LionSerializer = App.AnimalSerializer;
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 DS.EmbeddedRecordMixin
instead).
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.
Currently, fragments cannot have normal DS.belongsTo
or DS.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.
Building requires Grunt and running tests requires Test 'Em, which can both be installed globally with:
$ npm install --global grunt-cli bower testem
Then install NPM packages, build the plugin, and start the development test server:
$ npm install
$ bower install
$ grunt build
$ testem
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.
v0.2.6 (January 8, 2015)
ember install:addon
not invoking correct blueprintFAQs
Ember Data addon to support nested JSON documents
The npm package ember-data-model-fragments receives a total of 10,146 weekly downloads. As such, ember-data-model-fragments popularity was classified as popular.
We found that ember-data-model-fragments demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
Security News
A survey of 500 cybersecurity pros reveals high pay isn't enough—lack of growth and flexibility is driving attrition and risking organizational security.
Product
Socket, the leader in open source security, is now available on Google Cloud Marketplace for simplified procurement and enhanced protection against supply chain attacks.