I'm having some issues with rendering my view in backbone getting the data from my model so I'd appreciate any help. Here goes my html code:
<ul id="datos">
</ul>
<script type="text/template" id="listado-template">
<h2>Mi listado</h2>
<li>Item 1: <%= item1 %></li>
</script>
<script>
modelo = new Modelo();
modeloView = new ModeloView({model: modelo});
</script>
And this are my model and my view:
Modelo = Backbone.Model.extend({
urlRoot: 'myurljson',
});
Backbone.View.extend({
tagName: "ul",
el: "#datos",
template: _.template($('#listado-template').html()),
initialize: function() {
this.model.on('change', this.render, this);
},
render: function() {
this.$el.append(this.template(this.model.toJSON()));
return this;
}
});
The problem is my template is not renderized well. I'm getting an undefined error with item1. But if I delete this tag li, the tag h2 is renderized so I guess the problem is in getting my data from my model through "myurljson". Any help? Thank you!
Underscore will fail if you try to render an undefined variable. See this Fiddle trying to reproduce your problem : http://jsfiddle.net/nikoshr/PELfV/
You can
define a default item1 property in your model wit a sensible value
Modelo = Backbone.Model.extend({
urlRoot: 'myurljson',
defaults: {
item1: ''
}
});
A demo at http://jsfiddle.net/nikoshr/PELfV/1/
or test the value before printing it
<script type="text/template" id="listado-template">
<li>Item 1: <% if (typeof item1!=="undefined") print(item1) %></li>
</script>
And its demo http://jsfiddle.net/nikoshr/PELfV/2/
Related
I follow the example from this book https://leanpub.com/marionette-gentle-introduction. My problem is that the view does not rerender when i change the model by clicking on the button. As the answer from this question , i don't need to do anything because Backbone/MarionetteJS smart enough to change the view.
Here is the code
<!DOCTYPE html>
<html lang="en">
<head>
<title>Demo marionettejs</title>
<script src="./vendors/jquery/dist/jquery.js" type="text/javascript"></script>
<script src="./vendors/underscore/underscore.js" type="text/javascript"></script>
<script src="./vendors/backbone/backbone.js" type="text/javascript"></script>
<script src="./vendors/backbone.marionette/lib/backbone.marionette.js" type="text/javascript"></script>
</head>
<body>
<div id="main-region" class="container">
<p>Here is static content in the web page. You'll notice that it gets
replaced by our app as soon as we start it.</p>
</div>
<script type="text/template" id="contact-template">
<p><%- firstName %> <%- lastName %> : <%- time %> </p> <br />
<button>Change model</button>
</script>
<script type="text/javascript">
var ContactManager = new Marionette.Application();
ContactManager.Contact = Backbone.Model.extend({});
ContactManager.ContactView = Marionette.ItemView.extend({
template: "#contact-template",
initialize: function () {
this.currentMeterId = null;
},
events: {
"click button": "changeModel"
},
modelEvents: {
"change": "modelChanged"
},
changeModel: function() {
this.model.set("time", (new Date()).toString());
},
modelChanged: function() {
console.log("Model changed : " + this.model.get('time'));
},
//EDIT
onRender: function() {
//Create jsTree here.
}
});
ContactManager.on("before:start", function () {
var RegionContainer = Marionette.LayoutView.extend({
el: "#app-container",
regions: {
main: "#main-region"
}
});
ContactManager.regions = new RegionContainer();
});
ContactManager.on("start", function () {
var alice = new ContactManager.Contact({
firstName: "Alice",
lastName: "Arten",
time: "#"
});
var aliceView = new ContactManager.ContactView({
model: alice
});
ContactManager.regions.main.show(aliceView);
});
ContactManager.start();
</script>
</body>
</html>
#Edit
This code is just sample. In my real app, I have an ajax task that changes DOMs in the view. This ajax task creates a tree (jsTree) in onRender event. If i use modelEvents: {"change": "render"}, my jsTree will be reload and lost its state. So I want only update the model values in the view, others DOMs is retain.
The accepted answer to the question you pointed points to another question which has the following:
modelEvents: {
'change': "modelChanged"
},
modelChanged: function() {
console.log(this.model);
this.render();
}
And the most upvoted answer suggests the same:
modelEvents: {
'change': 'fieldsChanged'
},
fieldsChanged: function() {
this.render();
}
a comment to the most upvoted answer suggests
just {'change': 'render'} does the trick too
Which means you can do
modelEvents: {
'change': 'render'
}
So somehow you need to tell marionette invoke render on model changes.
I don't think backbone and marionette couple is smart enough to know whether you need to render view on model changes or you don't want to unless you tell them ;)
I'm sending data from my backbone view to a handlebars template (js fiddle: http://jsfiddle.net/8NhjD/) like this:
this.$el.html(this.template({
users: that.users.toJSON(),
audiences: that.audiences.toJSON()
}));
and I'm trying to access the list of users and audiences like this:
<select name="user" class = "form-control">
{{#each users}}
<option value="{{name}}">{{name}}</option>
{{/each}}
</select>
But the dropdown menus for the users and audiences are empty. What am I doing wrong here?
Your problem is that you are passing in the models, which do not expose their attributes directly. Try something like this:
this.$el.html(this.template({
users: that.users.toJSON(),
audiences: that.audiences.toJSON()
}));
UPDATED:
Without a complete fiddle, it is hard to see where you went wrong. Here is a working fiddle: http://jsfiddle.net/moderndegree/qW7Tz/
HTML:
<script id="thing-template" type="text/x-handlebars-template">
<ul>
{{#each things}}
<li>{{this.thing}}</li>
{{/each}}
</ul>
</script>
<div id="thing-view"></div>
JS:
var ThingModel = Backbone.Model.extend({}),
ThingCollection = Backbone.Collection.extend({
model: ThingModel
}),
ThingView = Backbone.View.extend({
el: '#thing-view',
template: Handlebars.compile($("#thing-template").html()),
initialize: function(){
this.things = new ThingCollection([{thing: 'purple'}, {thing: 'monkey'}, {thing: 'dishwasher'}]);
},
render: function(){
console.log(this.things.toJSON());
this.$el.html(this.template({
things: this.things.toJSON()
}));
return this;
}
});
var view = new ThingView().render();
Moving the collection-fetching from the view's initialize method to the route-handler resolved the issue.
I just started learning Backbone and from what I've seen so far when you create a view and you define a tagName and a className the view you create is created inside that element but it doesn't that it works on the code below, could someone please explain to me why? I've spend wayyy too much time on this and my head is spinning.
var app = {};
(function ($) {
app.Todo = Backbone.Model.extend({
defaults : {
name : '',
priority: '',
description: ''
}
});
app.TodoList = Backbone.Collection.extend({
model: app.Todo,
url: '#todolist'
});
app.TodoListView = Backbone.View.extend({
tagName: 'ul',
className: 'todolist',
initialize: function() {
this.template = _.template($('#todolist-template').html());
this.render();
},
render: function() {
this.$el.empty();
this.$el.append(this.template({items: this.collection.toJSON()}));
return this;
}
});
app.todoList = new app.TodoList([
new app.Todo({
name: 'unclog the sink',
priority: '10',
description: 'FIX THE SINK!!!'
}),
new app.Todo({
name: 'get bread',
priority: '0',
description: 'We are out of bread, go get some'
}),
new app.Todo({
name: 'get milk',
priority: '2',
description: 'We are out of milk, go get some'
})
]);
new app.TodoListView({el: $('#container'), collection: app.todoList});
})(jQuery);
template:
<script type="text/template" id="todolist-template">
<% _.each(items, function(item){ %>
<li>
<%= item.name %>
<%= item.description %>
<%= item.priority %>
</li>
<%}); %>
</script>
result:
<div id="container">
<li>unclog the sink FIX THE SINK!!! 10</li>
<li>get bread We are out of bread, go get some 0</li>
<li>get milk We are out of milk, go get some 2</li>
</div>
I am not expert in BackboneJS but trying to resolve the issue. As per backboneJS doc about el property, its always there in to render backbone view. The default element is <div> if you don't specify. You have understood right, that element would be render inside it.
In your code you are providing <div> element as an el, so its overriding your tagName property when creating new object of view. And another thing is you are calling render before creating object which may cause a problem. So as per my opinion, you code should looks like this :
$("$container").html((new app.TodoListView({collection: app.todoList})).render())
You should read about the el property of View, here. When you specify tagName and className it creates the DOM element <ul class="todolist"></ul> but doesn’t append it to the DOM. If the element already exists in the DOM, you can set el as a CSS selector that matches the element.
So in your case your template is getting created in an ul element, but the ul element itself is not added in the DOM.
Try doing following:
NOTE : div with an id as container should already be present in DOM.
View definition:
app.TodoListView = Backbone.View.extend({
el: '#container',
initialize: function() {
this.template = _.template($('#todolist-template').html());
this.render();
},
render: function() {
this.$el.html(this.template({items: this.collection.toJSON()}));
return this;
}
});
Template:
<script type="text/template" id="todolist-template">
<ul class="todolist">
<% _.each(items, function(item){ %>
<li>
<%= item.name %>
<%= item.description %>
<%= item.priority %>
</li>
<%}); %>
</ul>
</script>
View creation :
new app.TodoListView({collection: app.todoList});
I am having problems understanding how to render a collection in a view using a template. Here is my code:
<div id="mydiv"></div>
<script type="text/template" id="details">
<ul>
<% _.each(?, function(person) { %>
<li><%= person.name %></li>
<% }); %>
</ul>
</script>
<script>
var m = Backbone.Model.extend();
var c = Backbone.Collection.extend({
url: 'retrieve.php',
model: m
});
var v = Backbone.View.extend({
el : $('#mydiv'),
template : _.template($("#details").html()),
initialize : function() {
var coll = new c();
coll.fetch({success: function(){alert(JSON.stringify(coll));} });
this.render();
},
render : function() {
//what do I put here?
return this;
}
});
var view = new v();
I am confused about how to get the data returned from my php file into the template. What code do I need in the view and ._each? My php code is returning:
[{"id":"1","name":"John","age":"5"},{"id":"2","name":"Jane","age":"2"}]
and I see this in the alert().
You should call your render function from the success handler as a collection.fetch returns result asynchronously (you can also bind render function to a collection reset event).
var v = Backbone.View.extend({
el : '#mydiv',
template : _.template($("#details").html()),
initialize : function() {
var self = this,
coll = new c();
coll.fetch({ success: function(){ self.render() ; } });
},
render : function() {
// the persons will be "visible" in your template
this.$el.html(this.template({ persons: this.coll.toJSON() }));
return this;
}
});
And in the template refer to the same persons object
<script type="text/template" id="details">
<ul>
<% _.each(persons, function(person) { %>
<li><%= person.name %></li>
<% }); %>
</ul>
</script>
Update:
I've created the working fiddle, but I had to modify the original source code as I can't use your retrieve.php endpoint
What you have asked is a generic question of how to use a collection to generate a view. Most of them are comfortable with generating a view with a model, but not with a collection. I followed the following tutorial Binding a collection to a view. Might be helpful for you also.
I'm a newbie at Backbone.js and am coming across a scope issue with a simple view & model scenario.
I've created a simple model with a single default "score" value. I also created a simple view containing a template rendered value of "score" and a button to increment score by one on each press. The view repeats the render every time the score value is changed.
I've got this to work but in a way that I think may be a botch. The template will only render the first time unless I cache the value of "this" in view variable "thisView". If I don't it seems to lose focus and the rendering errors. Is this a good idea? Or am I missing something about repeatedly applying the render.
Thanks for any advice
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<style>
#view_container{background-color: rgba(12, 5, 11, 0.14);width: 100px;height: 100px;padding: 10px;}
</style>
</head>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>
<!-- View Template -->
<script type="text/template" id="view-template">
<div class="profileSpace">
<p>Score: <%= score %></p>
</div>
<button id="increaseScoreButton">Increase Score</button>
</script>
<div id="view_container"></div>
<script type="text/javascript">
(function ($) {
MyModel = Backbone.Model.extend({
defaults:{
score:0
},
initialize: function(){
},
increaseScore: function(){
//Increase Score by 1
var currentScore = this.get("score");
var newScore = currentScore +1;
this.set({score:newScore});
}
});
MyView = Backbone.View.extend({
el: $("#view_container"),
template: _.template($('#view-template').html()),
initialize: function(model){
thisView =this;
this.model.bind('change', this.render, this);
this.render();
},
events: {
"click #increaseScoreButton": "increaseScore"
},
increaseScore: function(){
this.model.increaseScore();
},
render: function(){
var currentScore = thisView.model.get("score");
var html = thisView.template({"score":currentScore});
$(thisView.el).html( html );
return thisView;
}
});
myModel = new MyModel;
myApp = new MyView({model:myModel});
})(jQuery);
</script>
</body>
</html>
You bind the change event via this.model.bind('change', this.render, this);
This syntax was introduced in Backbone 0.5.2 but you use Backbone 0.3.3 in your example.
0.5.2 — July 26, 2011
The bind function can now take an optional third argument to specify the this of the callback function.
Upgrade Backbone to a more recent version (0.9.2 as of today) and you should get the expected behaviour.
Or, as CoryDanielson pointed out in the comments, you could use _.bindAll to have a guaranteed context:
MyView = Backbone.View.extend({
initialize: function(model) {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.render();
},
render: function(){
var currentScore = this.model.get("score");
var html = this.template({"score":currentScore});
$(this.el).html( html );
return this;
}
});