Git Product home page Git Product logo

backbone-relational's People

Contributors

andris-silis avatar antonyhell avatar bpatram avatar clemens-panda avatar codazzo avatar douwem avatar dvv avatar emilisto avatar idanz avatar jayd3e avatar jeremymlane avatar jrreed avatar nikolas avatar paulmillr avatar pauluithol avatar pbuyle avatar peterjosling avatar pgherveou avatar philfreo avatar rall avatar reiz avatar richardasymmetric avatar ricky-wong avatar rusalex avatar rweng avatar singingwolfboy avatar thomasst avatar tom-pryor avatar ulmus avatar wcpetersen 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

backbone-relational's Issues

Backbone-relational with django-tastypie and non-full relations

I'm using backbone-relational together with django-tastypie (as you seem to do yourself by the look of your backbone-tastypie project). I seem to get an error when using tastypie relations that are not defined as full, ie sent as resource Uri strings only. The related models in backbone-relational do not get instantiated. When sending full JSON representation of my related objects, they get instantiated ok.

I use this line from your backbone-tastypie plugin:

Backbone.Model.prototype.idAttribute = "resourceUri" // I have camelCased the JSON in tastypie

I have set up my model as follows

UserProfileModel = Backbone.RelationalModel.extend({});

UserModel = Backbone.RelationalModel.extend({
    relations: [
        {
            type: "HasOne",
            key: "profile",
            relatedModel: "UserProfileModel"
        }
    ]
});

I then do this:

var user = new UserModel({resourceUri: currentUserResourceUri});
user.fetch({
    success:function(){
        var userProfile = user.get("profile");
        userProfile.fetch() // <= this is where it debugs, as user.attributes.profile is undefined 
    }
});

When I send the following JSON from the server on user.fetch(), it works:

{
    resourceUri: "/api/v1/user/1/",
    firstName: "Jens",
    lastName: "Alm",
    profile: {
        resourceUri: "/api/v1/profile/1/",
        title: "Admin"
    }
}

When I send the following it fails, though, from how I understand it, it should work:

{
    resourceUri: "/api/v1/user/1/",
    firstName: "Jens",
    lastName: "Alm",
    profile: "/api/v1/profile/1/"
}

Am I misunderstanding how backbone-relational works? Shouldn't this also create a model that I can later fetch to fill with actual attributes?

Create models in Collection

follow your example I use collection to create the relational model:

House = Backbone.RelationalModel.extend({
        relations: [
            {
                type: Backbone.HasMany,
                key: 'occupants',
                relatedModel: 'Person',
                includeInJSON: false,
                reverseRelation: {
                    key: 'livesIn'
                }
            }
        ]
    });

    Houses = Backbone.Collection.extend({
        model: House
    });
    Person = Backbone.RelationalModel.extend();
    Persons = Backbone.Collection.extend({
        model: Person
    });
    var houses = new Houses([{
            "id": "1",
            "name": "house-1",
            "occupants": ["10"]
        },{
            "id": "2",
            "name": "house-2",
            "occupants": ["20"]
        }
    ]);
    var persons = new Persons([{
            "id": "10",
            "name": "person-1"
        },{
            "id": "20",
            "name": "person-2"
        }
    ]);

the model and collection is created successful, but in firebug backbone throw an error:

list[i] is undefined
 list[i].apply(this, Array.prototype.slice.call(arguments, 1)); 

at the backbone.js line 97

If I create persons collection before the houses collection, therer is no error, bug the relationship between house and person is not create successful.

Heterogeneous related models?

I'm experimenting with using backbone-relational to replace some home-grown code that does basically the same thing, but not as elegantly.

Its going well, but I'm looking for advice on how to handle related models that might be heterogeneous.

For example, I might have an "Article" model that has a property that's an array of assets. Those assets might be "Text", "Audio", "Image", or "Video" each with individual API urls for the server side "/v1/text/", "/v1/audio"...

Any tips on how best to deal with situations like this?

Getting back empty array when I need an empty Backbone.Collection

I have a regular Backbone.Collection called Contests and a Backbone.RelationalModel called Contest which has the following relation to a collection of Submission's:

Contest  = Backbone.RelationalModel.extend
  relations: [{
    type: Backbone.HasMany
    key: '_submissions'
    relatedModel: 'Submission'
    collectionType: 'SubmissionCollection'
    includeInJSON: '_id'
    reverseRelation:
       key: 'contest'
      includeInJSON: '_id'
    }]
  urlRoot: '/api/contests'
  idAttribute: '_id'

in my app, when I do something like contests = new Contests(); contests.fetch() then I get back a collection of Contest's which is fine. However, when I inspect the Contest models within this collection, the _submissions attribute is an empty array, instead of a SubmissionCollection, (i.e. an instance of a BackboneCollection). Therefore I cannot bind events etc to the collection of Submissions.

I know that Backbone-relational is doing what its supposed to do because when I type in my console x = new Contest() and I inspect this model, the '_submissions' attribute is an empty SubmissionCollection.

So what I want to know is how can I get the same behaviour when I load my Contests as part of a Contest collection.

Incredible amount of toJSON's being called while rendering

Hello,

I was wondering if you had any suggestions for me. I recently switched to using backbone-relational from my own relational logic, and my application is now incredibly slow. It seems as though the models are recursively calling toJSON on themselves or something. See these screenshots from the Webkit profiling (top down):

