pass data from model to view in backbone.js - backbone.js

i want to pass data from model to view and i want to get this data length in view and make for loop on it but the property of length get undefined and i can't pass data to view there is an error in template html
<html>
<head>
<link href='//fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
</head>
<body>
<div id="container">Loading...</div>
<div class="list">
<button id="list">LIST</button>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js" type="text/javascript"></script>
<script type="text/template" id="view_list">
</script>
<script type="text/javascript">
var app = {};
app.postModel = Backbone.Model.extend({
url: 'https://jsonplaceholder.typicode.com/comments',
defaults: {
postId: 0,
id: 0,
email:"",
body:""
}
});
app.viewlist = Backbone.View.extend({
el:$(".list"),
initialize:function(){
this.model = new app.postModel();
},
template: _.template($('#view_list').html()),
events:{
"click #list" : "list"
},
list:function(e)
{
this.model.fetch({
success: function (post) {
console.log(post.toJSON().length);
this.$el.html(this.template(post.toJSON()));
}
});
}
});
app.viewpost = new app.viewlist();
</script>
</body>
and the error in html say
Uncaught TypeError: Cannot read property 'html' of undefined
at success (backbone:49)
at Object.a.success (backbone-min.js:12)
at o (jquery.min.js:2)
at Object.fireWith [as resolveWith] (jquery.min.js:2)
at w (jquery.min.js:4)
at XMLHttpRequest.d (jquery.min.js:4)

Based on the error, looks like you don't have the view within the scope of the success function. This should work:
var view = this;
this.model.fetch({
success: function (post) {
console.log(post.toJSON().length);
view.$el.html(view.template(post.toJSON()));
}
});
Although you should probably think about adding a render function to the view, and possibly having the view listen to model changes in order to trigger it.
initialize: function() {
this.model = new app.postModel();
this.model.on('sync', this.render, this); // Backbone 0.9.2 way
// Backbone 0.9.9+ way
// this.listenTo(this.model, 'sync', this.render);
}
render: function() {
this.$el.html(this.template(this.model.toJSON()));
},
...
list: function(e) {
this.model.fetch();
}

Related

Marionette 'could not find template' - load external templates

I'm new with backbone, and also marionette. Idk why I'm get this error. My structure seems correct, but the error persists.
This is my index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<!-- Main App -->
<div id="main-area"></div>
<!-- Templates -->
<script id="main-tpl" src="templates/main.tpl" type="text/x-template"></script>
<!-- 3rd party Dependencies -->
<script src="vendor/jquery/dist/jquery.js"></script>
<script src="vendor/underscore/underscore.js"></script>
<script src="vendor/backbone/backbone.js"></script>
<script src="vendor/backbone.wreqr/lib/backbone.wreqr.js"></script>
<script src="vendor/backbone.babysitter/lib/backbone.babysitter.js"></script>
<script src="vendor/marionette/lib/backbone.marionette.js"></script>
<script type="text/javascript">
// External templates load
_.each(document.querySelectorAll('[type="text/x-template"]'), function (el) {
$.get(el.src, function (res) {
el.innerHTML = res;
});
});
var App = new Backbone.Marionette.Application();
_.extend(App, {
Controller: {},
View: {},
Model: {},
Page: {},
Scrapers: {},
Providers: {},
Localization: {}
});
App.addRegions({
Main: '#main-area'
});
App.addInitializer(function (options) {
var mainView = new App.View.Main();
try {
App.Main.show(mainView);
} catch(e) {
console.error('Error on Show Main: ', e, e.stack);
}
});
App.View.Main = Backbone.Marionette.Layout.extend({
template: '#main-tpl'
});
(function(App) {
'use strict';
App.start();
})(window.App);
</script>
</body>
and my template/main.tpl is only test html.
<div>sounds</div>
All 3rd party dependencies paths are correct.
The error that appears is this:
Error: Could not find template: '#main-tpl'
Can someone tell me where am I wrong?
Thanks.
EDIT:
I think the problem is because $.get is async and the template load after backbone try to render, how can I solve this?
You can update your HTML and replace
<script id="main-tpl" src="templates/main.tpl" type="text/x-template"></script>
with
<script id="main-tpl" type="text/html">
--- template code ---
</script>
Or use requireJs !text plugin to load template files into marionette views.
The problem is that the template loads after the app initialization.
Instead, try this:
$(function () {
var tplList = document.querySelectorAll('[type="text/x-template"]');
var tplCounter = 0;
_.each(tplList, function (el) {
$.ajax({
'url': el.src,
success: function (res) {
el.innerHTML = res;
++tplCounter;
if(tplCounter == tplList.length){
App.start();
}
}
});
});
});
define(['marionette','tpl!cell.tpl'],function(tpl){
var Mn = Backbone.Marionette;
var MyView = Mn.View.extend({
className: 'bg-success',
template: tpl,
regions: {
myRegion: '.my-region'
}
});
})
var model = new Backbone.Model({});
var myView = new MyView({model:model});

