Have said it before and will say it again.. the people on StackOverflow are AWESOME! Got a REALLY useful overview of the Event Driven Approach and how it applies to backbone too. I was concerned that having to do everything in the .complete callback was going to mean lots of overhead in terms of calling multiple syncs/fetches. http://stackoverflow.com/questions/27755976/backbone-js-reduce-calls-to-the-server/27756807
So now I’m changing things up a bit in my app to use the Event Aggregator to pass the information out of the fetch method into other functions where its needed (without doing another call to an external site)
As an update of where I am this far below. Loads more to do but I’m feeling like its definitely starting to take a better shape now! Whoop!
$( document ).ready(function() { window.APP = {} APP.vent = _.extend({},Backbone.Events); /* *********** Models *********** */ var MatchInfo = Backbone.Model.extend({ defaults: { season:"1415" } });//model class var matchInfo = new MatchInfo(); //model instance /* *********** Collections *********** */ var Matches = Backbone.Collection.extend({ model: MatchInfo, //note this references the model class, not the model instance url : "http://www.hookhockey.com/index.php/temp-gillian/", sync : function(method, collection, options) { // By setting the dataType to "jsonp", jQuery creates a function // and adds it as a callback parameter to the request, e.g.: // [url]&callback=jQuery19104472605645155031_1373700330157&q=bananarama // If you want another name for the callback, also specify the // jsonpCallback option. // After this function is called (by the JSONP response), the script tag // is removed and the parse method is called, just as it would be // when AJAX was used. //console.log('sync'); options.dataType = "jsonp"; return Backbone.sync(method, collection, options); }, parse : function(response) { // console.log(response.matches); //.matches is what the json at http://www.hookhockey.com/index.php/temp-gillian/ is putting out return response.matches; }, customFilter: function(filters){ var results = this.where(filters); return new Matches(results); } }); //collection class var matches = new Matches(); //collection instance /* *********** Views *********** */ //model view var MatchModelView = Backbone.View.extend({ tagName: 'li', template: _.template( $("#matchTemplate").html() ), // uses underscore and the content from index.html script tag with the id of matchElement that contains the template tags initialize: function () { }, render: function() { var matchTemplate = this.template(this.model.toJSON()); //passes in all of the model data (using this.model.toJSON()) into the template (this.template) so that info is available to the template tags this.$el.html(matchTemplate); //pass the templated info into the el element and return it for render return this; } }); //model view class var matchView = new MatchModelView (); //model view instance //collection view class var MatchesModelView = Backbone.View.extend({ tagName: 'div', events: { "click .hello": "checkit", "click .leagues a" : "leagues", "change .dates" : "dates", "click .home" : "render", }, initialize: function () { _.bindAll(this, "render"); this.collection.bind("reset", this.render); }, render: function(showit){ if(this.collection == undefined){ console.log('undef'); //it'll be undefined if the sync hasn't finished yet } // this.collection.each(this.oneMatch, this); return this; //make the renders chainable }, renderA : function(){ console.log('render A'); $('#a', this.$el).html('heyA'); return this; //make the renders chainable }, renderB : function(){ console.log('render B'); $('#b', this.$el).html('heyB'); return this; //make the renders chainable }, senditout: function(showit){ if(this.collection == undefined){ console.log('undef'); //it'll be undefined if the sync hasn't finished yet } console.log('yup'); this.collection.each(this.oneMatch, this); return this; }, oneMatch: function (aMatch){ //this is the function to render all the individula models var matchView = new MatchModelView ({ model: aMatch }); this.$el.append(matchView.render().el); return this }, checkit: function(){ //this will bring back all of the items that are matching data-1: data-2 var prop = $(event.target).attr('data-1'); var val = $(event.target).attr('data-2'); newCollection(this.collection, prop,val); }, leagues: function(e){ // e.preventDefault(); console.log('leagues'); var val = $(event.target).attr('href').replace('#',''); //var val = [18, 20] newCollection(this.collection, "league", val); }, dates: function(){ console.log('dates'); var mydate = $('.dates option:selected').val(); newCollection(this.collection, "matchDate", mydate); } }); //end collection view class //note: render should be called on the view after it has been instantiated //these functions are getting called from events within the view //this function is doing almost all of the filtering, its called from multiple events with different params var newCollection = function(myCollection,prop,val){ var results = myCollection.where(_.object([prop], [val])); var filteredCollection = new Backbone.Collection(results); //create a new collection from the results var newMatchesModelView = new MatchesModelView({collection: filteredCollection }); $("#allMatches").html(newMatchesModelView.senditout().el); } /* Fetching */ //trying to only fetch once so there aren't multiple calls to the server. So its only done on the index page //matches is a collection, fetch will go to the server and pull down the information matches.fetch({ async:false, //may or may not use this success : function(collection, response, options) { /* notes: calling these outside of the success listener meant that nothing got returned. This is because they fire before the fetch returns http://stackoverflow.com/questions/9431673/load-data-into-a-backbone-collection-from-json-file the alternative is to call them within the success function or to call them like so: .complete(function() { console.log(matches); console.log('length: ' + matches.length); }); ..after the fetch call. */ //console.log(matches); return matches; }, error : function(collection, response, options) { console.log(response.statusText); }, // A timeout is the only way to get an error event for JSONP calls! timeout : 5000 }).complete( function(){ //sending my populated collection to the event aggregator APP.vent.trigger("matchesCollectionLoaded:event", matches); } );//end complete /* functions for producing initial links */ getLeagues = function(leagues){ //output the list of unique leagues into an array var myArray = JSON.parse(JSON.stringify(leagues)); for(i = 0; i < myArray.length; i++){ var current = myArray[i]; var button = '<li class="division"><a href="#'+current+'">Division '+current+'</a></li>' $('.leagues').append(button); } matchesModelView.$el.prepend($('.leagues')); }; getMatchDates = function(dates){ //output the list of unique leagues into an array var myArray = JSON.parse(JSON.stringify(dates)); for(i = 0; i < myArray.length; i++){ var current = myArray[i]; var button = '<option value="'+current+'">'+current+'</option>' $('.dates').append(button); } matchesModelView.$el.prepend($('.dates')); }; /* ./functions */ //collection view is instantiated var matchesModelView = new MatchesModelView({collection: matches }); matches.bind("sync", matchesModelView.render, matches); //is this still neccesary? /* *********** Routers *********** */ var fetched; MyRouter = Backbone.Router.extend({ routes: { "" : "index", ":id": 'league' }, index: function(){ APP.vent.trigger("matchesCollectionLoaded:event", matches); }, //end of index root league: function(id){ console.log('rooting' + id); //matchesModelView.leagues() //need to figure out how to dynamically create routes } }) var myRouter = new MyRouter(); Backbone.history.start(); // end routing /* *********** Events *********** */ // event aggregator - this listens for the event triggered in the fetch complete before running APP.vent.on("matchesCollectionLoaded:event", function(matches){ //clear it out first $("#allMatches").html(''); console.log("matchesCollectionLoaded:event"); //console.log(matches.toJSON()); //create the initial buttons //pull the unique leagues out var uniqueLeagues = _.uniq(matches.pluck("league")); //the populated collection is accessible here //pull the unique leagues out var uniqueDates = _.uniq(matches.pluck("matchDate")); //pass to info to the relative functions to create buttons getLeagues(uniqueLeagues); getMatchDates(uniqueDates); fetched = true; //mytest(matches); // can throw out to other functions now $('.hello').on("click", function(){ APP.vent.trigger("futureGames:event", matches); }); $('.home').on("click", function(){ APP.vent.trigger("homeBtn:event", matches); }); //load the initial screen homeScreen(matches); }); APP.vent.on("futureGames:event", function(matches){ console.log(matches.toJSON()) }); APP.vent.on("homeBtn:event", function(matches){ APP.vent.trigger("matchesCollectionLoaded:event", matches); }); //end events var homeScreen = function(matches){ $("#allMatches").html(matchesModelView.render().el); } }); //end doc ready