icereval / backbone-documentmodel Goto Github PK
View Code? Open in Web Editor NEWA plugin to create entire Document structures with nested Backbone.js Models & Collections with deep model references and event bubbling.
License: MIT License
A plugin to create entire Document structures with nested Backbone.js Models & Collections with deep model references and event bubbling.
License: MIT License
With an object like this:
var obj = {
id: 'a1',
foo: {
id: 'b2',
bar: 'baz'
}
}
instantiation of foo
seems to fail, it's simply omitted and the top-level Model doesn't have the expected attribute (e.g. model.get('foo')
returns undefined
).
As far as I can tell, it's this line's fault.
I knew #12 was solved back in May, but after circling around an obscure bug for half an hour, I realized the last 3 commits never made it to the npm registry. Npm says the package was last published on April 20.
So, could you please do a version bump and npm publish
?...
Thanks a lot!
Calling toJSON on a collection containing models that have string[] as an attribute will return these string[] as an array of undefined in the json object.
I encountered what I believe is a nasty edge case when calling toJSON when iterating on the models of a nested collection.
//TEST
console.error("_________________________________________________");
var fakeLocRaw = {
name : 'footestLoc',
emails : ['[email protected]', '[email protected]', '[email protected]']
};
var loc = new Backbone.DocumentModel(fakeLocRaw);
console.error(loc);
console.error(loc.toJSON());
console.error("loc.get('emails') = ");
console.error(loc.get('emails'));
var emlJSON = loc.get('emails').toJSON();
console.log(emlJSON);
loc.get('emails').each(function(eml){
console.error("eml = ");
console.error(eml.toJSON());
});
output :
_________________________________________________
Backbone.Model.extend.constructor {cid: "c5112", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
Object {name: "footestLoc", emails: Array[3]}
loc.get('emails') =
Backbone.Collection.extend.constructor {idAttribute: "id", parent: Backbone.Model.extend.constructor, name: "emails", length: 3, models: Array[3]…}
["[email protected]", "[email protected]", "[email protected]", remove: function, removeObj: function, pushArray: function, move: function]
eml =
Object {id: "5113", value: "[email protected]"}
eml =
Object {id: "5115", value: "[email protected]"}
eml =
Object {id: "5117", value: "[email protected]"}
_________________________________________________
Notice how the id attribute is added to the email'JSON when we are iterating over the emails collection with the 'each' method and how that does not occur if calling loc.get('emails').toJSON() .
It would be nice to have custom models and collections for nested values based on keys, data and options.
I’d suggest to introduce factory functions that create Backbone.DocumentModel
and Backbone.DocumentCollection
objects instead of having the base classes instantiated explicitly (Models and Collections).
These factory functions would get added to each Backbone.DocumentModel
as prototype members, so that you can extend the default behaviour easily.
Hi,
I see how arrays are turned into Collections containing Models with "pseudoIdAttributes", which i understand is necessary so that the data can we reconverted to an array on it's way back up the pipe. The issue is that when you pass one of these special models with pseudoIdAttributes to a itemView in Backbone, the template receives a string instead of an object. Marionette hangs templateHelpers on this object, so basically using DocumentModel kills Marionette's templateHelpers.
I can't imagine this is something you'd be able to solve since you can't know in which context toJSON is being called. I'm having to work around it by modifying Marionette's serializeData method on itemViews to convert any strings into objects with a named key:value pair. Just an FYI, unless you have any ideas?
In the following JSON object:
var user = {
addresses: [
{ type: 'Shipping', city: 'Charlottesville', state: 'VA',
items: [
{id: 1, name: "item1"},
{id: 2, name: "item2"}
]
},
{ type: 'Billing', city: 'Prescott', state: 'AZ',
items: [
{id: 1, name: "item1"},
{id: 2, name: "item2"}
]
}
]
};
I can't to override nested collection for 'items' and model for 'items' children:
User = Backbone.DocumentModel.extend({
getNestedModel: function (nestedKey, nestedValue, nestedOptions) {
console.log("Never executed here");
return new Backbone.DocumentModel(nestedValue, nestedOptions);
},
getNestedCollection: function (nestedKey, nestedValue, nestedOptions) {
console.log('nestedKey is "addresses" only' );
return new Backbone.DocumentCollection(nestedValue, nestedOptions);
}
});
var john = new User(user);
I don't understand the reasoning for not propagating the change event from subdocuments. If I understand correctly, part of the design purpose of this plugin is to allow people work with an entire document at a time easily, but this seems to go against the grain of that. When loading one would pass an entire document to the constructor and when saving one would use toJSON() to retrieve the entire document. It would also be very helpful to get a change notification for the entire document.
I understand it would be difficult to differentiate between changes in the root node and changes in subdocuments, but the point of the project was to smooth over the fact that there are subdocuments. If this is a problem, however, a possible solution is to convert the subdocument change events into something else, like childChange
or something.
I have an array of subdocuments. When I add a new one, the subdocument gets an 'id' attribute added. (In my case it gets an _id
since I have idAttribute='_id'
.)
In my app I am dealing with collections of objects and also changing their order in the UI which affects the indexing. It would be great to have support for nested paths like:
'myCollection.c231.myProperty' where '.c231.' is recognized as Backbone's Model CID (http://backbonejs.org/#Model-cid) of an item in a collection instead of an index.
For example:
m.get('myCollection.c231.myProperty') // this actually works
m.set('myCollection.c231.myProperty', 'some value') // but this doesn't
The reason why get() works is support for CID in Backbone.Collection.get() http://backbonejs.org/#Collection-get
Having support for CIDs one could define a CID-based bindings of input elements on the HTML page, which IMHO is more reliable than the indexed-based.
The index-based requires re-rendering the whole list each time the order of items changes.
Within my Backbone/Marionette/Handlebars application where a nested array was being passed into my Handlebars template helper function, all the array values were 'undefined'.
Everything else was working perfectly throughout the rest of the application, being able to .get() and .set() model & collection values. These only seems to be an issue when the nested objects or arrays are passed to a template helper.
I get an exception sice the new 0.6.2 version you released earlier today.
Uncaught TypeError: Cannot call method 'call' of undefined backbone-documentmodel.js:112
(anonymous function) backbone-documentmodel.js:112
.each..forEach underscore.js:79
documentModelSet backbone-documentmodel.js:108
Backbone.Model backbone.js:254
Backbone.Model.extend.constructor backbone-documentmodel.js:324
child backbone.super.js:21
child backbone.super.js:21
This did not occur before on the exact same model.
This looks like a really great take on the Backbone associations problem. I like how you can get up and running with live Model / Collection associations with zero configuration.
That being said it would be great if you could define relationship types for sub attributes. This might look something like how BackboneRelational does it:
relations: [{
type: Backbone.HasMany,
key: 'animals',
relatedModel: 'Animal',
collectionType: 'AnimalCollection',
reverseRelation: {
key: 'livesIn',
includeInJSON: 'id'
// 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
}
}]
I'm using relational now and it works very well. In particular I like how it keeps track of all models across the app in singleton "table" collections – very useful for preventing the creation of multiple model instances representing the same data model.
But the pain point is when I have a 3 level deeply nested model and I would have to define three trivial Model classes just to define all the associations.
It seems like if you added the ability to define types for the nested attributes this would be the ultimate solution to the Backbone relationship problem.
I think there is a problem with the way String objects are parsed / added to the Model.
Here is a little example :
First test without DocumentModel
var MySimpleModel = Backbone.Model.extend({});
var simpleInst = new MySimpleModel({
foo : new String("helleuuuuuuu"),
baz : [ 'aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd',],
});
console.error(simpleInst.toJSON());
/**
prints the following to the console :
Object {foo: String, baz: Array[14]}
baz: Array[14]
0: "aasd"
1: "aasd"
2: "aasd"
3: "aasd"
4: "aasd"
5: "aasd"
6: "aasd"
7: "aasd"
8: "aasd"
9: "aasd"
10: "aasd"
11: "aasd"
12: "aasd"
13: "aasd"
length: 14
__proto__: Array[0]
foo: String
0: "h"
1: "e"
2: "l"
3: "l"
4: "e"
5: "u"
6: "u"
7: "u"
8: "u"
9: "u"
10: "u"
11: "u"
length: 12
__proto__: String
[[PrimitiveValue]]: "helleuuuuuuu"
__proto__: Object
*/
$("#div1").text(JSON.stringify(simpleInst.toJSON()) );
/**
outputs the following in the div1 div
{"foo":"helleuuuuuuu","baz":["aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd"]}
*/
Second test WITH DocumentModel :
var MyDocumentModel = Backbone.DocumentModel.extend({});
var dmInst = new MyDocumentModel({
foo : new String("helleuuuuuuu"),
baz : [ 'aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd','aasd',], });
//DOCUMENT MODEL HAS A PROBLEM WHEN CONVERING A STRING OBJECT
console.error(dmInst.toJSON());
/**
Outputs the following to the console
Object {foo: Object, baz: Array[14]}
baz: Array[14]
0: "aasd"
1: "aasd"
2: "aasd"
3: "aasd"
4: "aasd"
5: "aasd"
6: "aasd"
7: "aasd"
8: "aasd"
9: "aasd"
10: "aasd"
11: "aasd"
12: "aasd"
13: "aasd"
length: 14
__proto__: Array[0]
foo: Object
0: "h"
1: "e"
2: "l"
3: "l"
4: "e"
5: "u"
6: "u"
7: "u"
8: "u"
9: "u"
10: "u"
11: "u"
__proto__: Object
__proto__: Object
NOTICE the foo variable is now an Object and not a String
*/
$("#div2").text(JSON.stringify(dmInst.toJSON()) );
/**
outputs the following to the div2 div :
{"foo":{"0":"h","1":"e","2":"l","3":"l","4":"e","5":"u","6":"u","7":"u","8":"u","9":"u","10":"u","11":"u"},"baz":["aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd","aasd"]}
Notice how foo is now unusable :)
*/
You can also try to add a new String("asdasd") inside the baz array and see what happens
This should be a no-brainer and allows people to use the plugin on Node.js or with browserify.
Here is the problem:
test("setting nested array to existing model",2,function(){
var model = new Backbone.DocumentModel({items:[]});
model.set('items', [[1],[2]]);
equal(model.get('items.0') instanceof Backbone.Model, false);
equal(model.get('items.0') instanceof Backbone.Collection, true);
});
both tests failed
It actually creates Backbone.Model object with keys 0 and 1. Is this an expected behaviour?
Hi,
I have such code:
this.model = new Backbone.DocumentModel();
this.model.set({
id: 123,
user: {
type: 'Spy',
name: {
first: 'Sterling',
last: 'Archer'
}
},
otherSpies: [
{ name: 'Lana' },
{ name: 'Cyrril' }
]
});
When I tried to get "user" DocumentModel I got undefined. I checked attributes, the "user" object was ignored:
this.model.get('user') // undefined
JSON.stringify(this.model.toJSON()) // "{"id":123,"otherSpies":[{"name":"Lana"},{"name":"Cyrril"}]}"
When calling fetch() to populate a DocumentCollection, then calling set() 2 times on one of the models of the collection triggers an exception if the model data contains an array.
example :
var Job7 = Backbone.DocumentModel.extend({
className : 'Job7',
});
var JobCollection7 = Backbone.DocumentCollection.extend({
model : Job7
});
function doTest7(){
$.getJSON("job1_1.json", function(job1) {
var coll2 = new JobCollection7([job1],{});
coll2.fetch({url : 'job1_1.json'}).complete(function(){
console.error("coll2 - 1");
console.error(coll2);
console.error( JSON.stringify(coll2) );
debugger;
console.error("");
$.getJSON("job1_1.json", function(job2) {
coll2.findWhere({ objectId : '545c8659e7e0a59538543a79'} ).set(job2);
console.error("coll2 - 2");
console.error(coll2);
console.error( JSON.stringify(coll2) );
debugger;
console.error("");
$.getJSON("job1_1.json", function(job3) {
coll2.findWhere({ objectId : '545c8659e7e0a59538543a79'} ).set(job3);
console.error("coll2 - 3");
console.error(coll2);
debugger;
console.error( JSON.stringify(coll2) );
console.error("");
});
});
});
});
}
content of the json :
{
"jobStatus": "CREATED",
"objectId": "545c8659e7e0a59538543a79",
"jobTasksByDate":
[
"545c8659e7e0a59538543a7b",
"545c8659e7e0a59538543a7e"
]
}
The error is thrown durin the 2nd call to set() on the model that is inside the collection and throws :
Uncaught TypeError: Cannot read property 'cid' of undefined
It seems only to happen when fetch is used and only when the underlying model has a member array.
I am having a hard time figuring out why.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.