Model on itemview is not shown - backbone.js

I am having a trouble displaying the model in an itemview.
Currently using require.js with backbone.marionette.
this is my header template:
// header.js
define( [
"jquery",
"underscore",
"marionette",
"user",
"userSession",
"text!../../tpl/header_template.html"
], function ( $, _, Marionette, User, UserSession, HeaderTemplate ) {
return Marionette.ItemView.extend( {
template: HeaderTemplate,
initialize: function(){
alert(UserSession.firstName + " " + UserSession.lastName)
},
onRender: function(){
alert(UserSession.firstName + " " + UserSession.lastName)
},
model: new User({
lastName: UserSession.lastName,
firstName: UserSession.firstName
})
});
});
this is the template snippet code
Logged in as <%= firstName %> <%= lastName %>
and this the code when I try to display the itemview
// attached the view
layout.header.show(new HeaderView());
When I run this code, the view is rendered fine, but the model (firstname and lastname) data is not correct. firstname and lastname are both null. The weird thing is both on initialize and onRender, they both display the first name and last name correctly.
Does anyone know how to overcome this?

I think you should do like:
initialize: function(){
this.model = new User({
lastName: UserSession.lastName,
firstName: UserSession.firstName
});
}

Related

passing model´s array to an underscore view

I'm trying to pass some models attibutes to my underscore view, however, for some reason I cannot find the right way to make it work, if some1 can point in the right direction, I would appreciated it.
App.appModel = new App.Models.AppModel({
"user" : data.user,
"acls" : data.acls //acls is an array, which I need to pass to the view
});
App.appLogged = new App.Views.App({
model : App.appModel
});
//My view
App.Views.App = Backbone.View.extend({
render : function() {
template = _.template( $('#Home').html(), {acls : this.model.toJSON }) ;
this.$el.html(template);
}
});
//so In my view... I need a li with each acl
<script type="text/template" id="Home">
<% _.each(acls, function(acl) { %>
<li><%= acl.get(0) %></li>
<% }); %>
</script>
It doens't throw any error... it just dont render it...
Thanks in advance.
Change your template compilation line:
//My view
App.Views.App = Backbone.View.extend({
render : function() {
template = _.template( $('#Home').html(), this.model.toJSON()) ;
this.$el.html(template);
}
});
model.toJSON will produce an object with keys corresponding to the model attributes. In this case, it will already contain the key acls. What you were producing is
{
acls: {
acls: [],
...
}
}
And what your template needs is:
{
acls: [xxxx]
}
Normally it's useful to make a call to console.log(this.model.toJSON()) on your render, to see what's going into your template.
Looks like missing parens in the call toJSON()?
//My view
App.Views.App = Backbone.View.extend({
render : function() {
template = _.template( $('#Home').html(), {acls : this.model.toJSON() }) ;
this.$el.html(template);
}
});

Nothing displayed on-screen and no errors, but JavaScript objects are populated

