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.
Related
I'm creating a bookmarking tool that lets you create a list of stored bookmarks from an in-page form. The list will include an identifier tag (eg - Amazon www.amazone.com tag: shopping). From this created list, I want to also have a list showing the various tags that have been named.
This should probably use the filter method, but I cannot seem to get it to filter a correct list; I keep getting each tag listed for each URL created so there are multiple examples of the same tag.
Here is the code that correctly works in creating my form, the resulting URL list, and the module exports. This is from my Views js file:
var $ = require('jquery');
var Backbone = require('backbone');
var listTemplate = require('../../templates/addresslist.hbs');
var formTemplate = require('../../templates/addressform.hbs');
var detailTemplate = require('../../templates/addressdetail.hbs');
var AddressFormView = Backbone.View.extend({
tagName: 'form',
template: formTemplate,
events: {
'submit': 'addAddress'
},
render: function(){
var renderedHtml = this.template();
this.$el.html(renderedHtml);
return this;
},
addAddress: function(event){
event.preventDefault();
this.collection.create({
title: $('#title').val(),
url: $('#url').val(),
tag: $('#tag').val(),
});
$('#title').val('');
$('#url').val('');
$('#tag').val('');
},
});
var AddressListView = Backbone.View.extend({
tagName: 'ul',
initialize: function(){
this.listenTo(this.collection, 'add', this.renderItem);
},
render: function(){
return this;
},
renderItem: function(address){
console.log('address', address);
var addressItem = new AddressItemView({model: address});
this.$el.append(addressItem.render().el);
}
});
var AddressItemView = Backbone.View.extend({
tagName: 'li',
template: listTemplate,
render: function(){
var context = this.model.toJSON();
this.$el.html(this.template(context));
return this;
}
});
var AddressDetailView = Backbone.View.extend({
template: detailTemplate,
render: function(){
this.$el.html('').append(this.template);
return this;
}
});
module.exports = {
'AddressFormView': AddressFormView,
'AddressListView': AddressListView,
'AddressItemView': AddressItemView,
'AddressDetailView': AddressDetailView,
}
My router js file looks like this:
var $ = require('jquery');
var Backbone = require('backbone');
var views = require('./views/addressview');
var models = require('./models/address');
var Router = Backbone.Router.extend({
routes: {
'': 'index',
'detail/:id/': 'detail'
},
initialize: function(){
this.collection = new models.AddressCollection();
},
index: function(){
var addressForm = new views.AddressFormView({collection: this.collection});
$('.app').html(addressForm.render().el);
var addressListing = new views.AddressListView({collection: this.collection});
$('.app').append(addressListing.render().el);
this.collection.fetch();
},
detail: function(addressId){
this.collection.fetch().done(function(){
var address = this.collection.get(addressId);
var addressDetail = new views.AddressDetailView({model: address});
$('.app').html(addressDetail.render().el);
}.bind(this));
},
});
var router = new Router();
module.exports = router;
Create new event in view for multiple tag selection
tagSelected :function(event){
var tags = [<tag1>,<tag2>] //getting from multiple tag selection
var models = _.filter(this.collection.models,function(model){
return tags.indexOf(model.get('tag')) >= 0;
})
this.collection.reset(models);
this.render();
})
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.
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>
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()
Array Of Objects
The data is received from server
var Updates = [
{"post_id":"1","post_desc":"This is my first post",
"comments":[{"id":1,"comment":"some comments","like":7},
{"id":9,"comment":"some comments","like":3}
]
},
{"post_id":"2","post_desc":"This is my second post",
"comments":[{"id":5,"comment":"some comments","like":5}]
}]
Model:
var Update = Backbone.Model.extend({
defaults:{
photo: "default.png"
}
});
Collection:
var latestUpdates = Backbone.Collection.extend({
model: Update
});
Single View:
var UpdateView = Backbone.View.extend({
tagName: "div",
className: "post-container",
template: $("#postTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
Master view:
var UpdatesView = Backbone.View.extend({
el: $("#postContainer"),
initialize: function () {
this.collection = new latestUpdates(Updates);
this.render();
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.renderUpdates(item);
}, this);
},
renderUpdates: function (item) {
var updateView = new UpdateView({
model: item
});
this.$el.append(updateView.render().el);
}
});
//create app instance
var wallUpdates = new UpdatesView();
How can I render comments section under each post?
Trying to achieve layout similar to facebook post-comment system
I'd use a CommentListView, owned by your UpdateView. tagName: "ul", className: "post-comments"
Then have a CommentView owned by the CommentListView. CommentView's render should not append anything to the DOM, but return its $el.
CommentListView would tell each of the CommentView's to render, appending each of their $el's to the CommentListView's $el.
For the containers, I'd use:
<div class="post-container" data-post-id="<%= YourPostId %>">
<div class="post-body">
<!--Your post can go in here-->
</div>
<ul class="post-comments">
<!--Append your comments in here-->
</ul>
</div>