passing model´s array to an underscore view - backbone.js

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

Related

Marionette layout view -- why is a template necessary

In spite of reading the marionette docs several times over, I am still not able to fully comprehend some aspects of it correctly.
I am creating a layout view 'AppLayout' as below:
var AppLayoutView = Marionette.LayoutView.extend({
regions: {
headerRegion: "#ecp_header",
bodyRegion: "#ecp_layout_region"
},
...
The html snippet for my app is having the two dom nodes for above defined regions:
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">
<div class="row" id="ecp_body">
...
in app.js, my calling code is like this..
ECPApp.on('start', function() {
require(['controller_cp', 'header_view'], function(ControllerCP, HeaderView) {
console.log("On start event executing...");
// create a event aggregator vent object and attach to app.
ECPApp.vent = new Backbone.Wreqr.EventAggregator();
var appLayoutView = new AppLayoutView();
appLayoutView.render();
//appLayoutView.showLayout();
//$('div.toolbar > ul > li:first > a').tab('show');
if (Backbone.history) Backbone.history.start();
});
This gives me error Cannot render the template since it is null or undefined.
I thought that the default render() behavior of layout always looks for a template, so I rolled out my own version of render, as below:
render: function() {
var $self = this;
/* if no session exists, show welcome page */
var promise = ECPApp.request('entities:session');
promise.done(function(data) {
if (data.result==0) {
console.log('Valid session exists. Showing home page...!');
$self.showHome();
} else {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
}
}).fail(function(status) {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
});
return $self;
},
showWelcome: function() {
var self = this;
require(['header_view', 'welcome_view'],
function(HeaderView, WelcomeView) {
var headerView = new HeaderView();
var welcomeView = new WelcomeView();
self.bodyRegion.show(welcomeView);
});
}
This time, I get another error saying, An "el" #ecp_layout_region must exist in DOM. However I am sure that the element is existing in the DOM, as I can see it by checking in the debug console window. Running $('#ecp_layout_region') shows a valid element.
Marionette layout view is pretty confusing. Going forward I need multiple nested views. I am stuck here.
How is your template located? Is your template wrapped by <script type = “text/template”> tag?
It may look like this:
Inside your html, in head section:
<script type = “text/template” id="yourLayout">
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">...</div>
</script>
And in Layout definition:
var AppLayoutView = Marionette.LayoutView.extend({
template: '#yourLayout'
...
});

my backbone sample is not working and giving with this.el.html is not a function

I am new bee to backbone and i am trying with a below sample
http://jsfiddle.net/naveencgr/L3orucjm/
While loading i am getting this.el.html is not a function, let me know what is the cause of it.
HTML:
<div id="container"></div>
<script type="text/template" id="template">
<input type="text" name="Name" id="Name"></input>
<input type="button" name="Add" id="Add" value="Add"></input>
<div id="list"></div>
</script>
JavaScript:
NameModel = Backbone.Model.extend({
});
var nameModel = new NameModel();
nameModel.set('name','test');
alert(nameModel.get('name'));
NameView = Backbone.View.extend({
tagName:"li",
render: function(){
var template=_.template("<%=name%>", nameModel.toJSON());
return template;
}
});
var nameView = new NameView();
NameViewList = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template($("#template").html(), {});
this.el.html(template);
},
events : {
"click input#Add" :"addName",
},
addName : function() {
var name = $("#Name").val();
if (name.trim().length > 0) {
//nameModel.set("name", name);
$("#list").append(nameView.render());
}
}
});
var nameViewList = new NameViewList({el : $("div#container")});
You have numerous errors in your code. You know about one of them but not the others.
The one you know about is:
this.el.html(template);
A view's el is just a DOM node and that doesn't have an html function. The html function you're trying to use is part of jQuery so you'd want to call it on this.$el (which is just a cached version of $(this.el)):
this.$el.html(template);
Other problems:
Your fiddle is missing vars all over the place; don't say:
NameModel = ...
say:
var NameModel
to avoid accidental globals.
Your NameView is strange. It has tagName: 'li' so presumably it should be creating list elements but the render doesn't do anything with the view's el, it just returns a string of HTML that ends up inside a <div>. That <div> should be a <ul>:
<ul id="list"></ul>
A render function generally populates the view's el and, to allow chaining, returns this:
render: function() {
var template = _.template('<%= name %>');
this.$el.html(template(nameModel.toJSON()));
return this;
}
You are using Underscore's _.template incorrectly. You used to be able to say:
var h = _.template(template_source, data);
to compile the template and fill it in in one step but as of Underscore 1.7, the second argument to _.template is an options object. Now you need to compile and fill in the template in separate steps:
var t = _.template(template_source);
var h = t(data);
You'll see this change in the render above.
The way you're using your NameView is strange. Apparently you are trying to use one NameView to handle multiple names, this would work with your strange NameView#render implementation but it will fall apart once NameView has anything to do or once NameView is updated (as above) to be more conventional. You should create one NameView for each name you're displaying and each NameView should have its own NameModel as its model property. This would make NameView#render look like:
render: function() {
var template = _.template('<%= name %>');
this.$el.html(template(this.model.toJSON()));
return this;
}
and NameViewList#addName would look like:
addName: function() {
var name = this.$("#Name").val();
if(name.trim().length > 0) {
var nameView = new NameView({
model: new NameModel({ name: name })
});
this.$('#list').append(nameView.render().el);
}
}
You'll note that we're using NameView#render's new return value, this x.append(v.render().el) pattern is quite common and idiomatic in Backbone so it is a good practice. You should also notice that the search for #list is now limited to the view's el by using the view's this.$ function; this.$('#list') is equivalent to this.$el.find('#list') and doing things this way helps you keep your views self-contained.
In real life you'd probably put your new NameModel({ name: name }) instances in a collection somewhere and events on that collection would trigger the creation of new NameViews.
Applying all that to your fiddle gives you this functional and more idiomatic version:
http://jsfiddle.net/ambiguous/8x0ma9qo/

Updating specific template in nested templates

I'm trying to render nested view.
Say I have views BigView and SmallView
<script type="text/template" id="big-template">
<% _.each(smallViews, function (smallView) { %>
<%= smallView.$el.html() %>
<% }); %>
</script>
<script type="text/template" id="small-template">
<div>
<%- name %>
</div>
</script>
I call render from the bigView and it renders everything fine, including its children smallViews.
How do I update (render) a smallView individually without rendering the bigView?
I suggest that you don't call view methods from your template code. I would move the render code for a smallview to the bigview render method.
Which would look like the following:
var BigView = Backbone.View.extend({
render:function() {
for (var i=0;i<this.collection.length;i++) {
var smallView = new SmallView({model:this.collection.at(i)});
this.$el.append(smallView.render().el);
}
return this;
}
});
Then your SmallView:
var SmallView = Backbone.View.extend({
template:_.template($('#smallTemplate').html()),
render:function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
});
Here is a fiddle that shows it in action with an event handler on smallView to re-render.
Just calling a properly defined render method of the smallView object should be enough. For example something like this.
// smallview file
var SmallView = Backbone.View.extend({
// some other methods
template: _.template($("#small-template").html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
// where you need to update the nested views
_.each(smallViews, function(smallView {
smallView.render();
}

Backbone view's "delete" button is deleting all elements from the collection as opposed to the targeted element only

EDIT1: I have tried many different strategies for deleting the element including this.model.destroy(), this.collection.remove(this.model), etc. I have also bound the events using the events: syntax and using a direct binding strategy in both initialize and render.
If I change the code to use this.collection.add instead of remove it adds an additional X notes where X is the number of notes currently in the collection.
I am writing a basic list app (to be fleshed out eventually) and I want each element in the list to have a button that will delete it from the list.
I am using
require.js
backbone
lo-dash
jquery
My code snippets below show my view code for the Notes View, Note View and some supporting into including the templates being used to render each.
At the moment, the code is not functioning as desired because clicking ANY Note's "hide" button causes all elements in the collection to be removed one at a time. I know that is what is happening because I can insert alerts at the end of the "deletenote" method which allows me to view the deletion piece-wise.
I also know that the parent render method is not the cause of the problem because I can turn off the event callback to re-render the parent NotesView and all Note views are still deleted.
<script type="text/template" id="noteslist-template">
<ul id="noteslist" style="list-style: none;">
</ul>
</script>
<script type="text/template" id="note-template">
<li>
<button type="button" class="notesbutton" id="hidenote">hide</button>
<div id="note"><%= text %></div>
</li>
</script>
NotesView.js
define
([ 'jquery',
'underscore',
'backbone',
'notes/sample/sampletext', //included to populate a collection for rendering *REMOVE ONCE DONE TESTING*
'notes/collections/Notes', //included to create a new instance of the Notes collection during the initialize method of this view
'notes/models/Note', //included to reference the model when creating NoteView instances for rendering
'notes/views/NoteView' ], //included to call render functions on each Note model in the collection
function($,_,Backbone,SampleText,Notes,Note,NoteView)
{
var NotesView = Backbone.View.extend
({
initialize: function()
{
this.template = _.template($('#noteslist-template').html());
_.bindAll(this,'render','rendernote');
this.collection.bind('add',this.render);
this.collection.bind('remove',this.render);
},
render: function()
{
console.log('collection render');
this.$el.html(this.template({})); //change to this.notelist = THISLINE
this.collection.each(this.rendernote);
//add call to render notelist to DOM
return this;
},
rendernote: function(note) //add notelist variable
{
console.log('collection rendernote');
var noteview = new NoteView(
{ model:note,
collection:this.collection} );
//add notelist += LINEBELOW
noteview.setElement(this.$('#noteslist'));
noteview.render();
//change noteview.render to NOT write to DOM
}
});
return NotesView;
}
);
NoteView.js
define
( [ 'jquery',
'underscore',
'backbone',
'notes/models/Note', ], //include the Note model to reference as the model for the collection
function($,_,Backbone,Note)
{
var NoteView = Backbone.View.extend
({
tagName: "li",
className: "note",
events:
{
'click #hidenote':'deletenote',
},
initialize: function()
{
_.bindAll(this,'render','remove','deletenote');
//this.model.bind('change',this.render);
this.template = _.template($('#note-template').html());
this.model.bind('remove', this.remove);
},
render: function()
{
this.notetorender = this.template(this.model.toJSON());
this.$el.append(this.notetorender);
return this;
},
deletenote: function()
{
this.options.collection.remove(this.model);
}
});
return NoteView;
}
);
noteview.setElement(this.$('#noteslist')); causes event delegation.
So eventhough you wrote 'click #hideNotes : deleteNode' inside NoteView, after the setElement call it works like that code is present inside NotesListView.
So when you click hide button of single li and expect only that li to be removed from ul however the click event is received by all li's hide button.That's why all items in collection are deleted .
JsFiddle here i do the same thing without using setElement
var NoteView = Backbone.View.extend({
events: {'click #hidenote': 'deletenote'},
initialize: function(){
this.model.on('destroy', this.$el.remove)
},
deletenote: function(){
this.model.destroy()
}
})

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