Can I pass a global var to an underscore view? - backbone.js

I wonder if possible to do pass a global variable when rendering a template. Basically I get this variable each time I call a controller, so it looks like this:
window.myVar = 0;
//this var will change in a given moment when I make a request to the server.. so:
//into the render method of my view, I have something like this:
var template = _.template($("#myTemplate").html(), { varIwantToPass : myVar } );
this.$el.html(template);
this way I can access it into the template with something like this:
<%= varIwantToPass.get('myVar') %>
if this possible?; and also, each time a render the view, this code will excute again and update the value?

Yes, except you template has to be
<%= varIwantToPass %>
and pass variable as window.myVar so you would not accidentally replace it
And yes it will update after each render if you pass the variable each time
Working example:
Html:
<script id="myTemplate" type="text/html">
<%= varIwantToPass%>
</script>
<div></div>
JS:
window.myVar = 'a';
var templateHtml = $("#myTemplate").html()
var render = function () {
var template = _.template(templateHtml, { varIwantToPass : window.myVar } );
return template;
}
$('div').html(render());
window.myVar = 'b'; //change variable
setTimeout(function() {
$('div').html(render());
}, 1000)
http://jsfiddle.net/omynhL1d/
However I would advocate on not using global variable and instead saving it somewhere in your backbone view or even better a model and then render by listening on that model change event

Related

AngularJS - Initialize form data based on value

I am trying to add edit functionality to my app. In one view, I have a button that brings you to the edit page.
<button ng-click="editMission(selectedMission.key)">Edit Mission</button>
The value selectedMission.key is used to determine what to initialize the edit page's form data with.
In the controller the function looks like this:
$scope.editMission = function(key){
$location.path('/edit');
}
On the edit page I have:
<div data-ng-init="editInit()">
And in my controller I have:
$scope.editInit = function(){
var query = myDataRef.orderByKey();
query.on("child_added", function(missionSnapshot){
if (missionSnapshot.key()==key){
...
}
});
}
How can I run the initialize function based on the key value from editMission. Should I use some getter/setter approach with a global key variable? I tried just placing the editInit code in editMission but the form data does not populate on view load.
Common practice is to use a service to share variables between views/controllers.
So in your case you would use the getter/setter approach as you suspected. I don't know what exactly you're trying to do, but the service in your case would look something like this:
app.factory('missionKeyService', function() {
var currentMission= {};
return {
setMissionKey: function(missionKey) {
currentMission.key = missionKey;
},
getMissionKey: function() {
return currentMission.key;
}
}
})
And in your controller1:
//include 'missionKeyService' in your controller function params
$scope.editMission = function(key) {
missionKeyService.setMissionKey(key);
$location.path('/edit');
}
And controller2:
//include 'missionKeyService' in your controller function params
$scope.editInit = function() {
var currentKey = missionKeyService.getMissionKey();
//do something with this key
...
}

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);
}
});

Add custom property to every item of a collection

