1
votes

This is a betting game, made in backbone. I have a bounty of 100 to help me refactor it into a marionette based application.

I am unsure where to start - in terms of reconfiguring the models, how to swap the views out to make them regions.

I believe the steps would be to start with creating a new marionette application and router.

var app = Marionette.Application.extend({
  initialize: function(options) {
    console.log('My container:', options.container);
  }
});

var app = new app({container: '#app'});


//router

var MyRouter = Marionette.AppRouter.extend({
  appRoutes: {
    "some/route": "someMethod"
  },
  routes : {
    "some/otherRoute" : "someOtherMethod"
  },
  someOtherMethod : function(){
    // do something here.
  }
});

Then create a few regions and layouts to manage old backbone views.

//regions

MyApp.addRegions({
  bankValue: "#bankvalue",
  bitValue: "#bitvalue"
});

Then convert the old backbone views to Marionette's ItemViews and CompositeViews.

//compositeview

var CompositeView = Marionette.CompositeView.extend({
  template: "#personalbank"
});

new CompositeView({
  model: userModel,
  collection: someCollection
});

Here is the latest js fiddle.

  //UserBankModel
  var UserBankModel = Backbone.Model.extend({
    defaults: {
      chips: 200
    },
    initialize: function() {
      console.log("UserBankModel initialize");
      this.on("change:chips", function(model) {
        var chips = model.get("chips"); // 23232
        console.log("Changed my chips to " + chips);
      });
    }
  });

  //UserBankView
  var UserBankView = Backbone.View.extend({
    initialize: function() {
     console.log("UserBankView initialize");
      this.render();
    },
    render: function(value) {
       this.$el.html(value);
    }
  });



  //BitcoinModel
  var BitcoinModel = Backbone.Model.extend({
    defaults: {
      currentValue: 0,
      lockedValue: 0
    },
    initialize: function() {
      console.log("BitcoinModel initialize");
      this.on("change:currentValue", function(model) {
        var currentValue = model.get("currentValue"); // 494
        console.log("Changed my currentValue to " + currentValue);
      });
    },
    getBitcoinValue: function(callback) {

      /*
        Backbone.ajax({
          dataType: 'json',
          url: "https://api.bitcoinaverage.com/ticker/USD",
          crossDomain: true,
          success: function(data) {
            callback(data);
          }
        });
      */

      json= {
        bid: 320,
        ask: 444
      };

      var mediumValue = (json.bid + json.ask) / 2;

      callback(mediumValue);
    }
  });

  //BitcoinView
  var BitcoinView = Backbone.View.extend({
    initialize: function() {
     console.log("BitcoinView initialize");
      this.render();
    },
    render: function(value) {
       this.$el.html(value);
    }
  });





  var App = Backbone.Model.extend({
    initialize: function() {
      var that = this;

      this.userBankModel = new UserBankModel();
      this.userBankView = new UserBankView({
        el: $("#bankvalue")
      });

      this.bitcoinModel = new BitcoinModel();
      this.bitcoinView = new BitcoinView({
        el: $("#bitvalue")
      });

      //setInterval(function() {
        //get val of bitcoin every second
        that.bitcoinModel.getBitcoinValue(function(mediumVal) {

          //set bit coin model
          that.bitcoinModel.set({
            currentValue: mediumVal
          });      

          //render the bit coin value
          that.bitcoinView.render(that.bitcoinModel.get("currentValue"));
        });
      //}, 1000);


      //render users chips
      this.userBankView.render(this.userBankModel.get("chips"));
    },
    currentBitcoinValue: 0,
    startBet: function(state) {
      console.log("start timer");
      this.state = state;

      //get locked value of bitcoin for the game

      var stashValue = this.bitcoinModel.get("currentValue");

      //set bit coin model with locked value
      this.bitcoinModel.set({
        lockedValue: stashValue
      });  

      var initialTimer = 5;

      var Timer = {
        i: initialTimer,
        onTimer: function() {
          var that = this;
          document.getElementById('timer').innerHTML = Timer.i;
          Timer.i--;
          if (Timer.i < 0) {
            app.gameResult();
            Timer.i = initialTimer; //reset
          } else {
            setTimeout(Timer.onTimer, 1000);
          }
        }
      };

      Timer.onTimer();
    },
    gameResult: function() {
      console.log("whats the result then");

      console.log("this.state", this.state);

      var lockedValue = this.bitcoinModel.get("lockedValue");
      var currentValue = this.bitcoinModel.get("currentValue");

      console.log("lockedValue>>", lockedValue);
      console.log("currentValue>>", currentValue);


      var result = "loss";//lose by default

      //locked value was higher
      if (
        this.lockedValue > this.currentValue && this.state["bet"] == "high" ||
        this.lockedValue < this.currentValue && this.state["bet"] == "low"
      ) {
        result = "win";//win if conditions are met
      }

      //get current value of user chips
      var newVal = this.userBankModel.get("chips");
      if (result == "win") {
        console.log("WIN -- you get chips");
        newVal += this.state["wager"];

      } else {
        console.log("LOSS -- you loose chips");
        newVal -= this.state["wager"];
      }

      //won or lost chips -- set new chip value
      this.userBankModel.set({
        chips: newVal
      });

      //render new user chips
      this.userBankView.render(this.userBankModel.get("chips"));
    }
  });





  var app = new App();

  var FormView = Backbone.View.extend({
    el: '#wager-form',
    events: {
      "submit": "doMethod"
    },
    doMethod: function(e) {

      e.preventDefault();

      var obj = [];
      this.$el.find('input[name]').each(function() {
          obj[this.name] = this.value;
      });

      //start bet
      app.startBet(obj);

    }
  });

  var form = new FormView();