https://docs.google.com/leaf?id=0B1uQWmnCzsgiMjYwZWNmNDAtYmIxNy00ZjRmLTk3M2UtOGNjM2U2MjJiNTQ1&hl=en_US
https://docs.google.com/leaf?id=0B1uQWmnCzsgiYWE0N2ViNzUtNTc2ZS00NmQyLTk0ZTEtYmEwYmRkNDAxOGFi&hl=en_US

It gets to the toJSON method of Backbone.js and Backbone-relational then goes for a long time.

One of my models is here:
https://github.com/Concert/Concert/blob/loggedinpage/concertapp/static/js/lib/models/Event.js

Here is the master view that is rendering all of the subviews:
https://github.com/Concert/Concert/blob/loggedinpage/concertapp/static/js/lib/panels/EventsPanel.js#L254-L268

And here is the base subview (Widget) class, and where toJSON is being called:
https://github.com/Concert/Concert/blob/loggedinpage/concertapp/static/js/lib/widgets/Widget.js#L48-L73

There don't appear to be an incredible amount of widgets being instantiated, so i'm not sure why toJSON is being called so much.

Does anything strike you as odd immediately? Am I missing a flag in my relations or anything?

Does automatic model creation update existing nested models?

I'm deflating models sending them over websockets and inflating them on the other side. The automatic model creation is GREAT for this.

My question is about how it works exactly though. When I set unchanged data these models still throw all sorts of events.

if model = //some backbone model instance

model = model.set(model.toJSON) shouldn't throw any events normally since nothing has changed.

I was hoping nested model creation would work the same way but it seems that if you set attributes on the parent model it creates all new nested models and clobbers the old ones.

Let me know if there is a way to do this, or if I just completely don't understand something. It would be really really useful if model creation was smart enough to update existing nested models.

Thanks for the great work!

npm - republish?

Noticing that the npm version of backbone-rel is 0.3.0 whereas the current tag / package.json says 0.4.0 - could you please republish, so it can be built with ender more easily...

Collections cannot be declared in CoffeeScript

I have a Backbone app using Backbone relational. My relations do not work at all if my Collections are declared in CoffeeScript - if I use JavaScript, they work fine.

Models and collections in CoffeeScript:

class App.Models.Person extends Backbone.RelationalModel
  urlRoot: '/api/people'
  relations: [{
    type: Backbone.HasOne,
    key: 'worksFor',
    relatedModel: 'App.Models.Company',
    collectionType: 'App.Collections.Companies',
    reverseRelation: {
      key: 'worksAt'
      includeInJSON: 'id'
    }
  }]

class App.Models.Company extends Backbone.RelationalModel
  urlRoot: '/api/companies'

class App.Collections.People extends Backbone.Collections
  model: App.Models.Person
  url: '/api/people'

class App.Collections.Companies extends Backbone.Collections
  model: App.Models.Company
  url: '/api/companies'

This does not work. But if I delete the collections and replace them with their JS equivalents, everything works:

                App.Collections.People = Backbone.Collection.extend({
                    model: App.Models.Person,
                    url: '/api/people/'
                });

                App.Collections.Companies = Backbone.Collection.extend({
                    model: App.Models.Company,
                    url: '/api/companies/'
                });

The error given is:

Uncaught TypeError: Cannot read property 'prototype' of undefined

On line 1113 of backbone-relational.js.

Basically, the model attribute doesn't seem to be getting set (it's the "this.model" that throws the error) in the CoffeeScript version - but the rest of backbone works fine, which leads me to think this is a backbone-relational issue.

Can provide more of the code if that helps. :)

Initialization order change when id's are present

Problem

When _id is present in the objects, the comparator will trigger before the object is initialized. Because of this, the comparator can't depend on any property on the model that is set at initialization stage, which is frustrating.

Expected Log (commenting out idAttribute will give this result)

first mother
child initialized
child initialized
comparator called
comparator called
mother initialized
second mother
child initialized
child initialized
comparator called
comparator called
mother initialized

Log (when id's are present)

first mother
child initialized
child initialized
comparator called
comparator called
mother initialized
second mother
child initialized
comparator called # comparator called before child is initialized
comparator called # comparator called before child is initialized
child initialized
mother initialized

Code

window.child = Backbone.RelationalModel.extend({
  url: "/null",
  idAttribute: "_id", // commenting out this line toggles the between the two outputs show above
  initialize: function() {
    return console.log("child initialized");
  }
});
window.childrenList = Backbone.Collection.extend({
  model: child,
  url: "/null",
  comparator: function(model) {
    console.log("comparator called");
    return 1;
  }
});
window.mother = Backbone.RelationalModel.extend({
  relations: [
    {
      type: Backbone.HasMany,
      key: "children",
      relatedModel: "child",
      collectionType: "childrenList",
      reverseRelation: {
        key: "mother"
      },
      includeInJSON: true
    }
  ],
  initialize: function() {
    return console.log("mother initialized");
  }
});
console.log("first mother");
window.myMother = new window.mother({
  name: "Jane",
  children: [
    {
      name: "John",
      _id: "j1"
    }, {
      name: "James",
      _id: "j2"
    }
  ]
});
console.log("second mother");
window.myMother = new window.mother({
  name: "Wade",
  children: [
    {
      name: "Jack",
      _id: "j3"
    }, {
      name: "Joe",
      _id: "j4"
    }
  ]
});

Confusion about fetching related models

I have a model called Client and a model called Jobs. Each Client can have many Jobs, but each Job can only belong to one Client. So Client HasMany Jobs and Jobs HasOne Client.

Anyway, I have a Job Collection that fetches data from my server. The server returns a JSON object like so:

[ {
id: 5,
due_date: 01-05-2012,
Client:
{
id: 100,
name: James,
phone: 123-123-1234
}
}]

However, backbone-relational doesn't seem to be able to parse the relation. I get an error when I call jobList.at(0).get('Client').get('name);

