//Convert the associations into model definitions. var Location = Backbone.AssociatedModel.extend({ defaults:{ add1:"", add2:null, zip:"", state:"" } }); var Project = Backbone.AssociatedModel.extend({ relations:[ { type:Backbone.Many, key:'locations', relatedModel:Location } ], defaults:{ name:"", number:0, locations:[] } }); var Department = Backbone.AssociatedModel.extend({ relations:[ { type:Backbone.Many, key:'controls', relatedModel:Project }, { type:Backbone.Many, key:'locations', relatedModel:Location } ], defaults:{ name:'', locations:[], number:-1, controls:[] } }); var Dependent = Backbone.AssociatedModel.extend({ validate:function (attr) { return (attr.sex && attr.sex != "M" && attr.sex != "F") ? "invalid sex value" : undefined; }, defaults:{ fname:'', lname:'', sex:'F', //{F,M} age:0, relationship:'S' //Values {C=Child, P=Parents, S=Spouse} } }); var Employee = Backbone.AssociatedModel.extend({ relations:[ { type:Backbone.One, key:'works_for', relatedModel:Department }, { type:Backbone.Many, key:'dependents', relatedModel:Dependent }, { type:Backbone.One, key:'manager', relatedModel:'Employee' } ], validate:function (attr) { return (attr.sex && attr.sex != "M" && attr.sex != "F") ? "invalid sex value" : undefined; }, defaults:{ sex:'M', //{F,M} age:0, fname:"", lname:"", works_for:{}, dependents:[], manager:null } });
emp = new Employee({ fname:"John", lname:"Smith", age:21, sex:"M" }); child1 = new Dependent({ fname:"Jane", lname:"Smith", sex:"F", relationship:"C" }); child2 = new Dependent({ fname:"Barbara", lname:"Ruth", sex:"F", relationship:"C" }); parent1 = new Dependent({ fname:"Edgar", lname:"Smith", sex:"M", relationship:"P" }); loc1 = new Location({ add1:"P.O Box 3899", zip:"94404", state:"CA" }); loc2 = new Location({ add1:"P.O Box 4899", zip:"95502", state:"CA" }); project1 = new Project({ name:"Project X", number:"2" }); project2 = new Project({ name:"Project Y", number:"2" }); project2.get("locations").add(loc2); project1.get("locations").add(loc1); dept1 = new Department({ name:"R&D", number:"23" }); dept1.set({locations:[loc1, loc2]}); dept1.set({controls:[project1, project2]}); emp.set({"dependents":[child1, parent1]});
emp.on('change', function () { console.log("Fired emp > change..."); //emp.hasChanged() === true; //emp.hasChanged("works_for") === true; }); emp.on('change:works_for', function () { console.log("Fired emp > change:works_for..."); var changed = emp.changedAttributes(); //changed['works_for'].toJSON() equals emp.get("works_for").toJSON() //emp.previousAttributes()['works_for'].get('name') === ""); //emp.previousAttributes()['works_for'].get('number') === -1; //emp.previousAttributes()['works_for'].get('locations').length === 0; //emp.previousAttributes()['works_for'].get('controls').length === 0; }); emp.set({works_for:dept1}); //Console log //Fired emp > change:works_for... //Fired emp > change...
emp.get('works_for').on('change', function () { console.log("Fired emp.works_for > change..."); //emp.get("works_for").hasChanged() === true; //emp.get("works_for").previousa-tributes()["name"] === "R&D"; }); emp.get('works_for').on('change:name', function () { console.log("Fired emp.works_for > change:name..."); }); emp.on('change:works_for.name', function () { console.log("Fired emp > change:works_for.name..."); //emp.get("works_for").hasChanged() === true; //emp.hasChanged() === true; //emp.hasChanged("works_for") === true; //emp.changedAttributes()['works_for'].toJSON() equals emp.get("works_for").toJSON(); //emp.get("works_for").previous-attributes()["name"] === "R&D"; //emp.get("works_for").previous("name") === "R&D"; }); emp.on('change:works_for', function () { console.log("Fired emp > change:works_for..."); //emp.hasChanged()); //emp.hasChanged("works_for")); //emp.changedAttributes()['works_for'].toJSON() equals emp.get("works_for").toJSON(); //emp.previous-attributes().works_for.name === "R&D"; }); emp.on('nested-change', function () { console.log("Fired emp > nested-change..."); }); emp.get('works_for').set({name:"Marketing"}); //Console log // @ emp.get('works_for') level //Fired emp.works_for > change:name //Fired emp.works_for > change... //@ emp level //Fired emp > change:works_for.name... //Fired emp > change:works_for... //Fired emp > nested-change...
//The actual set operation emp.get('works_for').get("locations").at(0).set('zip', 94403);A simple update like the one above can be listened to at various levels. See the examples below.
//Listen in @ emp.works_for.locations[0]. Should receive regular backbone events emp.get('works_for').get('locations').at(0).on('change:zip', function () { console.log("Fired emp.works_for.locations[0] > change:zip..."); }); emp.get('works_for').get('locations').at(0).on('change', function () { console.log("Fired emp.works_for.locations[0] > change..."); }); //@ emp.get('works_for').get('locations') level //Fired emp.works_for.locations[0] > change... //Fired emp.works_for.locations[0].zip > change...
//Listen in @ emp.works_for level emp.get('works_for').on('change:locations[0].zip', function () { console.log("Fired emp.works_for > change:locations[0].zip..."); }); emp.get('works_for').on('change:locations[0]', function () { console.log("Fired emp.works_for > change:locations[0]..."); }); emp.get('works_for').on('change:controls[0].locations[0].zip', function () { console.log("Fired emp.works_for > change:controls[0].locations[0].zip..."); }); emp.get('works_for').on('change:controls[0].locations[0]', function () { console.log("Fired emp.works_for > change:controls[0].locations[0]..."); }); emp.get('works_for').on('change:locations[*]', function () { console.log("Fired emp.works_for > change:locations[*]..."); }); emp.get('works_for').on('change:controls[*].locations[*]', function () { console.log("Fired emp.works_for > change:controls[*].locations[*]..."); }); emp.get('works_for').on('change:controls[*].locations[*].zip', function () { console.log("Fired emp.works_for > change:works_for.controls[*].locations[*].zip..."); }); emp.get('works_for').on('nested-change', function () { if (arguments[0] == "controls[0].locations[0]") console.log("Fired emp.works_for > nested-change:controls[0].locations[0]..."); if (arguments[0] == "locations[0]") console.log("Fired emp.works_for > nested-change:locations[0]..."); }); //Console log //@ emp.get('works_for') level //Fired emp.works_for > change:controls[0].locations[0]... //Fired emp.works_for > change:controls[0].locations[0].zip... //Fired emp.works_for > change:locations[0]... //Fired emp.works_for > change:locations[0].zip... //Fired emp.works_for > nested-change:controls[0].locations[0]... //Fired emp.works_for > nested-change:locations[0]... //Fired emp.works_for > change:locations[*]... //Fired emp.works_for > change:controls[*].locations[*]... //Fired emp.works_for > change:works_for.controls[*].locations[*].zip...
//Listen in @ emp level emp.on('change:works_for.locations[0].zip', function () { console.log("Fired emp > change:works_for.locations[0].zip..."); }); emp.on('change:works_for.locations[0]', function () { console.log("Fired emp > change:works_for.locations[0]..."); }); emp.on('change:works_for.controls[0].locations[0].zip', function () { console.log("Fired emp > change:works_for.controls[0].locations[0].zip..."); }); emp.on('change:works_for.controls[0].locations[0]', function () { console.log("Fired emp > change:works_for.controls[0].locations[0]..."); }); emp.on('nested-change', function () { if (arguments[0] == "works_for.controls[0].locations[0]") console.log("Fired emp > nested-change:works_for.controls[0].locations[0]..."); if (arguments[0] == "works_for.locations[0]") console.log("Fired emp > nested-change:works_for.locations[0]..."); }); emp.on('change:works_for.locations[*]', function () { console.log("Fired emp > change:works_for.locations[*]..."); }); emp.on('change:works_for.controls[*].locations[*]', function () { console.log("Fired emp > change:works_for.controls[*].locations[*]..."); }); emp.on('change:works_for.controls[*].locations[*].zip', function () { console.log("Fired emp > change:works_for.controls[*].locations[*].zip..."); }); //@ emp level //Fired emp > change:works_for.controls[0].locations[0]... //Fired emp > change:works_for.controls[0].locations[0].zip... //Fired emp > change:works_for.locations[0]... //Fired emp > change:works_for.locations[0].zip... //Fired emp > nested-change:works_for.controls[0].locations[0]... //Fired emp > nested-change:works_for.locations[0]... //Fired emp > change:works_for.locations[*]... //Fired emp > change:works_for.controls[*].locations[*].zip... //Fired emp > change:works_for.controls[*].locations[*]...
emp.on('add:dependents', function () { console.log("Fired emp > add:dependents..."); }); emp.on('remove:dependents', function () { console.log("Fired emp > remove:dependents..."); }); emp.on('reset:dependents', function () { console.log("Fired emp > reset:dependents..."); }); emp.get('dependents').on('add', function () { console.log("Fired emp.dependents add..."); }); emp.get('dependents').on('remove', function () { console.log("Fired emp.dependents remove..."); }); emp.get('dependents').on('reset', function () { console.log("Fired emp.dependents reset..."); }); emp.get("dependents").add(child2); emp.get("dependents").remove([child1]); emp.get("dependents").reset(); //Console log //Fired emp.dependents add... //Fired Fired emp.dependents remove... //Fired emp.dependents reset... //Fired emp > add:dependents... //Fired emp > remove:dependents... //Fired emp > reset:dependents...