I would like to know more about the best practice in using Marionette with backbone. Why use Marionette, what is the advantage?

Is it a simple case of refactoring the following example. Or does a Marionette based method work more like a collection?

  var App = Backbone.Model.extend({
    initialize: function() {
    //standard app
    }
  });

But would this be the way to refactor the App model to use Marionette?

  var App = Marionette.Application.extend({
      initialize: function(options) {
         console.log('My container:', options.container);

        //invoke other models
        this.otherModel1 = new OtherModel1();
        this.otherView1= new OtherView1({
          el: $("#selector1")
        });

      }
    });

  //add the selectors in one place?
  MyApp.addRegions({
    someRegion: "#some-div",
    anotherRegion: "#another-div"
  });


 // Although applications will not do anything
  // with a `container` option out-of-the-box, you
  // could build an Application Class that does use
  // such an option.
  var app = new App({container: '#app'});

This is the structure of my application. How would I refactor it properly to use marionette

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
    <title>backbone js</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/json2/20150503/json2.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/2.4.4/backbone.marionette.js"></script>

</head>

<body>
    Content of the document......

    <div id="app"></div>

    <div id="select1"></div>
    <div id="select2"></div>

    <script>

          //myModel1
          var MyModel1 = Backbone.Model.extend({
            initialize: function() {
              console.log("myModel1 initialize");
            }
          });

          //myView1
          var MyView1 = Backbone.View.extend({
            initialize: function() {
             console.log("myView1 initialize");
              this.render();
            },
            render: function(value) {
               this.$el.html(value);
            }
          });



          //myModel2
          var MyModel2 = Backbone.Model.extend({
            initialize: function() {
              console.log("myModel2 initialize");
            }
          });

          //myView2
          var MyView2 = Backbone.View.extend({
            initialize: function() {
             console.log("myView2 initialize");
              this.render();
            },
            render: function(value) {
               this.$el.html(value);
            }
          });




    //the core that invokes and bridges the other models.
      var App = Backbone.Model.extend({
            initialize: function() {
            var that = this;

                this.myModel1 = new MyModel1();
                this.myView1 = new MyView1({
                    el: $("#select1")
                });

                this.myModel2 = new MyModel2();
                this.myView2 = new MyView2({
                    el: $("#select2")
                });
            }
        });

    var app = new App();



    /*ok marionette stuff*/

    var MyApp = Marionette.Application.extend({
      initialize: function(options) {
        console.log(options.container);
      }
    });

    var myApp = new MyApp({container: '#app'});





      </script>
</body>
</html>

Ok so would the code kind of look like this? Where I pump the models into the options variable?