Here's my code:

Client = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'Jobs',
relatedModel: 'Job',
collectionType: 'JobCollection',
reverseRelation: {
key: 'Client',
includeInJSON: 'id'
}
}]
});

Job = Backbone.RelationalModel.extend({
});

JobCollection = Backbone.Collection.extend({
url: baseURL+'app/jobs/',
model: Job
});

var jobsList = new JobCollection();
jobsList.fetch({
data: { user_id: 1},
success: function(model, resp){
alert(jobsList.at(0).get('Client').get('name'));
}
});

Brunch/stich.js

Hi,

I tried to get Backbone.Relational to run with Stich.js/Brunch, but it doesn't work. Looks like a scoping problem to me.
Has anyone got this running with brunch?

Cheers,
Seb

'Change' event triggered before relations established on save()

I seem to have run into what looks like a race condition.

In Backbone.RelationalModel.set(), the call to Backbone.Model.prototype.set.apply() will trigger a change() event before Backbone.RelationalModel.initializeRelations() is set up. This will cause calls to render() in subscriber views (and anything else listening to a collection to which a model belongs) to fail. The subscribers are expecting Backbone.Model objects, not raw objects.

This is most prominent when calling save() and receiving a JSON object as the response to a PUT.

PCView.PCView.render spiral.js:1726 Backbone.Events.trigger backbone.js:112 _.extend._onModelEvent backbone.js:584 Backbone.Events.trigger backbone.js:117 _.extend.change backbone.js:325 _.extend.set backbone.js:204 Backbone.RelationalModel.Backbone.Model.extend.set backbone-relational.js:761 _.extend.save.success

As you can see, the event is triggered when set() passes a raw JSON object, triggering (eventually) a render() that looks into the object before it's been completely set and initialized.

Reset event for collection of related models ?

Hello,

Unless I am mistaken, it looks as if a reset of a "top-level" model that has a collection of related objects will trigger the add event for every related model in the collection.

Is there a way to have the equivalent of a reset for the collection of related models, ie: not trigger the add event for every model and trigger a single 'reset' event on the collection ?

Thanks

Ability to disable backwards pointing keys in includeInJSON relations

var Child = Backbone.RelationalModel.extend({})
var Mother = Backbone.RelationalModel.extend({ relations: [ {type: Backbone.HasMany, key: "children", relatedModel: "Child", reverseRelation: {key: "mother", includeInJSON: false}, includeInJSON: true }] })
var mo = new Mother({ "name" : "mum", "children" : [ { name:"john" }, { name:"joe" }]})

mo.toJSON() // will return something like this: { name: "mum", children: [ {name:"john", mother: undefined }, โ€ฆ ] }

It would be nice if one could get rid of the mother key, which points back up into the json tree. In many cases they aren't needed since the mother data is already present in the json object (only further up) and their relation is signified in the json structure itself.

However, e.g. in Rails + Mongoid this will crash things (though one can delete the keys server-side, but that is a hassle).

Let me know what you think?

Collection observers not removed

Backbone.Relation adds event listeners for 'add' and 'remove' events on the underlying collections. These event handlers are not removed once the relation is destroyed, causing memory leaks and (worse) rogue many-to-many relations to show up.

https://github.com/PaulUithol/Backbone-relational/blob/master/backbone-relational.js#L293
https://github.com/PaulUithol/Backbone-relational/blob/master/backbone-relational.js#L301
https://github.com/PaulUithol/Backbone-relational/blob/master/backbone-relational.js#L308

How to stop automatic model addition to a collection?

I have two models: User and Item.

class App.Models.User extends Backbone.RelationalModel
  relations: [
      type: Backbone.HasMany,
      key: 'items',
      relatedModel: 'App.Models.Item',
      collectionType: 'App.Collections.Items',
      reverseRelation:
        key: 'user_id',
        includeInJSON: 'id'
    ]

class App.Models.Item extends Backbone.RelationalModel
  url: ->
    instance = if @isNew() and @get('id') then '/' + @get('id') else ''
    '/users/' + @get('user_id').get('id') + '/' + 'items' + instance

What I expect is that if I create model with some attributes just with new -> it will not appear in the collection. Also it should not appear after invalid save or when validation failed. Model should be added to a collection only after it was successfully saved on the server.

users = new App.Collections.Users
users.fetch()

item = new App.Models.Item
  user_id: 1
  text: 'Text'

item.save # when error/invalid - should not be saved in the 

users.get('1').get('items') # should not have any items here

Thank you for your help!

catchall event handler

Hi!

Since the hierarchy of complex models can be very extended I'd like to have kinda catchall event handler which would respond to change/add/remove events on any model involved in the hierachy. This would allow to sync client/server state seamlessly and in realtime manner, with little-or-zero effort.

What do you think?

