Giter VIP home page Giter VIP logo

Comments (12)

PaulUithol avatar PaulUithol commented on September 6, 2024 1

If you want to handle events for a nested collection, you can bind to that collection, just like in plain Backbone. This should work (does work as far as I can tell) for change, add and remove.

As per your example:

e.get( 'rsvps' ).bind( 'all', function() {
        console.log( 'event fired on Event.rsvps; arguments=%o', arguments );
    });

from backbone-relational.

PaulUithol avatar PaulUithol commented on September 6, 2024 1

Hmm, sorry, this is something that should be working I guess (binding on rsvps:add and rsvps:remove from event).

from backbone-relational.

PaulUithol avatar PaulUithol commented on September 6, 2024 1

Checked again, this seems fine for me? Action sequence:

console.log( 'set {response:yes} on r' );
r.set( { 'response': 'yes' } );

console.log( 'remove r from e' );
e.get( 'rsvps' ).remove( r );

console.log( 'adding another rsvp' );
e.get( 'rsvps').add( { name: "Jacob" } );

Events logged:

set {response:yes} on r
event fired on Event.rsvps; arguments=["change:response", ...
event fired on Rsvp; arguments=["change:response", ...
event fired on Event.rsvps; arguments=["change", ...
event fired on Rsvp; arguments=["change", ...

remove r from e
event fired on Event.rsvps; arguments=["remove", ...
event fired on Rsvp; arguments=["remove", ...
event fired on Rsvp; arguments=["update:event", ...
event fired on Event; arguments=["remove:rsvps", ...
event fired on Event.rsvps; arguments=["relational:remove", ...

adding another rsvp
event fired on Event.rsvps; arguments=["add", ...
event fired on Event.rsvps; arguments=["update:event", ...
event fired on Event; arguments=["add:rsvps", ...
event fired on Event.rsvps; arguments=["relational:add", ...

from backbone-relational.

PaulUithol avatar PaulUithol commented on September 6, 2024 1

@rjharmon: ahh, okay. The idea I got from your post was just that you had issues with remove events not getting fired.

I don't think firing a change event for every modification on any relation by defaults would be a good idea, but something like that is pretty easy to do by adding a helper method to Backbone.RelationalModel, since this._relations actually does give you the related model or collection. Quick first attempt that you could build from:

Backbone.RelationalModel.prototype.bindRelationEvents = function() {
    var dit = this;
    _.each( this.getRelations(), function( relation ) {
        relation.related && relation.related.bind( 'all', function() {
            dit.trigger.apply( dit, arguments );
            // Or even simply `dit.render()`, if the number of events is reasonable...
        } );
    });
};

then call this.bindRelationEvents for objects you'd like to receive all events from their relations as well.

from backbone-relational.

rjharmon avatar rjharmon commented on September 6, 2024

I made this work for item adds/removes on a related collection using this.get('features-has-many-collection').bind("all", function() { this.change() }, but I'm not seeing how to iterate the collections on a model, as this._relations doesn't have meta-data for actually getting to the associated collections.

Recommendations?

from backbone-relational.

eric-hu avatar eric-hu commented on September 6, 2024

I'm getting the same behavior on Backbone 0.5.3 and Backbone-Relational 0.4.0. Changes to models in nested collections fire events on the nested collection, but do not fire on the parent-relation model.

E.g. (in Coffeescript)

class Event extends Backbone.RelationalModel
  relations: [{
          type: Backbone.HasMany,
          key: 'rsvps'
          relatedModel: 'Rsvp'
          collectionType: 'RsvpsCollection'
          createModels: true
          reverseRelation:
            key: 'event'
            includeInJSON: 'id'
         }]
  ...

At run time (in Firebug/Acebug)

e.bind 'all', () ->
  console.log 'event fired on Event object with arguments: ', arguments

r = e.get('rsvps').first()

r.bind 'all', () ->
  console.log 'event fired on Rsvp object with arguments: ', arguments

r.set {response: 'yes'}
# => event fired on Rsvp object with arguments: ["change", Rsvp ... ]

r.destroy()
# => event fired on Rsvp object with arguments: ["remove", Rsvp ... ]
# => event fired on Rsvp object with arguments: ["update:event", Rsvp ... ]
# => event fired on Event object with arguments: ["remove:rsvps", Rsvp ... ]
# => event fired on Rsvp object with arguments: ["destroy", Rsvp ... ]

from backbone-relational.

rjharmon avatar rjharmon commented on September 6, 2024

I opened this ticket because I was dissatisfied with the need to call bind('all') on all of my nested collections, in order to get notified of their changes. Calling those bind()'s myself works fine, but I wished for a single convenience method so that I'd receive a change() notification on my model when any of its nested collections change. It's pretty specific to some pretty common cases.

this.notifyOnRelationsChanged();
this.notifyOnRelationsChanged(function() { this.change() });
this.notifyOnRelationsChanged({only:['items', 'books']})
this.notifyOnRelationsChanged({except: ['interestedUsers']});

from backbone-relational.

eric-hu avatar eric-hu commented on September 6, 2024

@PaulUithol Strange, I just double checked this again in Firebug/Acebug and I'm still not seeing change events:

#>> lang=cf

e = router.events.first()
rsvps = e.get 'rsvps'
r = rsvps.first()

e.bind 'all', () ->
  log 'Event event fired.  Args: ', arguments

r.bind 'all', () ->
  log 'RSVP event fired.  Args: ', arguments

log 'changing rsvp'
r.set response: 'no'

log 'deleting rsvp'
r.destroy()

Output:

changing rsvp
deleting rsvp
DELETE http://localhost:3000/rsvps/29
200 OK
    2.63s   
RSVP event fired. Args: ["remove", Rsvp ...
RSVP event fired. Args: ["remove", Rsvp ...
RSVP event fired. Args: ["update:event", Rsvp ...
Event event fired. Args: ["remove:rsvps", Rsvp ...
RSVP event fired. Args: ["destroy", Rsvp ...

I defined the relations array only in Backbone.Models.Events, but it has the reverseRelation attribute defined. Could this be causing the issue?

(Btw, if this is too tangential to the original post I can create a new issue)

from backbone-relational.

floriandammeyer avatar floriandammeyer commented on September 6, 2024

Are there any news on this? We have a Backgrid table in our application that renders related models in a special cell and also offers an editor for that cell to change the related models. Changing the related models is no problem, but the application has to perform further actions when that relation changes. At the moment our custom cell editor just handles this too, but that is not a satisfying solution because it does not follow the principal of separation of concerns.

The problem is that, as mentioned above, the change event is not triggered for the relation's attribute.
I. e., we have a parent model ParentModel that has a HasMany relationship called 'related_items'.

var ParentModel = Backbone.RelationalModel.extend({
    defaults: function() {
        return {
            related_items: new ItemCollection();
        };
    },
    relations: [
        {
            key: 'related_items',
            type: Backbone.HasMany,
            relatedModel: ItemCollection.prototype.model
            // ...
        }
    ]
});

Listening to the 'change:related_items' on the ParentModel or a ParentModelCollection is pretty useless because that event is never triggered. Not even if you switch out the 'related_items' collection entirely: my_parent_model.set('related_items', new_collection); does not trigger the event.

Following code would seem totally natural to me

// this refers to some view which handles displaying a ParentModelCollection
this.listenTo(this.collection, 'change:related_items', this.changeCallback);

but as I understand this conversation, that is deliberately not supported. Instead I would have to iterate over each item in the collection and add a listener directly on each related collection, something like this

this.collection.each(function(model) {
    this.listenTo(model.get('related_items'), 'change', this.changeCallback);
}, this);

But that is totally impractical, because whenever new models are added or removed from my ParentModelCollection or whenever the referenced 'related_items' collection changes, I have to remove and re-bind my changeCallback for that particular model.

This is not the only time that we stumble on this problem. Our application needs to react to changed HasMany relations quite often. So I don't understand your statement:

I don't think firing a change event for every modification on any relation by defaults would be a good idea,

Why don't you think 'bubbling up' the change event to the parent model was a good idea? Can't be that much of an impact on performance I think and it would definitely make reacting to changed HasMany relations a lot more concise and straight forward.

from backbone-relational.

bpatram avatar bpatram commented on September 6, 2024

That would cause a lot of events being fired when updating large collections.

@floriandammeyer Can you check out this jsfiddle and fork it to show a failing test for your use case?

from backbone-relational.

floriandammeyer avatar floriandammeyer commented on September 6, 2024

I will take a look at it on the weekend

from backbone-relational.

floriandammeyer avatar floriandammeyer commented on September 6, 2024

Finally got the time now, I forked your fiddle here: http://jsfiddle.net/ee19fo4r/1/

In the first test case, I changed the expected behavior so that changing a model inside the child collection would also trigger a change event on the parent model. That behavior however does not make sense at all, because it would conflict with the expected behavior of the following test. You would not be able to determine what actually changed: a model inside the collection or the collection itself.

The following test expects a change event to be fired on the parent model if the related collection has been substituted entirely. I think this test case should be implemented, because it is the behavior that I expected from Backbone.Relational. If you switch out the collection entirely by calling parent.set('children', new Backbone.Collection());, it should trigger a change event for the children attribute just like chaning a normal attribute (like name) would do. However, at this moment that does not happen. Switching out the related collection happens silently, and this is problematic for my initial use case. Example:

// ...
Backbone.View.extend({
  initialize: function()
  {
    // This view should react to changes that happen to child models 
    // of the given parent models, so we iterate over all models in the
    // given collection to add an event handler
    this.collection.each(function(model) {
      this.listenTo(model.get('children'), 'change', this.handleChangedChild);
    }, this);

    // Obviously, if the related 'children' collection changes on any of the
    // parent models, we have to re-bind our listener to the new collection!
    this.listenTo(this.collection, 'change:children', function(model) {
      this.listenTo(model.get('children'), 'change', this.handleChangedChild);
      // Note: I did not implement any garbage-collection here 
      // that removes the old listeners for brevity
    });
  }
  // ...
});
// ...

In our application a related collection is being substituted quite frequently. Unfortunately, because no change event is triggered in that case, above code does not work as expected.

from backbone-relational.

Related Issues (20)

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.