Alright people,
I am using the RequireJs text plugin to pull in underscore templates (.html) in my backbone application. Unfortunately my underscore code in my templates are being rendered as plain text.
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html(), {posts : this.collection});
this.render();
},
render: function (Template) {
this.$el.html(this.template);
return this;
}
});
return BlogPostIndexView;
});
Here is my code for my view, you can see i am pulling-in two templates and setting them. However it getting rendered as ...
Globall Coach Blog Posts
<% _.each(posts, function(post){ %>
<%= _.escape(post.title) %>
<% }); %>
Has anybody ever had this issue?
Seems like you're not using the underscore template function correctly. Underscore compiles the string into a function in which you can pipe in data. so your code should look like this:
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html())({posts : this.collection});
this.render();
},
render: function () {
this.$el.html(this.template);
return this;
}
});
return BlogPostIndexView;
But I would refactor this further because you usually want to dynamically re-render with the latest data so I would put the piping in the data to the template in the "render" method instead of "initialize".
So preferably I would do this:
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html())
this.render();
},
render: function () {
this.$el.html(this.template({posts : this.collection}));
return this;
}
});
return BlogPostIndexView;
Turns #mu-is-to-short was correct, requireJs text module returns the the raw html.
Here it `define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template(Template);
},
render: function (Template) {
this.$el.html(this.template({posts : this.collection.toJSON()}));
return this;
}
});
return BlogPostIndexView;
});
Related
I have a view which is a Marionette ItemView. I would like to access other parameters on this view using "this" inside the template function but I get it as undefined and I'm unsure why.
define(['jquery', 'hbs!templates/template', 'backbone'],
function ($, template, Backbone) {
"use strict";
return Backbone.Marionette.ItemView.extend({
name: "Depth",
el: ".card",
template: function(serializedModel){
var self = this; // self is undefined, so I can't reference this.name, which would be Depth
var data = {isDepth: true, cardTitle: self.name, injectHTML: template()};
.... do some stuff ...
return template();
}
});
}
);
You can use templateHelpers to access custom variables in the template:
templateHelpers:function(){
return {
card_title: this.name
}
}
You can use underscore's bindAll to bind template to your marionetteItem View's this whenever its called . So something like :
Backbone.Marionette.ItemView.extend({
initialize: function(){
_.bindAll(this, 'template');
},
template: function() {
//this refers to the parent object scope.
}
});
I'm a beginner in Backbone trying to get a sample page up and running with multiple instances of a Carousel. Here's how my HTML looks like
<div class="en-carousel" id="one" data-href="/js/models/json/carousel.json" data-content-id=""></div>
<div class="en-carousel" id="two" data-href="/js/models/json/carousel2.json" data-content-id=""></div>
<div class="en-carousel" id="three" data-href="/js/models/json/carousel3.json" data-content-id=""></div>
The data is fetched using the Model and the render method of view iterates over the 'el' object of my View object. The model fetch is associated with a success handler which initiates the carousel. The controller code looks like
define([
'jquery',
'backbone',
'handlebars',
'text!view/carousel.html',
'plugin/jquery.bxslider.min',
'TPL'
], function($, Backbone, Handlebars, carouselTemplate, $bx, TPL){
var CarouselModel = Backbone.Model.extend({
setURL: function(url){
this.url = url;
}
});
var CarouselView = Backbone.View.extend({
el: '.en-carousel',
model: new CarouselModel(),
initialize: function () {
},
render: function () {
var that = this;
this.$el.each(function(){
//that.model.url = $(this).data('href');
that.model.setURL($(this).data('href'))
that.listenTo(that.model,'change', that.onSuccess);
that.model.fetch();
});
},
onSuccess: function(response, status, xhr){
var tpl = window["ENX"]["templates/handlebars/carousel.html"](this.model.get('response'));
$(this.el).append(tpl);
$(this.el).find('ul.carousel').bxSlider();
},
ajaxFail: function(){
alert(2)
}
});
return CarouselView;
});
I'm using Handlebars for templating. The problem here is the listenTo event is fired n*n times for n instances. So for 3 carousel I'm getting 9 carousels in total and all of them are being appended to div#one
The solution is simple: replace the entire content of el when the model changes.
initialize: function () {
this.model.fetch().done(function () {
this.render();
this.listenTo(this.model,'change', this.render.bind(this));
}.bind(this));
},
render: function () {
var tpl = window["ENX"]["templates/handlebars/carousel.html"](this.model.get('response'));
this.$el.html(tpl);
this.$el.find('ul.carousel').bxSlider();
}
There is also no need to iterate over el because it is just a single element.
In my view.js I am calling the wine-list-tpl (template), as below you can see this. But nothing is showing to the index page. Please help.
IN index.html
<script type="text/template" id="wine-list-tpl">
Hello world
<%= name %>
</script>
.......
<div class="wine-list"></div>
IN view.js
var WineLists = Backbone.View.extend({
el:'.wine-list',
template : _.template($('#wine-list-tpl').html()),
render: function(){
console.log("rendering is ok");
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
var WineList = Backbone.View.extend({
model:wines,
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
$('#wine-list').append((new WineLists({ model : wine })).render().$el);
});
}
});
var wineListView = new WineList();
I have added some data in to my local storage and i just want to show those data to that particular div element.
In WineLists view i am getting all those data. means when i am writing, console.log(this.model.get('name')); I got my desired data, but i only want to show those data through template to that particular div. what should i do, please suggest.
var WineLists = Backbone.View.extend({
model: new Wine(),
template : _.template($('#myTpl').html()),
render: function(){
//console.log(this.model.get('name'));
this.$el.html(this.template(this.model.toJSON()));
return this;
} });
var WineList = Backbone.View.extend({
model:wines,
el:'.wineDiv',
initialize: function(){
var self=this;
wines.fetch();
_.each(this.model.toArray(), function(wine, i){
self.$el.append((new WineLists({ model : wine })).render().$el);
});}});
var wineListView = new WineList();
//please check ur DOM manipulation i think there is some error in ur HTML try to change call //sequence
This is how your code should have been for expected results.
var WineLists = Backbone.View.extend({
el: '.wine-list',
render: function () {
var _self = this;
this.collection.each(function (model) {
var view = new WineListItemView({
model: model
})
view.render();
view.$el.appendTo(_self.el);
})
return this;
}
});
var WineListItemView = Backbone.View.extend({
template: _.template($('#wine-list-tpl').html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var wines = new Backbone.Collection([{
name: 'wine1'
}, {
name: 'wine2'
}])
var wineListView = new WineLists({
collection: wines
});
wineListView.render()
I've created my own version of what is basically: todomvc dependency-example, but I built it from looking at this Modular Backbone Example. I'm trying to move hardcoded html in the base template to it's own template file. I plan on creating multiple pages and want a minimal base template. However, when I load and insert the template in the view, the keypress event for createOnEnter stops working. Every other feature still works, which includes the other event listed in events (clearCompleted).
See: this.$el.append(notesTemplate);.
The browser never makes it to the function createOnEnter().
My app view:
define([
'jquery',
'underscore',
'backbone',
'models/notes/NoteModel',
'collections/notes/NoteCollection',
'views/notes/NotesListView',
'text!templates/notes/statsTemplate.html',
'text!templates/notes/notesTemplate.html'
], function($, _, Backbone, NoteModel, NoteCollection, NotesListView, statsTemplate, notesTemplate){
'use strict';
var NotesView = Backbone.View.extend({
el: $("#page"),
events: {
"keypress #new-note": "createOnEnter",
"click #clear-completed": "clearCompleted"
},
initialize: function() {
var onDataHandler = function(collection) {
this.render();
}
this.$el.append(notesTemplate);
this.model = new NoteCollection();
this.model.fetch({ success : onDataHandler, dataType: "jsonp"});
this.input = this.$("#new-note");
this.allCheckbox = 0;
this.listenTo(this.model, 'add', this.addOne);
this.listenTo(this.model, 'reset', this.addAll);
this.listenTo(this.model, 'all', this.render);
this.footer = this.$('footer');
this.main = $('#main');
this.model.fetch();
},
render: function() {
var done = this.model.done().length;
var remaining = this.model.remaining().length;
if (this.model.length) {
this.main.show();
this.footer.show();
this.$('footer').show();
this.footer.html(_.template(statsTemplate, {done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
this.allCheckbox.checked = !remaining;
},
addOne: function(note) {
var view = new NotesListView({model: note});
$("#notes-list").append(view.render().el);
},
addAll: function() {
this.model.each(this.addOne);
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
this.model.create({title: this.input.val()});
this.input.val('');
},
clearCompleted: function() {
_.invoke(this.model.done(), 'destroy');
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
this.model.each(function (note) { note.save({'done': done}); });
}
});
return NotesView;
});
Solved!
I didn't provide enough information for anyone to find the problem. It was a typo in the element with the ID #new-note. The above code works just fine.
I accomplished loading templates like this by setting the template option in the view like this:
template: _.template(notesTemplate),
and then in my render function calling:
this.$el.html(this.template());
to actually render it. This ensures the events get delegated properly.
I have a backbone view which load subview. When I load a subview, I would like to show a loader when the view fetch needed datas and hide the loader when the view is ready to render.
I did something like this :
var appView = Backbone.View.extend({
showLoader: function() {
// Code to show loader
},
hideLoader: function() {
// Code to hide loader
},
loadSubView: function() {
this.showLoader();
var myView = new MySubView();
this.$el.html(myView.render().el);
this.hideLoader();
}
});
For now, my sub-view load a collection and is implemented like this :
var mySubView = Backbone.View.extend({
initialize: function() {
this.myCollection.fetch({
async: false
});
},
render: function() {
// Code to render
}
});
My sub view load the collection synchronously because it is the only way I found to know when my view is "ready" to render but I think this is not the best way to use Backbone.
What schould I do ?
There are several ways to do it.
You can explicitly use the pubsub pattern. Something like this:
var AppView = Backbone.View.extend({
showLoader: function() {
console.log('show the spinner');
},
hideLoader: function() {
console.log('hide the spinner');
},
loadSubView: function() {
this.showLoader();
var subView = new SubView();
subView.on('render', this.hideLoader);
this.$el.html(subView.render().el);
}
});
var SubView = Backbone.View.extend({
render: function() {
console.log('a subView render');
this.trigger('render');
return this;
}
});
var appView = new AppView({el: $('body')});
appView.loadSubView();
http://jsfiddle.net/theotheo/qnVhy/
You can attach a function to the ajaxStart/ajaxStop events on the
spinner itself:
var AppView = Backbone.View.extend({
initialize: function() {
var _this = this;
this.$('#spinner')
.hide()
.ajaxStart(_this.showLoader)
.ajaxStop(_this.hideLoader);
}
...
}
Or you can use jQuery.ajaxSetup:
var AppView = Backbone.View.extend({
initialize: function() {
var _this = this;
jQuery.ajaxSetup({
beforeSend: _this.showLoader,
complete: _this.hideLoader,
success: function() {}
});
}
...
}