TIA,
--Vladimir

Uniqueness and 2-way messaging for Many-to-Many

Hello, Paul!

Considering your example with Person-Job-Company Many-to-Many relation, I can implement uniqueness of jobs and 2-way messaging between Person and Company objects linked by Job object only with a bunch of code during Job object initialization. Is it possible to do these tasks in a cleaner way?

May be it was just my case, but Company object added to 'jobs' collection of Person object triggers not an 'update:company' event but 'update:person' when being changed. And vice versa.

Person = Backbone.RelationalModel.extend({
    initialize: function(){
        this.bind('update:jobs', function(company) {
            alert(this.get('name') + ' says: "My company has been updated to ' + company.get('name') + '"');
        }, this);
    },
    relations: [
        {
            type: 'HasMany',
            key: 'jobs',
            relatedModel: 'Job',
            reverseRelation: {
                key: 'person'
            }
        }
    ],
    getCompanies: function(){
        return this.get('jobs').pluck('company');
    }
});

Job = Backbone.RelationalModel.extend({
    initialize: function(){
        this.bind('add', function(job, jobs){
            var person = job.get('person');
            var company = job.get('company');

            if(person && company){       
                // uniqueness of jobs
                if( ! this.isUnique(jobs, person, company)){
                    this.del(job, person);
                } else {
                    // 2-way messaging
                    person.bind('change', function(){
                        company.trigger('update:employees', person);
                    }, company);
                    company.bind('change', function(){
                        person.trigger('update:jobs', company);
                    }, person);
                }
            }
        }, this);
    },
    del: function(job, person){
        setTimeout(function(){
            person.get('jobs').remove([job]);
        }, 100);
    },
    isUnique: function(jobs, person, company){
        return _(jobs.models).chain().select(function(job){
            return job.get('company') === company && job.get('person') === person;
        }).size().value() == 1;
    }
});

Company = Backbone.RelationalModel.extend({
    initialize: function(){
        this.bind('update:employees', function(person) {
            alert(this.get('name') + ' says: "My employee has been updated to ' + person.get('name') + '"');
        }, this);
    },
    relations: [
        {
            type: 'HasMany',
            key: 'employees',
            relatedModel: 'Job',
            reverseRelation: {
                key: 'company'
            }
        }
    ],
    getPersons: function(){
        return this.get('employees').pluck('person');
    }
});

ibm = new Company({
    id: 1, 
    name: 'IBM'
});
bmw = new Company({
    id: 2,
    name: 'BMW'
});
pepsi = new Company({
    id: 3,
    name: 'Pepsi'
});

me = new Person({
    id: 1, 
    name:'Old Me'
});

me.get('jobs').add([{company: ibm}, {company: bmw}, {company: pepsi}, {company: pepsi}, {company: pepsi}]);
ibm.get('employees').add([{person: me}]);
bmw.get('employees').add([{person: me}]);
pepsi.get('employees').add([{person: me}]);

me.set({name: 'New Me'});
pepsi.set({name: 'New Pepsi'});

This is a link to jsFiddle with the code http://jsfiddle.net/avrelian/Z4a65/

add:relation event fires on collection initialize

I am binding to the add:relation event on a HasMany relation on my relational model to update the view when a new model is added to that relations collection.

Only problem is that it gets called once per item when the collection is reset. How can I silence the add event from being emitted when initialising the collection, but get it to fire for one-off additions?

Support Asynchronous Module Loading (require.js)

This is becoming more and more an accepted practice. jQuery 1.7 and even jQuery Mobile (in a branch) are supporting Asynchronous Module Loading. I have modified a copy of backbone-relational.js to employ a method which @jrburke implements here: https://github.com/jrburke/backbone/tree/optamd3. For this project:

(function(root, factory) {
  // Set up appropriately for the environment.
  if (typeof exports !== 'undefined') {
    // Node/CommonJS
    factory(root, exports, require('underscore'), require('backbone'));
  } else if (typeof define === 'function' && define.amd) {
    // AMD
    define('backbone-relational', ['underscore', 'backbone', 'exports'], function(_, Backbone, exports) {
      factory(root, exports, _, Backbone);
    });
  } else {
    // Browser globals
    factory(root, {}, root._, root.Backbone);
  }
}(this, function(root, exports, _, Backbone) {
  // Backbone.Relational extensions to Backbone
  // NOTE: the few lines assigning the exports, _, and Backbone are no longer needed
}));

I have not tested this in Node, nor without Require.js.

serializing

Hello Paul
What do you think about adding a toObject method or an optional argument to the toJSON method
to avoid stripping attribute marked with an includeInJSON = false when needed

This could be useful when you pass a data object to your template engine.

Error when destroying a simple model generated by Coffeescript

I have a situation which has to be simple to solve, I would guess. Surely people are using Backbone Relational with CoffeeScript out there...

Here's a model I have:

class MyCompany.Models.Establishment extends Backbone.RelationalModel
  defaults:
    name: null

class MyCompany.Collections.EstablishmentsCollection extends Backbone.Collection
  model: MyCompany.Models.Establishment
  url: '/establishments'

I haven't even added any relationships yet, just extended RelationalModel. Now through the console when I issue a destroy on an instance of the model it successfully destroys the model on the server, but when complete it fails with the trace:

