underscore template is not working in backbone - backbone.js

In my view.js I am calling the wine-list-tpl (template), as below you can see this. But nothing is showing to the index page. Please help.
IN index.html
<script type="text/template" id="wine-list-tpl">
Hello world
<%= name %>
</script>
.......
<div class="wine-list"></div>
IN view.js
var WineLists = Backbone.View.extend({
el:'.wine-list',
template : _.template($('#wine-list-tpl').html()),
render: function(){
console.log("rendering is ok");
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
var WineList = Backbone.View.extend({
model:wines,
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
$('#wine-list').append((new WineLists({ model : wine })).render().$el);
});
}
});
var wineListView = new WineList();
I have added some data in to my local storage and i just want to show those data to that particular div element.
In WineLists view i am getting all those data. means when i am writing, console.log(this.model.get('name')); I got my desired data, but i only want to show those data through template to that particular div. what should i do, please suggest.

var WineLists = Backbone.View.extend({
model: new Wine(),
template : _.template($('#myTpl').html()),
render: function(){
//console.log(this.model.get('name'));
this.$el.html(this.template(this.model.toJSON()));
return this;
} });
var WineList = Backbone.View.extend({
model:wines,
el:'.wineDiv',
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
self.$el.append((new WineLists({ model : wine })).render().$el);
});}});
var wineListView = new WineList();
//please check ur DOM manipulation i think there is some error in ur HTML try to change call //sequence