What is wrong with this backbone router?

Yes I have included undescore library and I now bind my loadChisiamo but I still got this issue.This is main.js
app_start();
var Router = Backbone.Router.extend({
routes: {
"home" : "loadHome",
"chisiamo" : "loadChisiamo",
"*actions": "defaultRoute"
},
initialize: function(){
_.bindAll(this,'loadHome','loadChisiamo');
},
loadHome: function(e){
e.preventDefault();
console.log('loadHome');
var home = new home_view();
},
loadChisiamo: function(e){
e.preventDefault();
console.log('loadChisiamo');
var chiSiamo = new chiSiamo_view();
},
defaultRoute: function(actions) {
}
});
var app_router;
function app_start(){
views();
var topBar = new topbar_view();
var menu = new menu_view();
var home = new home_view();
//app_router = new Router();
Backbone.history.start({ pushState: true });
topBar.render();
menu.render();
home.render();
}
var topbar_view,, menu_view, home_view, chiSiamo_view;
function views() {
topbar_view = Backbone.View.extend({
el: '#header',
template: Handlebars.templates['topbarHome.ht'],
initialize: function() {
_.bindAll(this, "render");
},
render : function() {
this.$el.html(this.template());
}
});
menu_view = Backbone.View.extend({...});
home_view = Backbone.View.extend({...});
chiSiamo_view = Backbone.View.extend({...});
}
Now, once I uncomment app_router = new Router() I get this error 'undefined is not a function '. So it's like I didnt define my router but I did above. So what can be the problem? This is my index.html
<body>
<div id="container">
<div id='header' class="header"></div>
<div id='main' class="main">
<div id="navigation"></div>
<div id="contain"></div>
</div>
</div>
<script src="scripts/vendor/jquery/jquery.js"></script>
<script src="scripts/vendor/jquery-ui/jquery-ui-1.10.4.min.js"></script>
<script src="scripts/vendor/underscore/underscore-min.js"></script>
<script src="scripts/vendor/backbone/backbone-min.js"></script>
<script src="scripts/vendor/handlebars/handlebars.js"></script>
<script src="scripts/vendor/modernizr/modernizr.custom.js"></script>
<script src="scripts/vendor/plugins/jquery.slides.min.js"></script>
<script src="scripts/templates.js"></script>
<script src="scripts/main.js"></script>
</body>

Backbone script doesn't work