Uncaught TypeError: Object #<Establishment> has no method 'getCollection'
    _.extend.unregister
    Backbone.Events.trigger
    Backbone.RelationalModel.Backbone.Model.extend.trigger
    _.extend.destroy.options.success
    jQuery.extend._Deferred.deferred.resolveWith
    done
    jQuery.ajaxTransport.send.callback

It's dying in line 235 of backbone-relational.js 0.4.0 because "this" is the model, I guess, instead of whatever it's supposed to be, and the model doesn't have a method "getCollection".

Any ideas what I'm doing wrong, or is this a bug? For reference, here's the Javascript coffee generates:

(function() {
  var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
    for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
    function ctor() { this.constructor = child; }
    ctor.prototype = parent.prototype;
    child.prototype = new ctor;
    child.__super__ = parent.prototype;
    return child;
  };
  MyCompany.Models.Establishment = (function() {
    __extends(Establishment, Backbone.RelationalModel);
    function Establishment() {
      Establishment.__super__.constructor.apply(this, arguments);
    }
    Establishment.prototype.defaults = {
      name: null
    };
    return Establishment;
  })();
  MyCompany.Collections.EstablishmentsCollection = (function() {
    __extends(EstablishmentsCollection, Backbone.Collection);
    function EstablishmentsCollection() {
      EstablishmentsCollection.__super__.constructor.apply(this, arguments);
    }
    EstablishmentsCollection.prototype.model = MyCompany.Models.Establishment;
    EstablishmentsCollection.prototype.url = '/establishments';
    return EstablishmentsCollection;
  })();
}).call(this);

Many to many for single model without linking model

I'm mapping out a Twitter like social graph with non-mutual followers/following relations. The use of a linking model makes sense in the Person/Job/Company example, but only adds overhead in the case of many-to-many for the same model on both sides. I find myself wanting to write a relation like

User = Backbone.RelationalModel.extend({
    relations: [{
        type: 'HasMany',
        key: 'following',
        relatedModel: 'User',
        reverseRelation: {
            type: 'HasMany',
            key: 'followers'
        }
    }]
});

Unless there is reason for this, I'd much like this feature! (or might even implement it myself)

Are three levels of models supported?

Thank you for a nice library!
I seem to have a problem getting three levels of nested models to update after storing them on the server, is this supported?

My structure of models is: Game -> Card -> Action
when saving the model tree the id of new cards get updated in the Backbone model, but not for new actions.
(The JSON response from the server adds ids for all models that are new.)

Code example:

Game = Backbone.RelationalModel.extend({
  relations: [{
    type: Backbone.HasMany,
    key: 'cards',
    relatedModel: 'Card',
    reverseRelation: {
      type: Backbone.HasOne,
      includeInJSON: false,
      key: 'game'
    }
  }],
  url: '/bugs/json.json'
});

Card = Backbone.RelationalModel.extend({
  relations: [{
    type: Backbone.HasMany,
    key: 'actions',
    relatedModel: 'Action',
    reverseRelation: {
      type: Backbone.HasOne,
      includeInJSON: false,
      key: 'card'
    }
  }]
});

Action = Backbone.RelationalModel.extend({});

$(function() {
  game = new Game ({
    "name": "First Game",
    "id": 4,
    "cards": [
      {
        "title": "Test scenario"
      }, {
        "title": "Other scenario",
        "id": 105,
        "actions": [
          {
            "name": "cut",
            "id": 41
          }, {
            "name": "call"
          }
        ]
      }
    ]
  });
  // After saving Card objects are updated with id from server, but Action objects are not
  game.save();
});

relation should be able to get content from model attribute different then 'key'

I'm having model like

var SomeModel = Backbone.RelationalModel.extend({
    relations: [
        {
            type: "HasOne",
            key: "item",
            relatedModel: 'ItemModel'
        }
    ]
});

Then I init this model with following JSON:

{
  id: 128,
  itemId: 356
}

So relation key is different from an attribute where ID is stored.
But I still want to keep relations - so when I change itemId - item will change. And vice versa - when I change item.id I'd like to update itemId attribute
What's the best way to get this behavior? I'm thinking on creating new Relation type that will have keyContentsAttribute option that will specify which model attribute to use. Is there better approach?

How to prevent caching of related models?

Raised by @takinbo in email:

Thanks for your great library - Backbone-relational. I'm using it quite extensively in a project and I came across is very interesting scenario that seems to be a bug.

I had some models which had other related models rendered on a webpage. If I make any changes to the models (from the database) and I refresh the collection, this gets reflected on the interface however, if I make the changes to a related model (again from the backend) and refresh the collection, this related model doesn't reflect the new changes. I traced this to https://github.com/PaulUithol/Backbone-relational/blob/master/backbone-relational.js#L484 (line 484). My hack was to avoid the lookup in the store and create the model each time.

Are there any better ways to solve this problem, is my solution going to have any performance hits on my application?

Thanks.

confused about documentation: direction of includeInJSON

Probably best to describe my confusion by illustrating the structure I'm trying to create. I have a Venue model that has zero or more nested Address models (1:n relationship). The backend store is MongoDB, which can have embedded objects. I'd like to store it in this format:

{
    id: 12345,
    label: 'OPUS Cafe Bistro',
    addresses: [
        {
            type: 'mailing',
            address1: '#52 - 650 Duncan Ave',
            city: 'Penticton, BC'
        },
        {
            type: 'main',
            address1: '#106 - 1475 Fairview Rd',
            city: 'Penticton, BC'
        }
    ]
}