This is how your code should have been for expected results.
var WineLists = Backbone.View.extend({
el: '.wine-list',
render: function () {
var _self = this;
this.collection.each(function (model) {
var view = new WineListItemView({
model: model
})
view.render();
view.$el.appendTo(_self.el);
})
return this;
}
});
var WineListItemView = Backbone.View.extend({
template: _.template($('#wine-list-tpl').html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var wines = new Backbone.Collection([{
name: 'wine1'
}, {
name: 'wine2'
}])
var wineListView = new WineLists({
collection: wines
});
wineListView.render()

Related

Marionette, backbone js betting game

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">
<%= id %>
</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/
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.

Backbone View and template

How would I create a view and template from either this model or collection? I can console log the data I want. I'm stuck on the view and template part. Thanks.
var Weather = Backbone.Model.extend({
urlRoot: "http://api.openweathermap.org/data/2.5/weather?q=New%20York&mode=json&units=imperial",
initialize: function (response) {
console.log(response.wind.speed);
console.log(response.main.temp);
console.log(response.name);
console.log(response.main.temp_min);
console.log(response.main.temp_max);
//console.log();
return response;
}
});
var WeatherCollection = Backbone.Collection.extend({
model: Weather,
url: 'http://api.openweathermap.org/data/2.5/weather?q=New%20York&mode=json&units=imperial',
parse: function(response) {
console.log(response.wind.speed);
console.log(response.main.temp);
console.log(response.name);
console.log(response.main.temp_min);
console.log(response.main.temp_max);
//console.log();
return response;
}
});
I would probably do something like this to start with:
var WeatherItemView = Backbone.View.extend({
template: _.template($('#weather-template').html()),
render: function () {
var content = this.template({
weather: this.model
});
this.$el.html(content);
return this;
}
});
var WeatherListView = Backbone.View.extend({
initialize: function () {
this.listenTo(this.collection, 'sync', this.render);
},
render: function () {
this.collection.each(function (weather) {
var subView = new WeatherItemView({
model: weather
});
this.$el.append(subView.render().$el);
}, this);
return this;
}
});
$(document).ready(function () {
var weathers = new WeatherCollection();
weathers.fetch(); // assuming its accessing an api endpoint.
var weathersView = new WeatherListView({
collection: weathers
});
$('body').html(weathers.render().$el);
});
<!-- template for one weather item view -->
<script id='weather-template' type="text/template">
<%= weather.escape('name') %>
</script>

Backbone appends to list view instead of replacing it

I am producing a single page website with Wordpress and Backbone.js, i have come into a problem with when i fetch new data. It simply adds DOM elements onto the container el rather than replacing them. The collection updates correctly as i can see it has the right amount of elements in the console.
var PostItem = Backbone.Model.extend();
var PostItems = Backbone.Collection.extend({
model: PostItem,
url: '/wp-admin/admin-ajax.php'
});
var postItems = new PostItems();
var PostView = Backbone.View.extend({ /* Model View */
tagName : 'article',
className : 'widget',
template : _.template( $('#widgetPost').html() ),
render: function(){
var attributes = this.model.toJSON();
this.$el.html( this.template( attributes ) );
return this;
}
});
var PostListView = Backbone.View.extend({ /* Collection View */
el : '#content',
initialize: function(){
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
},
addOne: function(postItem){
var postView = new PostView({ model : postItem });
this.$el.append( postView.render().el );
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
render: function(){
this.addAll();
},
});
var postListView = new PostListView({
collection : postItems
});
$(function(){
$('a#posts').click(function(){
postItems.fetch({
data: {
action: 'do_ajax',
fn: 'get_the_posts'
}
});
return false;
});
$('a#pages').click(function(){
postItems.fetch({
data: {
action: 'do_ajax',
fn: 'get_the_pages'
}
});
return false;
});
});
You need to clear out your collectionView's $el! :)
addAll: function(){
this.$el.empty();
this.collection.forEach(this.addOne, this);
}
This should do the trick.

Backbone view, initialize and render

I have a backbone view which load subview. When I load a subview, I would like to show a loader when the view fetch needed datas and hide the loader when the view is ready to render.
I did something like this :
var appView = Backbone.View.extend({
showLoader: function() {
// Code to show loader
},
hideLoader: function() {
// Code to hide loader
 },
loadSubView: function() {
this.showLoader();
var myView = new MySubView();
this.$el.html(myView.render().el);
this.hideLoader();
}
});
For now, my sub-view load a collection and is implemented like this :
var mySubView = Backbone.View.extend({
initialize: function() {
this.myCollection.fetch({
async: false
});
},
render: function() {
// Code to render
}
});
My sub view load the collection synchronously because it is the only way I found to know when my view is "ready" to render but I think this is not the best way to use Backbone.
What schould I do ?
There are several ways to do it.
You can explicitly use the pubsub pattern. Something like this:
var AppView = Backbone.View.extend({
showLoader: function() {
console.log('show the spinner');
},
hideLoader: function() {
console.log('hide the spinner');
},
loadSubView: function() {
this.showLoader();
var subView = new SubView();
subView.on('render', this.hideLoader);
this.$el.html(subView.render().el);
}
});
var SubView = Backbone.View.extend({
render: function() {
console.log('a subView render');
this.trigger('render');
return this;
}
});
var appView = new AppView({el: $('body')});
appView.loadSubView();
http://jsfiddle.net/theotheo/qnVhy/
You can attach a function to the ajaxStart/ajaxStop events on the
spinner itself:
var AppView = Backbone.View.extend({
initialize: function() {
var _this = this;
this.$('#spinner')
.hide()
.ajaxStart(_this.showLoader)
.ajaxStop(_this.hideLoader);
}
...
}
Or you can use jQuery.ajaxSetup:
var AppView = Backbone.View.extend({
initialize: function() {
var _this = this;
jQuery.ajaxSetup({
beforeSend: _this.showLoader,
complete: _this.hideLoader,
success: function() {}
});
}
...
}

Backbone - View not updating page

I'm trying to use the render function in questionListView, and it appears to be running, but is not updating the page.
The template:
<script id="myTemplate" type="text/template">
<p>Test</p>
</script>
Part of the JS:
$(function(){
//Test data
var initialListData = [
{ listName: "Sample Questions", listID: 1},
{ listName: "Default questions", listID: 2}
];
// MODELS ----------------------------------------------------
var questionList = Backbone.Model.extend({
defaults: {
listName: "Name of the list",
listID: 0
}
});
// COLLECTIONS ----------------------------------------------------
var populateList = Backbone.Collection.extend({
model: questionList
});
// VIEWS ----------------------------------------------------
var questionListView = Backbone.View.extend({
template: $("#myTemplate").html(),
render: function () {
console.log('I can see this, but nothing happens...');
var tmpl = _.template(this.template);
$(this.el).append(tmpl(this.model.toJSON()));
return this;
}
});
var AppView = Backbone.View.extend({
el : $("#content"),
initialize: function (){
this.collection=new populateList(initialListData);
this.render();
},
render: function (){
_.each(this.collection.models, function (item) {
this.renderSelect(item);
}, this);
},
renderSelect: function(item){
var populateQuestionList = new questionListView({
model: item
});
this.$el.append(populateQuestionList.render().el);
}
});
var app = new AppView();
} (jQuery));
Thanks!
Are you triggering this in a callback to the document.ready event? If not, your code could be executing before the DOM is actually loaded and ready. Try:
$(function () {
var app = new AppView();
});
A few misc points.
You can do template: _.template($("#myTemplate").html()) to cache the template function as a micro-optimization
You can use this.$el instead of $(this.el) in recent version of backbone. You are already doing this in one place but not both.

Resources