This is my first trial on backbone + marionette + require + handlebars. I will provide the full explanation on what I did, and I have no clue on why it doesn't work. I removed all possible JavaScript errors, and everything gets properly loaded. So, no errors in the console, but the page stays entirely blank.
What it represents is a simple header menu with buttons (an unordered list of buttons to be displayed in the header).
Index.php
<head>
<meta charset="UTF-8">
<title>Zwoop</title>
<link rel="stylesheet" type="text/css" href="http://www.zwoop.be/dev/css/layout.css">
</head>
<body>
<script id='zwoop_interface' type='text/template'>
<div id="headerRegion">
</div>
<div id="mainRegion"></div>
</script>
<script src="http://www.zwoop.be/dev/js/libs/require/require.js" data-main="js/main"></script>
</body>
main.js
Notes: I don't receive any JavaScript errors and the JS files are properly loaded (I checked this in the browser).
//Require.js
require.config({
baseUrl: 'js',
paths : {
jQuery : '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min',
jQueryUI : '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min',
lodash : 'libs/lodash/lodash',
backbone : 'libs/backbone/backbone',
marionette : 'libs/marionette/marionette',
handlebars : 'libs/handlebars/handlebars-v1.1.2',
text : 'libs/require/text',
localstorage : 'libs/backbone/localstorage'
},
shim : {
backbone: {
deps : ['jQuery', 'lodash'],
exports : 'Backbone'
},
marionette: {
deps : ['backbone'],
exports : 'Marionette'
},
handlebars: {
exports: 'Handlebars'
}
}
});
require(["backbone","marionette", "views/main_menuView"], function (Backbone, Marionette, Main_menuView) {
var Zwoop = new Marionette.Application();
//Pass options if required
var options = {
};
//Initialize functions
Zwoop.on("initialize:before", function(options){
console.log("test");
});
Zwoop.addInitializer(function(options){
var Main_Layout = Marionette.Layout.extend({
template: "#zwoop_interface",
regions: {
headerRegion: "#headerRegion",
bodyRegion: "#bodyRegion"
}
});
var main_layout = new Main_Layout();
//Rendering the layout is required before you can show anything within the regions
main_layout.render();
main_layout.headerRegion.show(Main_menuView);
console.log("rendered"); //This console log works
});
Zwoop.vent.on("main_layout:rendered", function(){
//Initialize router
new ZwoopRouter();
Backbone.history.start();
console.log("router started"); //This one is not called; I don't know why
});
//Start the application
Zwoop.start(options);
return Zwoop;
});
3_ main_menuView.js
Notes: I console logged 'Main_MenuCollection.toJSON()' and the object is properly set.
define([
'jQuery',
'marionette',
'handlebars',
'text',
'text!templates/main_menu.html',
'models/main_menuModel'
], function ($, Marionette, Handlebars, Text, Main_menu_tpl, Main_MenuCollection) {
'use strict';
var Main_MenuView = Marionette.ItemView.extend({
initialize: function () {
_.bindAll(this, 'render');
this.render();
},
el: '#headerRegion',
template: Handlebars.compile(Main_menu_tpl),
events: {
'click .main_menu_item':'select_menu'
},
select_menu: function(){
console.log("clicked");
},
render: function () {
this.$el.html(this.template({
models: Main_MenuCollection.toJSON()
}));
return this;
}
});
var main_menuView = new Main_MenuView();
return main_menuView;
});
4_ main_menu.html
This is the template that I used:
<ul id="main-menu">
{{#each models}}
<li><a id="{{models.id}}" href="{{models.href}}" class='main_menu_item'">{{models.label}}</a></li>
{{/each}}
</ul>
4_ main_menuModel.js model + collection
Note: Also here, I console logged the collection before returning it, and it is properly set.
define([
'backbone'
], function(Backbone){
var Menu_ItemModel = Backbone.Model.extend({
initialize: function(){
},
//These are data that are related to the main menu
defaults: {
id: 'undefined',
href: 'undefined',
label: 'undefined'
}
});
var btn_bars = new Menu_ItemModel({id:'btn_bars', href: 'bars', label:'Bars'});
var btn_eat = new Menu_ItemModel({id:'btn_eat', href: 'places_to_eat', label:'Places to eat'});
var btn_events = new Menu_ItemModel({id:'btn_events', href: 'events', label:'Bars'});
var btn_touristic = new Menu_ItemModel({id:'btn_touristic', href: 'touristic', label:'Touristic places'});
var btn_hotels = new Menu_ItemModel({id:'btn_hotels', href: 'hotels', label:'Hotels'});
var btn_shops = new Menu_ItemModel({id:'btn_shops', href: 'shops', label:'Shops'});
var btn_companies = new Menu_ItemModel({id:'btn_companies', href: 'companies', label:'Companies'});
var Main_MenuCollection = Backbone.Collection.extend({
initialize: function(){
},
model: Menu_ItemModel
});
var main_menuCollection = new Main_MenuCollection();
main_menuCollection.add([
btn_bars,
btn_eat,
btn_events,
btn_touristic,
btn_hotels,
btn_shops,
btn_companies
]);
return main_menuCollection;
});
The first attempt, and I'm not quite experienced yet so I really don't see where to find the problem. Do you have any suggestions?
Your main_layout gets rendered, but never shown. This seems to be the main issue.
In addition, console.log("router started") might not get called, because although you define a listened for the "main_layout:rendered" event, I don't see it getting triggered anywhere.
Also, it seems you might be confused about layouts VS regions : regions remain "static" with the application, whereas layouts are removed and redisplayed as the user navigates through the app. So for example, you'd use a region to display the app's header menu, but you'd use a layout to display (e.g.) the user's homepage (so you can organize the various sub-views). In other words, you create application regions to segment areas in your application that will always be displayed (e.g. header, main content, footer), and then you can also use layouts (with declared regions) to organize views that require sub-views (e.g. a "user profile" page with a "last comments" region, a "contact info" region, etc.). they have the same name, but think of application regions as "areas" in the application, and layout regions as "parts of a big, ciomplex view".
Last but not least, you might want to consider using layouts like this https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L43 (from my Marionette book) : note that we use the lyout's "show" event listener to then display the sub-views. This means we don't need to call render manually, which is more in line with Marionette's conventions.

Backbone.js model property is getting not defined error

I'm very new to Backbone.js and am trying to get this simple example working. Basically, in jsFiddle when I run the code it tells me that the property "firstname" is not defined.
Here's a link to the fiddle:
http://jsfiddle.net/cpeele00/YjUBG/16/
var User = Backbone.Model.extend({});
var UserList = Backbone.Collection.extend({
model: User
});
var UserView = Backbone.View.extend({
el: $('#user-list ul'),
template: _.template($('#user-list-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var user1 = new User();
user1.set({
firstname: 'Momo',
lastname: 'Peele'
});
var user2 = new User();
user2.set({
firstname: 'Bobo',
lastname: 'Peele'
});
var users = new UserList([user1, user2]);
var userView = new UserView({model: users});
userView.render();
​
Any help figuring this out would be greatly appreciated.
V/R
Chris
Since the model is actually a collection, you need to iterate over it, and apply the template to each model in the collection. One way is to use the Underscore extension Collection.each:
render: function() {
// clear the view
this.$el.empty();
// save a reference to the view object
var self = this;
// iterate over the collection
this.model.each(function(singleModel) {
// render the model
self.$el.append(self.template(singleModel.toJSON()));
});
return this;
}
Here's the updated Fiddle.
(You could also put the iteration into the template itself if you like, but I think it's generally preferable to keep code in the view, rather than the template.)

Can't display data using Handlebars template and BackboneJS

I have been trying to display some data(a json object with only three properties) by fetching it from server (2 lines of php code). To fetch and display that data in html page I've used BackboneJS and Handlebars template respectively. Here is the javascript code
var User = Backbone.Model.extend({
urlRoot:"getUser/"
});
var UserView = Backbone.View.extend({
el:$("#user"),
initialize: function(){
this.model.bind("change", this.render());
},
render: function(){
var templateSource = $("#user-temp").html();
var template = Handlebars.compile(templateSource);
$(this.el).html(template(this.model));
var newDate = new Date();
console.log("in UserView render :: " + newDate.getTime());
console.log(this.model.toJSON());
//var pp = "nothing";
}
});
var UserRouter = Backbone.Router.extend({
routes:{
"":"userDetails"
},
userDetails:function(){
//var newUser = new User({id:1});
var newUser = new User();
var userView = new UserView({model:newUser});
var newDate = new Date();
newUser.fetch({
success:function(){
console.log("in router :: " + newDate.getTime());
console.log(userView.model.toJSON());
}
});
}
});
Handlebars template in index.html page
<div id="user"></div>
<script id="user-temp" type="text/x-handlebars-template">
<div>
ID {{attributes.id}}
Name {{attributes.name}}
Age {{attributes.age}}
</div>
</script>
PHP code
$user = array("name"=>"Arif","id"=>"1","age"=>"100");
echo json_encode($user);
Now the problem is I can't see the data ($user) i'm sending from server in index.html page, in console (google chrome) i've rather found this
in UserView render() :: 1350880026092
Object
__proto__: Object
in router :: 1350880026097
Object
age: "100"
id: "1"
name: "Arif"
__proto__: Object
(The BIG numbers in console is time in milliseconds.)
But If I change the code for console output (just showing the model)
(in UserView render() function)
console.log(this.model);
(in UserRouter userDetails() function)
console.log(userView.model);
Then the console looks like this
in UserView render :: 1350881027988
child
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object <<======
age: "100"
id: "1"
name: "Arif"
__proto__: Object
changed: Object
cid: "c0"
id: "1"
__proto__: ctor
in router :: 1350881027995
child
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object <<======
age: "100"
id: "1"
name: "Arif"
__proto__: Object
changed: Object
cid: "c0"
id: "1"
__proto__: ctor
Here i can see the attributes (arrow marks <<====== )
So what am i doing wrong? Am i missing some basic concepts here? By the way, I'm new to Handlebars and BackboneJS. Moreover its my first question in stackoverflow, so if you think the info i've given isn't enough, please feel free to ask what further info you need.
Thanks in advance.
You bind your model to this.render() which you means you execute your render function and then bind your model to whatever render returns (nothing, in your case).
Try
initialize: function(){
_.bindAll(this, 'render'); // guarantees the context for render
this.model.bind("change", this.render);
}
or, with a more up to date syntax (see the changelog for 0.9.0 http://backbonejs.org/#changelog, bind and unbind have been renamed to on and off for clarity)
initialize: function(){
_.bindAll(this, 'render');
this.model.on("change", this.render);
}

Creating a backbone view for a collection

How can I bind a backbone view to a collection rather than a model? Do I need to wrap the collection in a model?
e.g.
If I have a backbone model Client and a collection of these called Clients
Client = Backbone.Model.extend({
defaults: {
Name: ''
}
});
Clients = Backbone.Collection.extend({
model: Client,
url: 'Clients'
});
and a view
var ClientListView = Backbone.View.extend({
template: _.template($("#clients-template").html()),
el: $('#clientlist'),
initialize: function() {
_.bindAll(this, 'render');
this.collection = new Clients();
},
render: function( event ){
$(this.el).html(this.template({ this.collection.toJSON()));
return this;
}
});
then I can't access each client element in the underscore template. However if I wrap the collection like this
$(this.el).html(this.template({ clients: this.collection.toJSON() }));
then I can. Is this the correct way to go about this? I would expect this to be a common scenario but I can't find any examples on it, am I going about it the wrong way?
Yes, you need to pass the wrapped collection.
Addy Osmani is using similar approach in his Backbone Fundamentals examples - see for example this view and corresponding template:
In the view:
$el.html( compiled_template( { results: collection.models } ) );
In the template:
<% _.each( results, function( item, i ){ %>
...
<% }); %>
Another alternative is to have a view that will create separate view for each model in the collection. Here is an example from An Intro to Backbone.js: Part 3 – Binding a Collection to a View:
var DonutCollectionView = Backbone.View.extend({
initialize : function() {
this._donutViews = [];
this.collection.each(function(donut) {
that._donutViews.push(new UpdatingDonutView({
model : donut,
tagName : 'li'
}));
});
},
render : function() {
var that = this;
$(this.el).empty();
_(this._donutViews).each(function(dv) {
$(that.el).append(dv.render().el);
});
}
});
You might want to take a look at backbone collectionView.

Resources