(Please ignore the ugly data structures; I've adjusted it for brevity.) Now I believe I set up the relationship between Venue and Address thusly:

var Venue = Backbone.RelationalModel.extend({
    relations: [
        {
            type: Backbone.HasMany,
            key: 'addresses',
            relatedModel: 'Address',
            includeInJSON: false,
            collectionType: 'Addresses',
            reverseRelation: {
                key: 'venue'
            }
        }
});

If I understand correctly, I set includeInJSON to false in order to prevent the Venue from being serialised into the venue key in Address, but under reverseRelation I leave includeInJSON blank in order to have the full Address (without a venue property) serialised as an array in the addresses property of the Venue โ€“ as per my hoped-for example. Correct?

By the same token, I'm trying to understand the same concept in relation to a join-style relationship. Consider that Venue now has an organisationID field:

/* venue in JSON format  */
{
    id: 12345,
    organisationID: 336,
    label: 'OPUS Cafe Bistro',
    addresses: []
}

/* and now for the organisation */
{
    id: 336,
    label: 'OPUS Entertainment Group'
}

Using the examples in the documentation, which seem to prefer establishing the Backbone.HasMany relationship, I think that I'd have to set up Organisation thus:

var Organisation = Backbone.RelationalModel.extend({
    relations: [
        {
            type: Backbone:HasMany,
            key: 'venues',
            relatedModel: 'Venue',
            includeInJSON: Backbone.Model.prototype.idAttribute,
            collectionType: 'Venues',
            reverseRelation: {
                key: 'organisationID',
                includeInJSON: false
            }
        }
    ]
});

... which should serialise into the above example, right? (I.e., Venue grabs Organisation's id and inserts it into organisationID, and Organisation doesn't store any list of Venues)

Thanks in advance for any help โ€“ looking forward to using this handy library, after clawing my eyeballs out trying to write my own relational glue for Backbone.js :-)

fetchRelated does not fire "success" handler if it finds models in the Store.

Disclaimer: I haven't distilled this down to a reproducible test case, but from what I'm seeing...

I'm using Backbone-Relational both on the Client and on the Server using Node.js.

Our node.js app interacts with a Python/Tastypie API, and many of the responses from Tastypie are not "full" responses, but are simply uris to other models.

On one of them (a user model), one of the related properties returned from the API is a uri. So, in Node, I fire off a "fetchRelated" with a success and error handler - both of which fire off a passed in callback to continue the request processing.

This works fine if Backbone-Relational doesn't find the model in the Store - the fetch is fired, and the callback is executed.

But, if Backbone-Relational DOES find the model(s) in the Store (ie. toFetch.length is 0), then the callback is never fired.

So, I'm not sure if the right things is for the success handler to be called, or if I should set up by app differently by binding to a different event to continue processing.

Is there an event that I can bind to that will work, whether Backbone-Rel has to issue actual fetches for the model(s), or if it finds them from the store?

Reverse Relations not working as expected

In the example code, if I do the following:

  paul = new Person({
        id: 'person-1',
        name: 'Paul',
        user: pUser
    });
console.log("Users person",pUser.get('person'));

Outputs "undefined"

However, if I replace the User class with this code:

       relations: [
           { // Create a (recursive) one-to-one relationship
               type: Backbone.HasOne,
               key: 'nothing',
               relatedModel: 'User',
           }
       ]
});

And try the above log, everything seems to work as expected.

example not working ?

Hello, just trying to get started with backbone-relational.

Following your example, this bit works fine :
paul = new Person({
id: 'person-1',
name: 'Paul',
user: { id: 'user-1', login: 'dude', email: '[email protected]' }
});

// A User object is automatically created from the JSON; so 'login' returns 'dude'.
paul.get('user').get('login');

ourHouse = new House({
id: 'house-1',
location: 'in the middle of the street',
occupants: ['person-1', 'person-2', 'person-5']
});

The problem I have is that when I try :

ourHouse.occupants, I get undefined ?

HasOne relationship from string

Hello Paul

when a hasMany relationship is initialized from an array of id it is turned into a collection of model. Wouldn't be useful to have the same behavior for hasOne relationship ?

ourHouse = new House({
    id: 'house-1',
    location: 'in the middle of the street',
    occupants: ['person-1', 'person-2', 'person-5']
    owner: 'person-1'    
});

// 'ourHouse.occupants' is turned into a Backbone.Collection of Persons.
ourHouse.get('occupants').at(0); // === paul
// 'ourHouse.owner' is turned into a Person.
ourHouse.get('owner'); // === paul