I'm trying to add a custom property to every item of a collection, but it doesn't show up in the template.
I have a lot of quotations which have a client_id. Now I want to fetch the client by the client_id and add it to the collection entry. In general, it works when inspecting the populated object with console.log, but it doesn't show up in the template.
That's how I tried it:
sprocket.QuotationsView = Backbone.View.extend({
id: 'content-inner',
initialize: function(options) {
// instantiate Collection
this.collection = new Quotations();
// compile Handlebars template
this.tpl = Handlebars.compile(this.template);
},
render: function() {
var self = this;
var obj = this.el;
// get quotations and set data to handlebars template
$.when(this.collection.fetch()).then(function(quotations) {
$.each(quotations, function(i, quotation) {
var loadContact = new Contact({id: quotation.contact_id}).fetch();
$.when(loadContact).then(function(contact) {
quotations[i]['contact'] = contact;
});
});
$(obj).html(self.tpl(quotations));
// if response is empty (no quotations in database), set empty template
}, function() {
$(obj).html(self.tpl);
});
return this;
}
});
My template looks like this:
<div>
{{#if .}}
{{#each .}}
{{number}} <!-- this works -->
{{contact}} <!-- this doesn't work -->
{{contact.name}} <!-- this doesn't work too -->
{{/each}}
{{/if}}
</div>
That is because the callback that actually changes the data inside the Quotation.attribute.contact (ie. your quotations[i]['contact'] = contact; line) is being executed after fetching the Contact which happens to be after the template is being rendered.
$.each(quotations, function(i, quotation) {
var loadContact = new Contact({id: quotation.contact_id}).fetch();
// This is the callback that is being executed after the backend responds
$.when(loadContact).then(function(contact) {
quotations[i]['contact'] = contact;
});
});
// This is the template rendering which is before the server has time to respond
$(obj).html(self.tpl(quotations));
Render instead the template after all Contacts are being fetched and added to Quotations.
A quick way to solve this:
Make the loop which load all the Contacts inside a function that contains a callback.
Call the callback after all Contacts have loaded.
The callback should render the template.
This is a personal opinion and in no way an answer to this question: I don't like the data logic with the backend and the view rendering and logic in the same class. I use Backbone.Marionette to split the View and the Controller in two different entities loosely coupled by events. Only the Controller is aware of the View and only the View is aware of the DOM.

Backbonejs: model not getting passed in underscore template

I am new to backbonejs. What I am trying to do is, render a template on page load and pass model as data parameter in _.template function. Here is my bacbone code:
var Trip = Backbone.Model.extend({
url: '/trips/' + trip_id + '/show'
});
var InviteTraveller = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var trip = new Trip();
trip.fetch({
success: function(){
console.log(trip); //logs trip object correctly
var template = _.template($('#invite-traveller-template').html(), {trip: trip});
that.$el.html(template);
}
});
}
});
var Router = Backbone.Router.extend({
routes: {
'': 'fetchTrip'
}
});
var inviteTraveller = new InviteTraveller();
var router = new Router();
router.on('route:fetchTrip',function () {
inviteTraveller.render();
});
Backbone.history.start();
And here is my sample template:
<script type="text/template" id="invite-traveller-template">
<h3>Trip</h3>
<h3><%= trip.get('name') %></h3>
</script>
On running, I am getting the this in browser window and console shows:
trip is not defined
I am facing this issue since yesterday but could not figure out the solution yet. Not understanding what is going wrong, code also seems to be right. Any help would be greatly appreciated.
Update:
I removed
inviteTravellers.render();
from router.on() and then reloaded the page in browser. I still got same error which means that <script></script> (template) is being compiled before calling render() of InviteTraveller view. What can be the possible reason for this?
I had the same issue (underscore v1.8.2). My fix:
var template = _.template($('#invite-traveller-template').html());
var compiled = template({trip: trip});
that.$el.html(compiled);
You're passing the whole model to the template. Typically you would call model.toJSON and then pass its result to the template. Additionally using <%= in your template to render the attribute, which is meant for interpolating variables from that JSON object you're passing.
You can pass a whole model to the template and use <% ... %> to execute pure Javascript code and use print to get the attribute but it's probably overkill.
Have a look at this fiddle.
You code work perfectfly, here's it
I think that your problem came from another code, not the one you have posted, because there's no way for your view to render if you remove :
inviteTravellers.render();
Try to chaneg <h3><% trip.get('name'); %></h3> by <h3><%= trip.get('name') %></h3>
My code seems to be right but still my template was getting compiled on page load and I was getting trip is not defined error. I did not understand the reason of this behavior yet.
I solved this issue by using handlebarsjs instead of default underscore templates.

using underscore variables with Backbone Boilerplate fetchTemplate function

I am building an application using the Backbone Boilerplate, and am having some trouble getting underscore template variables to work. I have a resource named Goal. My Goal View's render function looks like this:
render: function(done) {
var view = this;
namespace.fetchTemplate(this.template, function(tmpl) {
view.el.innerHTML = tmpl();
done(view.el);
});
}
I'm calling it inside of another view, like so:
var Goal = namespace.module("goal");
App.View = Backbone.View.extend({
addGoal: function(done) {
var view = new Goal.Views.GoalList({model: Goal.Model});
view.render(function(el) {
$('#goal-list').append(el);
});
}
});
I'm using local storage to save my data, and it's being added just fine. I can see it in the browser, but for some reason, when I load up the app, and try to fetch existing data, i get this error:
ReferenceError: Can't find variable: title
Where title is the only key I'm storing. It is a direct result of calling:
tmpl();
Any thoughts are greatly appreciated.
Your template is looking for a variable title, probably like this <%- title %>. You need to pass it an object like this tmpl({ title: 'Some title' })
Turns out, I wasn't passing in the model when i created the view, which was making it impossible to get the models data. Once I passed in the model correctly, I could then pass the data to tmpl, as correctly stated by #abraham.
render: function(done) {
var
view = this,
data = this.model.toJSON();
clam.fetchTemplate(this.template, function(tmpl) {
view.el.innerHTML = tmpl(data);
done(view.el);
});
},

Resources