MyApp.addInitializer(function(options){
  // do useful stuff here
        var otherView1= new OtherView1({
          model: options.otherModel1 
        });
  MyApp.mainRegion.show(otherView1);
});

MyApp.addInitializer(function(options){
  new MyAppRouter();
  Backbone.history.start();
});

this is my current application - but I am unsure how to structure it with marionette moving forward?

Latest fiddle of the entire core application.


Ok so, I have my application with standard backbone like this..

with a core Model that invokes and bridges the other models. Something like this.

Old backbone way

var App = Backbone.Model.extend({
    initialize: function() {

      this.myModel1 = new MyModel1();
      this.myView1 = new MyView1({
        el: $("#select"),
        model: this.myModel1
      });

      this.myModel2 = new MyModel2();
      this.myView2 = new MyView2({
        el: $("#select"),
        model: this.myModel2
      });
    }
});

so is Marionette supposed to work like this?

App.addInitializer(function(options) {
      App.myModel1 = new MyModel1();
      App.myView1 = new MyView1({
        el: $("#select1"),
        model: App.myModel1
      });

      App.myModel2 = new MyModel2();
      App.myView2 = new MyView2({
        el: $("#select2"),
        model: App.myModel2
      });
});

and what about regions.. Do I stop using el: selectors for the view and rely on regions? And if so, how?

var View1Region = Backbone.Marionette.Region.extend({
  el: "#select1", // Must be defined for this syntax
  // Whatever other custom stuff you want
});

var View2Region = Backbone.Marionette.Region.extend({
  el: "#select2", // Must be defined for this syntax
  // Whatever other custom stuff you want
});

// Use these new Region types on App.
App.addRegions({
  view1Region: View1Region,
  view2Region: View2Region
});

// This is equivalent to:
App.view1Region = new View1Region();
App.view2Region = new View2Region();

I've made a new jsfiddle to start structuring the marionette version.

but am I invoking it correctly, using the new composite view correctly.

  //UserBankModel
  var UserBankModel = Backbone.Model.extend({
    defaults: {
      chips: 200
    },
    initialize: function() {
      console.log("UserBankModel initialize");
      this.on("change:chips", function(model) {
        var chips = model.get("chips"); // 23232
        console.log("Changed my chips to " + chips);
      });
    }
  });



var CompositeView = Marionette.CompositeView.extend({
  template: "#personalbank"
});

var userBankView = new CompositeView({
  model: UserBankModel
});



var MyApp = Marionette.Application.extend({
  initialize: function(options) {
    console.log('My container:', options.container);

    this.userBankModel = new UserBankModel();

  }
});

var app = new MyApp({
  container: '#app'
});

app.addRegions({
  bankValue: "#bankvalue",
  bitValue: "#bitvalue"
});

If we focus on the view for a second, how would I refactor this in the way I was intending to.

html

<div id="list"></div>

<script type="text/template" id="list-template">
    <div class="pagination">
        <ul></ul>
    </div>
</script>

<script type="text/template" id="item-template">
    <a href="#<%= id %>"><%= id %></a>
</script>

js

var Item = Backbone.Model.extend();
var Items = Backbone.Collection.extend({
    model: Item
});

var Views = {};

Views.ListItem = Backbone.Marionette.ItemView.extend({
    template: "#item-template",
    tagName: 'li'
});

Views.List = Backbone.Marionette.CompositeView.extend({
    template: "#list-template",
    itemView: Views.ListItem,
    itemViewContainer: "ul"
});

var Data = [
    {id: 1},
    {id: 2}
];

var items = new Items(Data);

var list = new Views.List({
    collection: items
});

list.render();

$("#list").html(list.el);

http://jsfiddle.net/c72Vg/168/

1
^ an outstanding bounty - looking for a marionette solution to the jsfiddle presented.The Old County
I don't really know why this conversion from traditional backbone to marionette backbone seems illusive to never get answered. I just don't want to hack at it.The Old County
does anyone want 100+ rep - to try and answer this in 6 hoursThe Old County

1 Answers

3
votes

A while ago the answer would have been "no", use Marionette's Application.addInitializer(function () {}) but when Marionette v3 is released that will be removed and in it's place you're expected to use events.