Combining multiple templates before render using requireJs & BackboneJs - javascript

I am trying to implement the idea of multiple templates per-view in BackboneJs using requireJs and the requiteJs text plugin.
Here is my view code - you can see i have passed in two templates in my define() of witch gets passed through successfully.
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
var $template = $(Template);
$template.prepend(ElementList);
this.template = _.template($template.html());
},
render: function (Template) {
this.$el.html(this.template({posts : this.collection}));
return this;
},
add: function() {
}
});
return BlogPostIndexView; });
You can see i am trying to combine the second template into the html of the first template. This works but unfortunately when i render then i get this....
<div class="outer-wrapper">
<div id="blog-post-wrapper">
<h1>texting views</h1>
</div>
<ul>
<% _.each(posts, function(post){ %>
<li><%= post.title %></li>
<% }); %>
</ul>

I'm missing closing tag for the outer-wrapper, but lets suppose it should be after closing 'ul' tag and your templates looks as follows:
container.html
<div class="outer-wrapper">
<div id="blog-post-wrapper">
<h1>texting views</h1>
</div>
</div>
list.html
<ul>
<% _.each(posts, function(post){ %>
<li><%= post.title %></li>
<% }); %>
</ul>
code:
define([..."container.html", "list.html"...], function (...container, list...) {
...
initialize: function () {
// container:
// no need to compile 'container' if there are no variables..
// list:
this.listTemplate = _.template(list);
}
...
render: function () {
var $container = $(container);
$container.append(this.listTemplate({...}));
this.$el.html($container);
}
BTW: check this !text alternative https://github.com/tbranyen/lodash-template-loader

Related

Backbone.js Uncaught TypeError

I have written the flowing backbone.js program :
<script>
var PostModel = Backbone.Model.extend();
var PostView = Backbone.View.extend({
template: _.template($('#posttemplate').html()),
intialize: function() {
console.log("intializing view");
},
render: function() {
console.log("rendering..");
var htmloutput = this.template(this.model.toJSON());
$('#postcontainer').html(htmloutput);
return this;
}
});
$(document).ready(function() {
var postmodel = new PostModel({title: "hello"});
var postview = new PostView({model: postmodel});
postview.render();
});
</script type="text/javascript">
<script type="text/template" id="posttemplate">
<div> Title: <%= title %> , post: <%= post %> </div>
</script>
<div class="container" id="postcontainer"></div>
when i run the code i get the following error:
Uncaught TypeError: Cannot read property 'replace' of undefined
but it works perfectly fine when i put
template = _.template($('#posttemplate').html()); into the render function.
Your problem is that you're trying to access the template before it exists. The HTML document is parsed from the top to the bottom and when you have
template: _.template($('#posttemplate').html())
then the $('#posttemplate') selector does not return any results because the element containing the template hasn't been parsed yet.
Try moving the
<script type="text/template" id="posttemplate">
<div> Title: <%= title %> , post: <%= post %> </div>
</script>
element up above your first script element.
The reason it works when you put it in the render function is that render is called after the document fires a ready event, at which point the template exists.

Using callback function with loop in underscore.js of backbone.js project not working

I am developing backbone.js project with underscore.js and require.js.
I created a javascript callback function in order to get data from web service using ajax :
this.getProCallBack = function(type,callback){
var result = "";
$.ajax({
url: url, //rest url
type:"GET",
dataType:"json",
contentType: "application/json",
success: function(response){
result = callback(response);
}
});
}
Here is View of backbone.js
define(["jquery" ,
"underscore" ,
"backbone",
"text!templates/Layout/ba.html",
"promo"
],function($, _, Backbone, BannerTem, Promo){
var BaView = Backbone.View.extend({
initialize : function(){
},
render: function(options){
var _banner = _.template(BannerTem);
$(this.el).html(_banner({type : this.options.type}));
return this;
}
});
return BannerView;
});
I'm using text.js of to call underscore template with html file.
Here is a template : ba.html
<div id="ei-slider" class="ei-slider">
<ul class="ei-slider-large">
<%
var home = new Promo();
home.getProCallBack(type,function(result){
_.each(result, function(slideimg){
switch(slideimg.ItemCount){
case 0 :
alert(slideimg.PromotionImage);
%>
<li>
<a href="#detail/<%=slideimg.ItemList[0].UID%>">
<img src="slideimg.PromotionImage%>" />
</a>
</li>
<%
break;
default:
%>
<li>
<a href="#detail" style="float: left;">
<img src="slideimg.PromotionImage%>" />
</a>
</li>
<%
}
%>
</ul>
</div>
Problem : alert() is working in case block, yet <img> did not show in DOM.
I'm not sure is it because of _.each() does not work in callback function or what could be caused this problem.
Any idea what could be causing this. Thanks.
Your approach is a bit backwards. AJAX calls don't work that well inside templates because of timing issues.
You should turn your logic inside out so that the AJAX call is in your view and you handle the template inside the $.ajax success callback. Something like this in your view:
render: function(options){
var _banner = _.template(BannerTem);
var home = new Promo();
home.getProCallBack(this.options.type, function(result) {
this.$el.html(_banner({result result}));
}.bind(this));
return this;
}
and then the template would be more like this:
<div id="ei-slider" class="ei-slider">
<ul class="ei-slider-large">
<% _.each(result, function(slideimg) {
if(slideimg.ItemCount == 0) { %>
<li>
<a href="#detail/<%=slideimg.ItemList[0].UID%>">
<img src="<%= slideimg.PromotionImage%>" />
</a>
</li>
<% } else { %>
<li>
<a href="#detail" style="float: left;">
<img src="<%= slideimg.PromotionImage%>" />
</a>
</li>
<% }
}) %>
</ul>
</div>
I switch the switch to an if/else along the way, a switch is difficult to get right in an Underscore template so I tend not to bother.

Backbone getting model for a clicked element

Say I have a collection of sportsmen. I juste display their names and when there's a click on a name, I'd like to dislay some more details. But that implies I have to retrieve the associate model.
So I have a model, a collection and then two views, one that takes care of rendering each individual name, and the other one manages the list of those names. And then I'll have to create a view to display the details of the clicked sportsman.
But I can't figure out how to retrive the associate model. If I place my event in the view that manages the list, it triggers on click but there's an error saying that the model is undefined.
And if the event listener is located in the view that renders each individual name, it doesn't do anything on click.
Here's my code :
Index.html
<script id="nameListTemplate" type="text/template">
<%= first %> <%= last %>
</script>
<script id="contactTemplate" type="text/template">
<ul>
<li><%= first %></li>
<li><%= last %></li>
<li><%= age %></li>
<li><%= sport %></li>
<li><%= category %></li>
</ul>
</script>
<script src="js/lib/jquery.min.js"></script>
<script src="js/lib/underscore-min.js"></script>
<script src="js/lib/backbone-min.js"></script>
<script src="js/models/sportsManModel.js"></script>
<script src="js/collections/sportsMenCollection.js"></script>
<script src="js/views/nameView.js"></script>
<script src="js/views/nameListView.js"></script>
<script src="js/app.js"></script>
<ul id="sportsMenName"></ul>
<div id="sportsManDetails"></div>
sportsManModel.js
var app = app || {};
app.SportsManModel = Backbone.Model.extend({});
sportsMenCollection.js
var app = app || {};
app.SportsMenCollection = Backbone.Collection.extend({
model: app.SportsManModel
});
nameView.js
var app = app || {};
app.NameView = Backbone.View.extend({
tagName: 'li',
className: 'sportsMan',
template: _.template($('#nameListTemplate').html()),
/*events: {
'click .sportsMan': 'showSportsManDetails'
},
// here it doesn't work at all
showSportsManDetails: function(e){
alert(this.model.get('first'));
},*/
render: function(){
this.$el.append(this.template(this.model.attributes));
return this;
}
});
nameListView.js
var app = app || {};
app.NameListView = Backbone.View.extend({
el: '#sportsMenName',
initialize: function(sportsMen){
this.collection = new app.SportsMenCollection(sportsMen);
this.render();
},
/*events: {
'click .sportsMan': 'showSportsManDetails'
},
// here it says that this.model is undefined
showSportsManDetails: function(e){
alert(this.model.get('first'));
},*/
render: function(){
this.collection.each(function(sportsMen){
this.renderContact(sportsMen);
}, this)
},
renderContact: function(sportsMen){
var nameView = new app.NameView({
model: sportsMen
});
this.$el.append(nameView.render().el);
}
});
The first problem is that you have a view for li.sportsMen element so you don't need to use class .sportsMen in events at all.
And second problem you had wrong method name for click event showSportsManDetails but should be showContactDetail as you have showContactDetail method or rename method name to showSportsManDetails instead.
app.NameView = Backbone.View.extend({
...
events: {
'click': 'showSportsManDetails'
},
showSportsManDetails: function(e){
alert(this.model.get('first'));
},
Here is a working example http://jsbin.com/yutaduvokehu/1/edit
i think the problem may be with the scope , try adding this initalize function in nameView :
initailize : function(){
_.bindAll(this,'showContactDetail');
}

Backbone JSON collection to template output

I asked a question on this yesterday which helped alot.
I have rewrote most of the code by following tutorials and youtube videos as well as help on stackoverflow however i am unsure what i am doing wrong when pushing the JSON data to the underscore template.
Basically i want to take the data from the json array, loop through it and display it. I've seen tutorials that do this through .get but weren't using an json array. Any help is appreciated.
My code looks like this: (I've put a comment on the line i guess things are going wrong)
<body>
<div class="News"></div>
<script type="text/template" id="NewsTemplate">
<table>
<% _.each(NewsCollection, function(item) { %>
<tr>
<td><%= item.title %></td>
</tr>
<% }); %>
</table>
</script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone.js"></script>
<script type="text/javascript">
var NewsModel = Backbone.Model.extend({ });
// backbone collection, gather google news json array
var NewsCollection = Backbone.Collection.extend({
url: 'data.js'
})
var NewsList = Backbone.View.extend({
el: '.News',
template: _.template($("#NewsTemplate").html()),
render: function () {
var that = this;
var NewsItems = new NewsCollection();
NewsItems.fetch({
//guessing im doing something wrong here?
success: function (NewsItems) {
$(this.el).html(that.template({'collection.toJSON': NewsItems.toJSON()}));
}
})
}
});
// Backbone router do action on homepage load
var Router = Backbone.Router.extend({
routes: {
'': 'home'
}
});
var newslist = new NewsList();
var router = new Router();
router.on('route:home' , function (){
newslist.render();
});
Backbone.history.start();
</script>
</body>
Try this :
Replace following lines
success: function (NewsItems) {
$(this.el).html(that.template({'collection.toJSON': NewsItems.toJSON()}));
}
with
success: function (NewsItems) {
$(this.el).html(that.template({ newsItems: NewsItems.toJSON()}));
}
Update template as:
<script type="text/template" id="NewsTemplate">
<table>
<% _.each(newsItems, function(item) { %>
<tr>
<td><%= item.title %></td>
</tr>
<% }); %>
</table>
</script>

Trying to Upgrade a jQM 1.0 Backbone project to 1.3.1

I'm trying to upgrade a Backbone tutorial by Steve Smith's jQuery Mobile version from 1.0 to 1.3.1 - the process doesn't seem as straightforward as I first thought however. I get no errors in the console, but nothing "happens." Everything works fine in 1.0 - however when I upgrade the files to 1.3.1, the data isn't rendered.
JsFiddle
HTML (script imports and css removed):
<title>Exercise</title>
<!-- templates -->
<script type="text/template" id="activity-list-item-template">
<li><%= date %> - <%= type %></li>
</script>
<script type="text/template" id="activity-details-template">
<h3><%= type %></h3>
<ul data-role="listview" id="activitiy-fields" data-inset="true">
<li>Date: <%= date %></li>
<li>Minutes: <%= minutes %></li>
<li>Distance: <%= distance %></li>
<li>Comments: <%= comments %></li>
</ul>
</script>
<body>
<div data-role="page" id="activities">
<div data-role="header">
<h1>Activities</h1>
</div>
<div data-role="content">
<!-- the contents of the list view will be rendered via the backbone view -->
</div>
</div>
<div data-role="page" id="activity-details" data-add-back-btn="true">
<div data-role="header">
<h1>Activity Details</h1>
</div>
<div data-role="content" id="activity-details-content">
<!-- the contents of the list view will be rendered via the backbone view -->
</div>
</div>
</body>
Javascript:
var exercise = {};
(function($){
exercise.Activity = Backbone.Model.extend({
});
exercise.Activities = Backbone.Collection.extend({
model: exercise.Activity,
url: "exercise.json",
comparator: function(activity){
var date = new Date(activity.get('date'));
return date.getTime();
}
});
exercise.ActivityListView = Backbone.View.extend({
tagName: 'ul',
id: 'activities-list',
attributes: {"data-role": 'listview'},
initialize: function() {
this.collection.bind('add', this.render, this);
this.template = _.template($('#activity-list-item-template').html());
},
render: function() {
var container = this.options.viewContainer,
activities = this.collection,
template = this.template,
listView = $(this.el);
$(this.el).empty();
activities.each(function(activity){
var renderedItem = template(activity.toJSON()),
$renderedItem = $(renderedItem); //convert the html into an jQuery object
$renderedItem.jqmData('activityId', activity.get('id')); //set the data on it for use in the click event
$renderedItem.bind('click', function(){
//set the activity id on the page element for use in the details pagebeforeshow event
$('#activity-details').jqmData('activityId', $(this).jqmData('activityId')); //'this' represents the element being clicked
});
listView.append($renderedItem);
});
container.html($(this.el));
container.trigger('create');
return this;
}
});
exercise.ActivityDetailsView = Backbone.View.extend({
//since this template will render inside a div, we don't need to specify a tagname
initialize: function() {
this.template = _.template($('#activity-details-template').html());
},
render: function() {
var container = this.options.viewContainer,
activity = this.model,
renderedContent = this.template(this.model.toJSON());
container.html(renderedContent);
container.trigger('create');
return this;
}
});
exercise.initData = function(){
exercise.activities = new exercise.Activities();
exercise.activities.fetch({async: false}); // use async false to have the app wait for data before rendering the list
};
}(jQuery));
$('#activities').on('pageinit', function(event){
var activitiesListContainer = $('#activities').find(":jqmData(role='content')"),
activitiesListView;
exercise.initData();
activitiesListView = new exercise.ActivityListView({collection: exercise.activities, viewContainer: activitiesListContainer});
activitiesListView.render();
});
$('#activity-details').on('pagebeforeshow', function(){
console.log('activityId: ' + $('#activity-details').jqmData('activityId'));
var activitiesDetailsContainer = $('#activity-details').find(":jqmData(role='content')"),
activityDetailsView,
activityId = $('#activity-details').jqmData('activityId'),
activityModel = exercise.activities.get(activityId);
activityDetailsView = new exercise.ActivityDetailsView({model: activityModel, viewContainer: activitiesDetailsContainer});
activityDetailsView.render();
});
This seems to be an issue that's scuppered a few people. 1.3.0 makes things like the parameter passing plugin break, so there must have been a change in the networking code somewhere.

Categories

Resources