I am getting Uncaught ReferenceError: _auditNumber is not defined error while trying to bind my model to the view using backbone.js and underscore.js
<script id="searchTemplate" type="text/template">
<div class="span4">
<p>"<%= _auditNumber %>"</p>
</div>
<div class="span4">
<p>"<%= _aic %>"</p>
</script>
Collection
//Collection
var AuditsCollection = Backbone.Collection.extend({
initialize: function() {
this.on('add', this.render);
},
render: function() {
_.each(this.models, function (item) {
var _auditView = new AuditView({
model: item
});
$("#audits").append(_auditView.render().el);
});
},
});
Model
var Audit = Backbone.Model.extend({
url: function () {
return myUrl;
},
defaults: {
_auditNumber: "",
_aic: "",
},
parse: function (data) {
data.forEach(function (auditItem) {
var auditsCollection = new AuditsCollection();
auditsCollection.add(JSON.stringify(auditItem));
});
}
});
// Sub View
var AuditView = Backbone.View.extend({
className: 'row-fluid',
template: $("#searchTemplate").html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
I know I am missing something simple, any help is appreciated.
2 problems (at least - you're kind of off in the weeds given how many backbone tutorials there are).
Your model URL is returning a list of results. That's what collections are for. Your model should fetch a single record and the parse method has to return the model's attribute data. If you stick with the tutorials, you won't need a custom url function and you won't need a custom parse function at all.
var Audit = Backbone.Model.extend({
url: function () {
//This needs to be a url like /audits/42 for a single record
return myUrl;
},
defaults: {
_auditNumber: "",
_aic: "",
},
parse: function (data) {
//this needs to return an object
return data[0];
}
});
You aren't passing a valid data object to your template function.
// Sub View
var AuditView = Backbone.View.extend({
className: 'row-fluid',
//compile template string into function once
template: _.template($("#searchTemplate").html()),
render: function () {
//render template into unique HTML each time
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
Related
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>
I've got a problem trying to use backbone on saving my Model from a form. Here I want my my view to actually be an editing form:
(function() {
'use strict';
var YachtEditor = {};
window.YachtEditor = YachtEditor;
var template = function(name) {
return Mustache.compile($('#' + name + 'Template').html());
};
YachtEditor.Tank = Backbone.Model.extend({
defaults : {
dCapacity : "",
sType : ""
}
});
YachtEditor.Tanks = Backbone.Collection.extend({
// url: "/rest/tanks",
localStorage: new Store("tanks"),
model : YachtEditor.Tank
});
YachtEditor.TankView = Backbone.View.extend({
template: template("tank"),
events: {
'click .save' : 'save',
'click .remove' : 'remove'
},
initialize: function() {
console.log("initialize tank View :");
console.log(this.model.get("id"));
},
render: function() {
this.$el.html(this.template(this));
return this;
},
save: function() {
console.log('change');
var self = this;
var values = {
sType: self.$("#sType").val(),
dCapacity: self.$("#dCapacity").val()
};
console.log("dCapacity : " + values.dCapacity);
console.log("sType : " + values.sType);
this.model.save(values);
},
remove: function() {
this.model.destroy();
},
dCapacity : function() {
return this.model.get("dCapacity");
},
sType : function() {
return this.model.get("sType");
}
});
YachtEditor.TanksView = Backbone.View.extend({
el: $("div.tankZone"),
template: template("tanks"),
events: {
"click .add" : "addTank",
"click .clear" : "clear"
},
initialize: function() {
this.tanks = new YachtEditor.Tanks();
// this.tanks.on('all', this.render, this);
this.tanks.fetch();
this.render();
},
render: function() {
this.$el.html(this.template(this));
this.tanks.each(this.renderTank, this);
return this;
},
renderTank: function(tank) {
var view = new YachtEditor.TankView({model: tank});
$(".tanks").append(view.render().el);
return this;
},
addTank: function() {
this.tanks.create({});
this.render();
},
clear: function() {
this.tanks.each(function(tank) {
tank.destroy();
});
this.render();
}
});
...
})();
Here is the mustache template i use for each tank
<script id="tankTemplate" type="text/x-mustache-template">
<div class="tankView">
<h1>Tank</h1>
<select id="sType" value="{{ sType }}">
#for(option <- Tank.Type.values().toList) {
<option>#option.toString</option>
}
</select>
<input id="dCapacity" type="text" value="{{ dCapacity }}">
<button class="destroy">x</button>
</div>
</script>
My problem here is that this.model.save() triggers a 'too much recursion' in underscore. js. (chrome is displaying an error also.
Here is the call stack on error:
_.extend
_.clone
_.extend.toJSON
_.extend.save
_.extend.update
Backbone.sync
_.extend.sync
_.extend.save
YachtEditor.TankView.Backbone.View.extend.save
st.event.dispatch
y.handle
I suspect the save to recall the blur event but i cannot find a way to explicit it... Maybe I'm not using backbone as i should?
My problem, aside of some pointed out by Yurui Ray Zhang (thank you), was that I was using a backbone-localstorage.js from an example I found here : git://github.com/ngauthier/intro-to-backbone-js.git
The "too much recursion error" stopped to appear as soon a I replaced it with a storage I found here : https://github.com/jeromegn/Backbone.localStorage
a few things. you defined your tank model as
app.Tank = ...
but in your collection you are referencing it as:
model : YachtEditor.Tank
and in your view, you are trying to assign elements before they are rendered on the page:
this.input = {}
this.input.sType = this.$("#sType");
this.input.dCapacity = this.$("#dCapacity");
I'm not sure how your view is rendered to the page, some people, like me, like to use render() to render the template directly to the page:
render: function() {
this.$el.html(this.template(this));
//done, you should be able to see the form on the page now.
},
some others, will use something else to insert the el, eg:
//in another view
tankView.render().$el.appendTo('body');
but either way, if you want to cache your elements, you need to do it after they are rendered to the page, not in initialize.
//this method is only called after render() is called!
cacheElements: function() {
this.input = {}
this.input.sType = this.$("#sType");
this.input.dCapacity = this.$("#dCapacity");
}
I'd suggest, first, try to fix this things, and then, try to add some console log or debuggers in your readForm method to see if the values are grabbed correctly:
readForm: function() {
var input = this.input;
console.log(input.sType.val());
console.log(input.dCapacity.val());
this.model.save({
sType: input.sType.val(),
dCapacity: input.dCapacity.val()
});
},
Here's my Backbone.js:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
var vent = _.extend({}, Backbone.Events);
App.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'*other' : 'other'
},
index: function() {
},
other: function() {
}
});
App.Models.Main = Backbone.Model.extend({
defaults : {
FName: ''
}
});
App.Collections.Mains = Backbone.Collection.extend({
model: App.Models.Main,
initialize: function() {
this.fetch({
success: function(data) {
console.log(data.models);
}
});
},
url: '../leads/main_contact'
});
App.Views.Mains = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('reset', this.render, this);
console.log(this.collection);
},
render: function() {
return this.collection.each(this.addOne, this);
},
addOne: function(main) {
var mainC = new App.Views.Main({ model: main});
this.$el.append(mainC.render().el);
return this;
}
});
App.Views.Main = Backbone.View.extend({
tagName: 'li',
template: template('mainContactTemplate'),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
mains = new App.Collections.Mains();
main = new App.Views.Main({ collection: mains});
new App.Router;
Backbone.history.start();
})();
What I want to do is have the data returned in the ul to be bound to a DOM element called $('#web-leads'). How do I do that, given this code? Incidentally, I've already posted about this here and tried to follow the first answer and the second answer combined. But I still don't get HTML and data bound to the DOM. The data is returning from the server correctly in my collection, so I know that's not the problem. Don't worry about the router stuff. That's for later.
Generally in Backbone you don't put data on DOM elements: you put it in views that wrap that DOM element.
That being said, if you really want to store data on the element, jQuery has a function for that:
$('#web-leads').data('someKey', 'yourData');
Then you can retrieve that data with:
$('#web-leads').data('someKey');
* EDIT *
In a comments discussion with the OP it became apparent that the real goal was simply to append a view's element to an element on the page. If the element being appended to is #web-leads, then this can be accomplished with:
$('#web-leads').append(theView.render().el);
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.
I am trying to write some backbone.js stuff to get a better understanding on where and if it fits in better for me on projects. Any way I have a site and I am loading a collection with page content.
Json data comes back with (pid,name,title,content) on my router the default is
defaultRoute: function (actions)
{
this.showInfo('food');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#" + id).addClass("active");
view.render();
}
if I put a 0 in place of id in this "new ContentView({ model: this._items.at(0) })" I will get the first item in the collection and if I do this in the View:
var ContentView = Backbone.View.extend({
el: $('#content'),
render: function ()
{
this.el.empty();
$(this.el).append(this.model.attributes.content);
return this;
}
});
I get the content displayed perfectly but of course may not be the content I wanted
Is it possible to select from a collection based on name == "food"?? I dont want to have to map the content to id numbers defeats the purpose of storing in a db
Sorry if this seems like a foolish question but I have crawled all over looking and Im sure Im missing something simple
here is my full NavigationRouter code in case it helps
var NavigationRouter = Backbone.Router.extend({
_data: null,
_items: null,
_view: null,
routes: {
"p/:id": "showInfo",
"*actions": "defaultRoute"
},
initialize: function (options)
{
var _this = this;
$.ajax({
url: "page_data.php",
dataType: 'json',
data: {},
async: false,
success: function (data)
{
_this._data = data;
_this._items = new ItemCollection(data);
_this._view.render();
Backbone.history.loadUrl();
}
});
return this;
},
defaultRoute: function (actions)
{
this.showInfo('home');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#l_" + id).parent().addClass("active");
view.render();
}
});
Backbone mixes in a bunch of Underscore's functions into its Collections.
So if you want to find the model in the collection where name === 'food', you can do:
var foodModel = this._items.find(function(model) {
return model.get('name') === 'food';
});
// this will set foodModel to the first model whose name is 'food'
As a side note, you don't need to call empty in your render function, which can just be:
render: function() {
$(this.el).html(this.model.get('content'));
return this;
}
jQuery's html function just replaces the content of an element with the html string you pass in.