PaulUithol / Backbone-relational

Get and set relations (one-to-one, one-to-many, many-to-one) for Backbone models
http://backbonerelational.org
MIT License
2.34k stars 330 forks source link

don't initialize related Model/Collection in defaults #582

Closed daton89 closed 6 years ago

daton89 commented 6 years ago

I fall in a strange error initializing the related Model or Collection in the default of the related model:

These are my Models / Collections

export const File = Backbone.RelationalModel.extend({
    idAttribute: '_id',
    urlRoot: '/api/v1/files',
    defaults: {
        _id: undefined,
        originalname: '',
        createdOn: Date.now()
    }
})

export const Files = Backbone.Collection.extend({
    model: File,
    url: '/api/v1/files'
})

export const ChemicalAgent = Backbone.RelationalModel.extend({
    idAttribute: '_id',
    urlRoot: '/api/v1/chemical-agents',
    defaults: {
        name: '',
                files: new Files()
    },
    relations: [{
        type: Backbone.HasMany,
        key: 'files',
        relatedModel: File,
        collectionType: Files
    }]
})

when i use a ChemicalAgent, i populate it with name and files, then when i try to instantiate a new Model the files i populate before is present in new model even if i create a new instance of model. an example:

// routing with id 

Backbone.Relational.store.reset()

const chemicalAgent = new ChemicalAgent()

if (id) {

    chemicalAgent.set({ _id: id })

    await chemicalAgent.fetch()
        // chemical attributes are now {name: 'candeggina', files: [{...}, {...}]}
}

// if i go to the page of create new chemical (without pass an id) i found the files that i populated before

Backbone.Relational.store.reset()

const chemicalAgent = new ChemicalAgent()
 // here chemicalAgent attributes are: { name: '', files: [{...}, {...}] }
if (id) {

    chemicalAgent.set({ _id: id })

    await chemicalAgent.fetch()

}

so if i don't initialize new Files()in model declaration this issue doesn't happen

bpatram commented 6 years ago

Because of your defaults, all your models will be given a reference to the same collection (see code below)

defaults: {
    name: '',
    files: new Files()
}

This is because new Files() is instantiated once and not each time you create a model instance. Try changing your defaults to be a function instead. This function will be evaluated each time a new model is instantiated thus giving you a reference to a new collection instead of the same one.

defaults() {
    return {
        name: '',
        files: new Files()
    };
},
bpatram commented 6 years ago

There is another potential issue (same concept) with your defaults for the File model:

    defaults: {
        _id: undefined,
        originalname: '',
        createdOn: Date.now()
    }

The line Date.now() will be evaluated when the model is extended and not when the model is created. So all your models will be given the same exact date, which I am assuming is not your intention. I would suggest assigning defaults as a function returning an object to avoid that potential issue.

    defaults() {
        return {
            _id: undefined,
            originalname: '',
            createdOn: Date.now()
        };
    }