backbone.jstriggersscopemarionettebackbone-events

Backbone.Marionette Scope issue when triggering the same event multiple times with different arguments


The situation

I'm trying to render an overview of a nested data structure with models and collections. In particular, i'm loading a SubjectsCollection, where each subject has a few attributes, one of which is supposed to be a nested LessonsCollection.

My Controller loads the SubjectsCollection. Once the SubjectsCollection is loaded, the controller then renders a SubjectListView (a Marionette.CollectionView) with the SubjectsCollection and shown in a Marionette.Region.

Each SubjectModel within the SubjectListView gets rendered as a SubjectLayout (a Marionette.Layout).

Once the SubjectListView is shown in the Marionette.Region, a function cycles through the SubjectLayout's and triggers an event (package-definition:subject:layout:ready) on App.vent (a Marionette.EventAggregator) for each SubjectLayout, passing the SubjectLayout as an argument for any Callbacks to receive.

The desired effect

A method called openLessons reacts to the package-definition:subject:layout:ready event and loads a LessonsCollection. This should happen multiple times (Once for each SubjectLayout that was rendered).

Each LessonsCollection should render a LessonsListView and the LessonsListView should be shown in a region in the SubjectLayout of the SubjectsCollection to which they belong.

The SubjectLayout instance is supposed to be passed as an argument to the openLessons method, so each LessonsListView can be shown in the assigned region of it's corresponding SubjectLayout.

The Code

Here's the code I've got so far:

var PackageDefinition_Overview_Controller = Controller.extend({

  ...

  Data: {
    packageDefinition: {
      // attributes: {
      //   foo: 'bar',
      //   subjects: [{
      //     attributes: {
      //       baz: 'quux',
      //       lessons: []
      //     }
      //   }]
      // }
    }
  },

  // Initialization
  //---------------
  initialize: function() {

    ...        

    this.bindTo(App.vent, "package-definition:subject:layout:ready", this.openLessons);
  },

  ...

  openSubjects: function(packageDefinitionOverviewLayout) {
    var that = this,
        packageDefinition = packageDefinitionOverviewLayout.model;
        packageDefinition_id = packageDefinition.get('id'),
        subjects = packageDefinition.get('subjects') || new SubjectsCollection(),
        subjectsRegion = packageDefinitionOverviewLayout.subjects;

    ...

      subjects.load(packageDefinition_id);

    ...

    subjects.isLoaded.done(function() {
      packageDefinition.set({
        subjects: subjects
      });
      that.showSubjectListView(subjectsRegion, subjects);
    });

    ... 

  },

  openLessons: function(subjectLayout) {
    var that = this,
        subject = subjectLayout.model;
        subject_id = subject.get('id'),
        lessons = subject.get('lessons') || new LessonsCollection(),
        lessonsRegion = subjectLayout.lessons;

    ...

      lessons.load(subject_id);

    ...

    lessons.isLoaded.done(function() {
      console.log('Data', that.Data);
      subject.set({
        lessons: lessons
      });
      that.showLessonsListView(lessonsRegion, lessons);
    });

    ...

  },

  ...  

  showSubjectListView: function(region, subjects) {
    var subjectsListView = new SubjectsListView(subjects);
    region.show(subjectsListView);

    // Time to load the Lessons
    subjectsLayouts = subjectsListView.children;
    _.each(subjectsLayouts, function(subjectLayout) {
      App.vent.trigger("package-definition:subject:layout:ready", subjectLayout);
    }, this);
  },

  showLessonsListView: function(region, lessons) {
    var lessonsListView = new LessonsListView(lessons);
    region.show(lessonsListView);
  }

});

The problem

Despite the the fact that package-definition:subject:layout:ready gets triggered multiple times, and multiple LessonsCollection's get loaded. Only the last SubjectLayout receives its LessonsListView.

Also, each SubjectsCollection's 'lessons' attribute contains the very last LessonsCollection that got loaded.

So obviously there's a scoping problem here.

The questions

The questions are:

  1. Why do all SubjectsCollection get their 'lessons' attribute overwritten with the last LessonsCollection?
  2. Why does only the last SubjectLayout instance receive a LessonsListView?
  3. How do I resolve the problem?

Solution

  • Solved

    Well, that's a bit embarassing.. Turns out the problem all along was I accidentally defined some vars in the global scope, such as here:

    var that = this,
      subject = subjectLayout.model; // <-- should've been a comma... *headdesk*
      subject_id = subject.get('id'),
      lessons = subject.get('lessons') || new LessonsCollection(),
      lessonsRegion = subjectLayout.lessons;