in the current version setting a string on a hasone relationship set the value to undefined, I guess, this could be updated in the createModel method what do u think ?

        createModel: function( item ) {
            if ( this.options.createModels && typeof( item ) === 'object' ) {
                return new this.relatedModel( item );
            } else { // set model }
        },

Endless recursion on save with circular references

I have two models that both reference each other:

class App.Models.B extends Backbone.RelationalModel
  relation: [{
    type: Backbone.HasOne,
    key: 'a',
    relatedModel: 'A',
    reverseRelation: {
      key: 'bs',
      includeInJSON: 'id'
    }
  }]

class App.Models.A extends Backbone.RelationalModel
  relation: [{
    type: Backbone.HasMany,
    key: 'cs',
    relatedModel: 'C',
    collectionType: 'CsCollection',
    reverseRelation: {
      key: 'a',
      includeInJSON: 'id'
    }
  }, {
      type: Backbone.HasMany,
      key: 'bs',
      relatedModel: 'B',
      collectionType: 'BsCollection',
      reverseRelation: {
        key: 'a',
        inclideInJSON: 'id'
      }
    }
  ]

Unfortunately, when saving an instance of B, Backbone-relational aborts with a stack overflow error after unlocking the event queue:

Uncaught RangeError: Maximum call stack size exceeded
...
_.extend._onModelEvent() at backbone.js:609
d() at (internal script):1452
Backbone.Events.trigger() at backbone.js:115
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:821
_.extend.add() at backbone-relational.js:82
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:820
_.extend._onModelEvent() at backbone.js:609
Backbone.Events.trigger() at backbone.js:115
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:821
_.extend.add() at backbone-relational.js:82
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:820
_.extend._onModelEvent() at backbone.js:609
Backbone.Events.trigger() at backbone.js:115
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:821
_.extend.add() at backbone-relational.js:82
[...]
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:820
_.extend._onModelEvent() at backbone.js:609
Backbone.Events.trigger() at backbone.js:115
Backbone.RelationalModel.Backbone.Model.extend.trigger() at backbone-relational.js:821
_.extend.process() at backbone-relational.js:88
_.extend.unblock() at backbone-relational.js:99
Backbone.RelationalModel.Backbone.Model.extend.set() at backbone-relational.js:997
_.extend.save() at backbone.js:265
App.Views.Bs.BView.BView.save() at tb_view.js:55
d() at (internal script):1452
jQuery.event.dispatch() at jquery.js:3320
jQuery.event.add.elemData.handle.eventHandle() at jquery.js:2926

Using a JS debugger I figured out that Backbone-relational is firing an endless chain of 'change' events between B and BsCollection for the attribute, which has been updated during the save.

Any ideas what I may be doing wrong?

fetchRelated() confusion

Hi,

Great plugin! It makes plenty of stuff easier, but I'm having trouble with fetchRelated(). I keep getting an empty array back, no matter what I do. Shouldn't it create an http request? I've tried on my own app, and also using the exact code from your example in a separate test, and the same thing happens. I'm afraid I'm going to need some guidance with this one...

Oh, and the regular fetch() works just fine, so I'm using that for now.

Thanks!

Cloning a backbone model with relations does not perform deep cloning

When cloning a simple backbone object with a HasOne relationalship (by calling model.clone() ) the relation is not cloned. There's only a pointer to the original object (same cid) so it's not deep-copied.

Also i noticed that cloning a model with a HasMany relationship does not copy the models (array) at all.

I think the clone() method needs to be supported by backbone.relational.

None of the test cases (in test/test.js) handle clone() - missing in test coverage.

Let me know your feedback
Thanks lot and well done for the library btw!

How to handle the case when a reference is broken

Hi !

I have a Message model, which has two HasOne relations to a User model.

Message = Backbone.RelationalModel.extend({
          urlRoot: 'views/Message/',
      defaults: {
                    text: undefined,
                    recipient: undefined, // Foreign key to User
                    sender: undefined, // Foreign key to User
      },
        relations: [{
            type: Backbone.HasOne,
            key: 'recipient',
            relatedModel: 'User',
            collectionType: 'User',
            includeInJSON: true
        }, {
            type: Backbone.HasOne,
            key: 'sender',
            relatedModel: 'User',
            collectionType: 'User',
            includeInJSON: true
        }],
});

The server sends to the frontend some updates with new messages received, and the 'sender' and 'recipients' attributes appear as usernames ... problem is that the frontend might not know this user. So, some Messsage objects are just created with broken relations to User.
For some complicated backend reasons, we have to deal with that, and the server cannot provide us more info.

Of course - when trying to serialize the message for rendering it, and in a few other cases - when the relation is broken, the page just crashes.

So my question : is there a generic way to handle a broken relation (as we'll face the same problem in other models as well) ? Any hook, any standard place were a default value can be set ?

Setting relation between a parent model and child collection

One thing that keeps tripping me up, is I'm expecting when I set up a reverseRelation that there's also a property on the collection created that references the parent model.

So, if you take this code:

Team = Backbone.RelationalModel.extend({});
TeamList = Backbone.Collection.extend({
    model: Team
});

Person = Backbone.RelationalModel.extend({
    relations: [
        {
            type: Backbone.HasMany // Use the type, or the string 'HasOne' or 'HasMany'.
            , key: 'teams'
            , relatedModel: 'Team'
            , collectionType: 'TeamList'
            , reverseRelation: {
                key: 'person'
            }
        }
    ]
});

What I'd like is for the TeamList collection to contain a reference to the Person model via a "person" property. So if I can something like:

var dan = new Person({name: "Dan"});

I could do:

console.log(dan.get("teams").person.get("name"));

Even if we added an addtional "collectionKey" so that you had to explicitly define a property on the collection level, it would be handy.

The problem I'm running into is I end up binding events at the collection level that need to be aware of the parent Model. So, what I end up doing is creating the relation manually.

The problem with binding it only to the models is that if I need to trigger off an event in the parent model if there are no models in the collection, there's no easy way to do that.

Experimenting

Hi I am experimenting on backbone relational, (looks great !)
I create the following in console

var Membership = Backbone.RelationalModel.extend({
    urlRoot: '/membership',
});

var User = Backbone.RelationalModel.extend({
    urlRoot: '/user',
    relations: [
        {
            type: Backbone.HasMany,
            key: 'memberships',
            relatedModel: 'Membership',
            collectionType: 'MembershipCollection',
            reverseRelation: {
                key: 'user'
            }
        }
    ]
});

var UserCollection = Backbone.Collection.extend({model:User});
var MembershipCollection = Backbone.Collection.extend({model:Membership});

// create a membership from a json hash
member = new Membership({
                "id": 4,
                "user": {
                    "id": 7,
                    "username": "paul",
                    "email": "[email protected]"
                }
            })

If I type member.get('user') it returns me the user object, I was expecting to receive a Model instance of it
Can you let me know what's wrong with this piece of code ?

Thks

Cleanup after saving new model

Hello Paul, one issue I ran into lately :
When you save a new model with nested relation, and get the model back from the server with an id set on both parent and children model, the original children are replaced with the one returned by the server but are not removed from the store.
Is there a way to avoid that, or automate the clean up ?
I use something like that to remove this models from the store after a save

    clean: function(Model) {
      return this.store.getCollection(Model).each(function(m) {
        if (m.isNew()) m.destroy();
      });

json parse error

As I defined a model and a collection like these:

    City = Backbone.RelationalModel.extend({
        url:'static/js/provinces.json',
        relations: [{
            type: Backbone.HasMany, 
            key: 'citys',
            relatedModel: 'City',
            includeInJSON: false,
            reverseRelation: {
                key: 'parent'
            }
        }]
    });
    Cities = Backbone.Collection.extend({
        model:City,
        url:'static/js/provinces.json'
    });

but when I use the fetch method of the collection, the server return a json data like this:

[
    {
        "id":100,
        "name": "city a",
        "condition":0
    }, {
        "id":101,
        "name": "city b",
        "condition":1,
        "citys":["100"]
    }
]

but this json will throw an error when the jquery parse the json data, because the '"citys":["100"]' is not a valid json format.

how to deal with this problem.

has many through

Is there a way to do many-to-many relationships with has-many-through accessors, as in Rails?

Memory/Processor Leak with many objects

Hi Paul!

I'm using backbone-relational in a fairly large single webpage app and I'm running into a few problems. In the app, I create and destroy large amounts of models and collections and there seem to bee some kind of leakage, possibly in backbone.relational. The problem may lie elsewhere, but I notice that the number of toJSON-calls made increases exponentially as the app works, soon measuring in the 100's of thousands for each ajax call. I haven't figured out where the problem lies yet, but perhaps you have a clue where I should look?

/Jens Alm

Problem with reverseRelation and setRelated()

I've been working on tracking down a very bizarre issue over the past day. I'm still trying to wrap my head around what's really happening, but I've at least tracked down where in the backbone-relational.js the code is going awry.

Since this bug is hard to explain, the easiest thing to do is just view the example: http://jsfiddle.net/cbemj/

Run the code and view the browsers debug console to see the output. In the output you'll see that the "roles" attribute is correctly updated inside the setUser() method, but after the each() loop runs, the roles for the first object set are lost.

Now I've spent a lot of time debugging this issue and it appears to be a problem with the reverse relationship between the objects. If you remove the reverseRelation to the ServiceDeskRole model in the ServiceDesk model, the code will work properly.

After a lot of digging through the code, it appears to be the setRelated() (on line 382) where things go wrong. I believe what's happening is since the "related" object is getting updated from an outdated copy of the object when there are multiple reverse relationships set up.

If I add the following line to the top of the funtion:

    if( this.related ) return;

My code works as I'm expecting, but I think this might cause other problems. I'm still trying to digest everything that's going on in the code.

On an important note, the parent functioning triggering this code is:

    addRelated: function( model, options ) {
        if ( model !== this.related ) {
            var oldRelated = this.related || null;
            this.setRelated( model );
            this.onChange( this.instance, model, { _related: oldRelated } );
        }
    },

If you comment out either of the setRelated() or the onChange() lines, the code appears to work fine. It's only when both lines run that the problem happens. I'm thinking because onChange already calls setRelated() that it's the double setting of this.related that's causing the actual problem.

Can you take a look and see if you can figure out what the root problem is?

Use foreign keys at model instantiation

Hello,

I want to discuss a feature for Backbone relational. I think it would be even better with the support of operations on foreign keys. I encountered some problems while developing my webgame (a Risk-like) and I really believe there is use cases for that.

To be concise: it would be great to be able to define the associated models with their foreign key. Example:

users = new Users([
  new User(id: 1, name: "Romain", friendIds: [ 2, 3 ])
  new User(id: 2, name: "Basile", friendIds: [ 1 ])
  new User(id: 3, name: "Cyril", friendIds: [ 2, ])
])

Without friendIds, it would be painful to declare the friendships (it must be done in two times).
Thanks to the friendIds collection, it's really simple.

What do you think about that? I would be glad to help you in trying to implement this feature.

Romain

Change events not being fired on collection change

When a nested collection changes (EDIT: item removal, specifically), the model doesn't get notified.

This or similar should do the trick (it's worked for me outside of backbone-relational):

    collection.bind("all", function() {
      this.change();
    }, this);

thoughts?

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.