I'm new to Backbone Framework and this is my first app. I couldn't see any rendering of my app's view in my browser. I've checked the error console and didn't find any errors. Could you guys have a look and help me? I appreciate your time on my behalf and many thanks in advance
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script id="contactTemplate" type="text/template">
<h1> <%= name %> </h1>
<dl>
<dt> <%= address %> </dt>
<dt> <%= tel %> </dt>
<dt> <%= email %> </dt>
<dt> <%= type %> </dt>
</dl>
</script>
<script>
// Your code goes here
(function ($) {
/* Dummy JSON DataSet */
var contacts = [
{name:"Test1",address:"Test Address",tel:"0123",email:"test#test.com",type:"family"},
{name:"Test2",address:"Test Address",tel:"01234",email:"test#test.com",type:"friends"},
{name:"Test3",address:"Test Address",tel:"012345",email:"test#test.com",type:"office"}
];
/* Defining Model */
var Contact = Backbone.Model.extend({
defaults:{
name:'',
address:'',
tel:'',
email:'',
type:''
}
});
/* Defining Collection (Set of Models) */
var Directory = Backbone.Collection.extend({
model:Contact
});
/* View for rendering indivijual Model*/
var ContactView = Backbone.View.extend({
tagName:'div',
className:'contact-container',
template:$('#contactTemplate').html(),
render:function(){
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
/* View for rendering collection of Models */
var DirectoryView = Backbone.View.extend({
el:$("#contacts"),
intialize:function(){
this.collection = new Directory(contacts);
this.render();
},
render:function(){
var that = this;
_.each(this.collection.models, function(item){
this.renderContact(item);
},this);
},
renderContact:function(item){
var contactView = new ContactView({
model:item
});
this.$el.append(contactView.render().el);
}
});
/* Initializing the view */
var directory = new DirectoryView();
} (jQuery));
</script>
</head>
<body>
<div id="contacts">
</div>
</body>
</html>
Beware,
(function ($) {...})(jQuery)
won't guarantee that your code will be executed when the DOM is ready. At the time of rendering DirectoryView, <div id="contacts"> isn't yet available so $el is also undefined.
Putting your script after </body> or into document.ready will solve your problem.
You also have a typo here:
el:$("#contacts"),
intialize: function(){ // <- initialize: function()
this.collection = new Directory(contacts);
this.render();
}
#akoskm's answer is correct, and i changed your code a few look like this:
ContactView = Backbone.View.extend({
tagName: 'div',
className: 'contact-container',
template: $('#contactTemplate').html(),
render: function(){
var tmpl = _.template(this.template,this.model.toJSON());
this.$el.html(tmpl);
return this;
}
});
DirectoryView = Backbone.View.extend({
el: "#contacts",
initialize: function(options){
contacts = options.contacts
this.collection = (new Directory(contacts)).models;
this.render();
},
render: function(){
var that = this;
_.each(this.collection, function(item){
this.renderContact(item);
},this);
},
renderContact: function(item){
var contactView = new ContactView({
model: item
});
this.$el.append(contactView.render().el);
}
});

Make backbone wait to render view

I'm new to backbone and I have a collection of objects and a view that displays them as a list. It is set up so that every time an object is added to the collection the view is re-rendered. This is really inefficient because if I add 20 things to the collection all at once, it will be rendered 20 times when it only needs to be rendered one time after the last item has been added. How do I make backbone hold off on rendering until I'm done adding things to the collection?
Here is my code:
<html>
<head>
<meta charset="utf-8" />
<title>Looking At Underscore.js Templates</title>
</head>
<body>
<script type="text/template" class="template">
<ul id="petlist">
<% _.each(model.pets, function(pet) { %>
<li><%- pet.name %> (<%- pet.type %>)</li>
<% }); %>
</ul>
</script>
<div id="container"/>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script type="text/javascript">
var serviceData = [
{
name: "Peaches",
type: "dog"
},
{
name: "Wellington",
type: "cat"
},
{
name: "Beefy",
type: "dog"
}
];
$(document).ready(function() {
var Pet = Backbone.Model.extend({
name: null,
type: null
});
var Pets = Backbone.Collection.extend();
var AppView = Backbone.View.extend({
updateUi: function(model) {
_.templateSettings.variable = "model";
var template = _.template($("script.template").html());
var model = { pets: model.collection.toJSON() };
var html = template(model);
$("#container").html(html);
}
});
var pets = new Pets();
var av = new AppView();
pets.bind("add", av.updateUi);
pets.set(serviceData);
});
</script>
</body>
</html>
You could also create an extra method called, lets say, add.
So, if you add a new object, the application just adds a single object instead of rendering the hole collection again.
Somethin like this:
App.Model = Backbone.Model.extend();
App.Collection = Backbone.Collection.extend({
model: App.Model
});
App.CollectionView = Backbone.View.extend({
tagName: 'ul',
render: function(){
this.collection.each(function(model){
var objView = new App.ModelView({ model: model });
this.$el.append( objView.render().el );
});
return this;
},
showAddForm: function(){
// Here you show the form to add another object to the collection
},
save: function(){
// Take form's data into an object/array, then add it to the collection
var formData = {
type: $('select[name=type]').val(),
name $('input[name=name]').val()
};
// Once added to the collection, take the object/array and...
this.addElement(formData);
},
addElement: function(model){
var modView = new App.ModelView({ model: model });
this.$el.append( modView.render().el );
}
});
App.ModelView = Backbone.View.extend({
tagName: 'li',
template: _.template( "<li><%= name %> (<%= type %>)</li>" ),
render: function(){
this.$el.html( this.template( this.model.toJSON() ) );
return this;
}
});
Do you get the idea?
When you render the hole collection, the collectionView render method calls a modelView for each object/pet.
So, this way, when you get the info of a new pet/object you can just create an instance of ModelView, and append it to the actual rendered view.
Hope it helps.
You need to re-factor a couple of things on your code. For your specific case you need to use reset instead of set, to dispatch only one event when data is set. Also you could pass the collection to the view and the view could listen the reset event.
Additionally, to prevent the stress of the browser you could use document.createDocumentFragment that is a DOM holder that speed up the treatment of append. Check point number two for more reference:
http://ozkatz.github.io/avoiding-common-backbonejs-pitfalls.html
<html>
<head>
<meta charset="utf-8" />
<title>Looking At Underscore.js Templates</title>
</head>
<body>
<script type="text/template" class="template">
<ul id="petlist">
<% _.each(model.pets, function(pet) { %>
<li><%- pet.name %> (<%- pet.type %>)</li>
<% }); %>
</ul>
</script>
<div id="container"/>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
<script type="text/javascript">
var serviceData = [
{
name: "Peaches",
type: "dog"
},
{
name: "Wellington",
type: "cat"
},
{
name: "Beefy",
type: "dog"
}
];
$(document).ready(function() {
var Pet = Backbone.Model.extend({
name: null,
type: null
});
var Pets = Backbone.Collection.extend();
var AppView = Backbone.View.extend({
initialize : function(){
this.collection.bind("reset", av.updateUi);
},
updateUi: function() {
$items = document.createDocumentFragment();
this.collection.each(function(model){
var itemView = new ItemView({model : model});
$items.append(itemView.el);
itemView.render();
});
$("#container").html($items);
}
});
var pets = new Pets();
var av = new AppView({collection : pets});
pets.reset(serviceData);
});
</script>
</body>
</html>
You can also do:
pets.set(serviceData, { silent: true });
pets.trigger('add', pets);
And modify av.updateUi to work with whole collection and create the HTML at once, but 'reset' event would be probably more appropriate than 'add' in this case.

Clueless on preloading collection from server using Backbone.js and Tastypie

I'm trying to display a list of projects using backbone.js.
Basically, backbone should be able to .fetch() the projects into the Projects collection.
This works, as I can tell from the async request which is filled with projects.
But, how do I approach rendering them on page load? There's not much documentation about using the reset() method for 'bootstrapped models'. Any help is appreciated! Thanks.
app.js:
var oldSync = Backbone.sync;
Backbone.sync = function(method, model, success, error){
var newSuccess = function(resp, status, xhr){
if(xhr.statusText === "CREATED"){
var location = xhr.getResponseHeader('Location');
return $.ajax({
url: location,
success: success
});
}
return success(resp);
};
return oldSync(method, model, newSuccess, error);
};
(function($) {
window.Project = Backbone.Model.extend({});
window.Projects = Backbone.Collection.extend({
model: Project,
url: PROJECT_ENDPOINT,
parse: function(data) {
return data.objects;
}
});
window.ProjectView = Backbone.View.extend({
tagName: 'li' ,
className: 'project',
initialize: function() {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.projects = new Projects();
this.projects.fetch(function(data) {
console.log("haha");
});
this.template = _.template($('#project-template').html());
},
render: function() {
var renderedContent = this.template(this.model.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
})(jQuery);
Template:
.........
<script>
PROJECT_ENDPOINT = "{% url api_dispatch_list resource_name="project" %}";
</script>
<script type="text/template" charset="utf-8" id="project-template">
<span class="project-title"><%= title %></span>
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
You can add bootstrapped models to the template:
<script>
PROJECT_ENDPOINT = "{% url api_dispatch_list resource_name="project" %}";
INITIAL_DATA = <%= collection.to_json %>
</script>
And then in your view constructor replace this.projects.fetch(...) with this.projects.reset(INITIAL_DATA)
I like to set up my apps to have some sort of "start" function that i call with the json data for the preloaded items.
function MyApp(){
this.start = function(initialModels){
var myModels = new MyCollection(initialModels);
var modelsView = new MyModelsView({collection: myModels});
modelsView.render();
// ... other startup code here
}
}
and the html page has a script blog that looks something like this:
<script language="javascript">
var myApp = new MyApp();
myApp.start(<%= mymodel.to_json %>)
</script>
hope that helps

Resources