Why my Marionette itemView does not render? - javascript

I'm using Marionette with Handlebars templates and I can't get my itemView to render inside a CollectionView.
Here is the CollectionView code:
define( [ 'App', 'marionette', 'handlebars', 'models/Model', 'collections/Collection', 'text!templates/welcome.html'],
function(App, Marionette, Handlebars, Model, Collection, template) {
//ItemView provides some default rendering logic
var ItemView = Marionette.ItemView.extend( {
//Template HTML string
template: Handlebars.compile(template),
//model: new Model(),
// View Event Handlers
events: {
},
initialize: function(o) {
console.log('init itemView');
}
});
return Marionette.CollectionView.extend( {
issues: new Collection(),
itemView: ItemView,
onRender: function() {this.issues.fetch()},
initialize: function(o) { console.log('init collectionView')}
});
});
here is the template
<div class="hero-unit">
<h1>Marionette-Require-Boilerplate Lite</h1>
<p>Lightweight Marionette Boilerplate application to get you off the ground fast.</p>
<p class="muted">
You are viewing this application on
</p>
<br/>
<table>
{{#each items}}
<tr><td>{{title}} - {{number}}</td></tr>
{{/each}}
</table>
<a class="btn btn-primary btn-large" href="https:github.com/BoilerplateMVC/">See more Boilerplates</a>
The only thing I get from this code is that the CollectionView does trigger its initialize method and that the collection is fetched from GitHub.

There are multiple reasons this could not be working, depending on the Marionette version you are using:
For the latest Marionette version, you have to use 'childView' instead of 'itemView'.
The items to display are expected in the property 'collection' not 'issues'.
example:
var IssuesView = Marionette.CollectionView.extend({
childView: IssueView,
onRender: function () {
this.collection.fetch();
},
initialize: function (o) {
console.log('init collectionView');
}
});
new IssuesView({'collection': new Backbone.Collection()});
Now, based on the code you provided, I assume your goal is to display the issues inside 'items', if that is correct, I will suggest to use a 'CompositeView' instead, and then you can provide a 'container' and render the 'issues' inside the items. For example:
var IssueView = Marionette.ItemView.extend({
itemTag: 'tr',
//Template HTML string
template: Handlebars.compile($("#item-template").html()),
//model: new Model(),
// View Event Handlers
events: {
},
initialize: function (o) {
console.log('init itemView');
}
});
var IssuesView = Marionette.CompositeView.extend({
childView: IssueView,
childViewContainer: "#issues",
template: Handlebars.compile($("#some-template").html()),
onRender: function () {
//this.issues.fetch();
},
initialize: function (o) {
console.log('init collectionView');
}
});
Where your templates are:
<script id="item-template" type="text/x-handlebars-template">
<td>
{{title}} - {{number}}
</td>
</script>
<script id="some-template" type="text/x-handlebars-template">
<div class = "hero-unit" >
<h1>Marionette - Require - Boilerplate Lite </h1>
<p>Lightweight Marionette Boilerplate ...</p>
<p class = "muted"> You are viewing this application on </p>
<br/>
<table id="issues">
</table>
</script>
Here is jsfiddle with a working version of this:
http://jsfiddle.net/gvazq82/v5yj6hp4/2/

Your problem is that you're not specifying a collection in your CollectionView. You want to instead do
var collectionView = Marionette.CollectionView.extend({
collection: new issues
...
});

Related

Save form data with handlebarsjs and marionettejs

I'm trying to save the form data to a server using a Deployd API; but when i click the save button, a new document it's created at the server but it contain no data. I don't understand how it can work; i guess i have to pass the data to the model to be saved? How do i do that? I thought that the view was already linked with the template and model.
I'm using Requirejs, MarionetteJS and Handlebars.
Here is the code.
MODEL:
define(['underscore','backbone'], function(_, Backbone){
var Student= Backbone.Model.extend({
urlRoot: '/students',
defaults: {
nameStudent: '',
lastnameStudent: ''
},
initialize: function(){
console.log('studentModel: initiated');
},
});
return Student;
});
VIEW:
define([
'jquery',
'underscore',
'backbone',
'marionette',
'handlebars',
'js/models/student',
'text!templates/forms/studentFormAdd.html'
], function($, _, Backbone, Marionette, Handlebars, studentModel, studentFormAddTemp){
var studentFormAdd = Backbone.Marionette.ItemView.extend({
model: studentModel,
template: Handlebars.compile(studentFormAddTemp),
events: {
'click #saveBtn': function(){
this.model.save();
console.log(this.model.toJSON());
console.log('saveBtn event: initiated');
}
},
initialize: function(){
console.log('studentFormAdd: initiated');
this.model = new studentModel();
this.model.on('change', this.render);
},
});
My template has the following sintax:
<div>
<form>
Student Name<br>
<input type="text" name="nameStudent" value="{{nameStudent}}" placeholder="Student name"/>
<br>
Student lastname<br>
<input type="text" name="lastnameStudent" value="{{lastnameStudent}}" placeholder="Student lastname"/><br>
</form>
</div>
Backbone doesn't use two way binding, so:
this.model.save();
in your saveBtn event handler is empty. If you want realtime two-way binding, you can use:
https://github.com/theironcook/Backbone.ModelBinder or https://github.com/NYTimes/backbone.stickit
If you wan't to simply consume the form data on submit and save it in a model, you can use https://github.com/marionettejs/backbone.syphon.
var data = Backbone.Syphon.serialize(this);
this.model.set(data);
this.model.save();

Backbone Marionette - Log Model in Template

I am wondering how to log the model used in a Backbone.Marionette CompositeView's template?
The <% console.log(model) %> I am using causes an error Uncaught ReferenceError: model is not defined
Is there a way to log the content of the model from the template?
Here is a fiddle to my code: http://jsfiddle.net/16L1hen4/
Here is my Template and JavaScript
Template:
<div id="main"></div>
<script type="text/template" id="tree-template">
<li><%- name %></li>
<% //This console.log fails and I don't know why %>
<% console.log('inside template model =',model) %>
</script>
JavaScript:
var App = new Backbone.Marionette.Application();
App.addRegions({
mainRegion: '#main'
});
var TreeModel = Backbone.Model.extend({
});
var TreeCollection = Backbone.Collection.extend({
model: TreeModel,
url: 'https://api.mongolab.com/api/1/databases/backbone-tree/collections/tree?apiKey=akey'
});
// Tree branch view
var CompositeView = Backbone.Marionette.CompositeView.extend({
tagName: 'ul',
template: _.template( $('#tree-template').html() )
});
// Tree root view
var CollectionView = Backbone.Marionette.CollectionView.extend({
tagName: 'div',
className: 'js-tree-root',
childView: CompositeView,
});
var treeCollection = new TreeCollection();
treeCollection.fetch().done(function () {
var collectionView = new CollectionView({collection: treeCollection});
App.mainRegion.show(collectionView);
});
Edit: In a regular Backbone.View I can pass the model to the data as a property. Notice in this example that the View's model is passed to the template with a data property which can be logged in the template. This could be useful in the template to test for the existance of properties on the model. I am not sure how to do this using a Marionette CompositeView
var jsonData = {
"color": "red"
};
var TheModel = Backbone.Model.extend({
});
var TheView = Backbone.View.extend({
el: '#main',
tagName: 'ul',
template: _.template( $('#the-template').html() ),
render: function () {
var tmpl = this.template({data: this.model.toJSON()});
this.$el.html( tmpl );
}
});
var theModel = new TheModel(jsonData);
var theView = new TheView({model: theModel});
theView.render();
<div id="main"></div>
<script type="text/template" id="the-template">
<li><%- data.color %></li>
<% //Log the current model that the template uses %>
<% console.log('template model =',data) %>
</script>
This happens to be related to the anwser I posted on your question here : Backbone Marionette Composite View Rendering Template
and is cause by the same issue. model being undefined in the template context.
The data passed to your template is the following:
{_id: Object, name: "Level 1", children: Array[2]}
and model is undefined in this context.
If you want to log/control the data passed to your template you have should use SerializeData in such a way:
var CompositeView = Backbone.Marionette.CompositeView.extend({
tagName: 'ul',
serializeData: function(){
var data = Backbone.Marionette.CompositeView.prototype.serializeData.apply(this, arguments);
console.log(data);
return data;
},
template: _.template( $('#tree-template').html() )
});

Marionette: childViewContainer was not found

I am trying to learn how to use Marionette with Backbone. I am not sure why I am getting the following error: Uncaught ChildViewContainerMissingError: The specified "childViewContainer" was not found: ul
Here's a fiddle to my code: http://jsfiddle.net/e7L822c8/
Here is my JavaScript:
window.App = new Backbone.Marionette.Application();
App.addRegions({
mainRegion: '.js-page'
});
App.start();
var TheModel = Backbone.Model.extend({});
var TheCollection = Backbone.Collection.extend({
model: TheModel,
});
var ListView = Backbone.Marionette.CompositeView.extend({
tagName: 'div',
className: 'js-list-container',
template: _.template( '#ListViewTemplate' ),
childViewContainer: 'ul',
childView: ItemView
});
var ItemView = Backbone.Marionette.ItemView.extend({
initialize: function() {
console.log('this.model =',this.model);
console.log(this);
},
tagName: 'li',
className: 'list-item',
template: _.template( '#ItemViewTemplate' )
});
var dataArray = [
{"name":"FirstObject"},{"name":"SecondObject"},{"name":"ThirdObject"}
];
var theCollection = new TheCollection(dataArray);
var listView = new ListView({collection: theCollection});
App.mainRegion.show(listView);
Here is my HTML:
<div class="js-page">
</div>
<script type="text/template" id="ListViewTemplate">
<h3>Here is a list</h3>
<ul class="js-list">
</ul>
</script>
<script type="text/template" id="ItemViewTemplate">
Display List Item here
</script>
There are two problems in the code :
you have to define ItemView before ListView in your js code
the js cant access the template in your code
template: _.template( '#ListViewTemplate' ),
if you replace ListViewTemplate by its content :
template: _.template( "<h3>Here is a list</h3><ul class='js-list'></ul>" ),
it works check the jsfiddle : http://jsfiddle.net/pwassqww/2/
so the problem is with your template definition .

underscore.js template || bind/append backbone

I asked question the other day on this app; after some good advice, I moved on and I now think this is a different issue.
Before, I was not getting any display on the screen/no errors or any console.logs. After working on it some more, I now have my model/view and some of my render function working.
I think the issue is with my template or with my append. Below is the full code as it stands now. There are //comments where I think there maybe some issues.
Any help with this would be greatly appreciated.
EDIT :: Thanks for the advice Niranjan. I made some the changes you mentioned; I took away the counter and sample data. With these new changes, my newsFeed.js is no longer being read and so I am unclear as to how to populate my collection. When I console.log out my collection I get an empty array with my defaults shown, but with the json file not being read in the first place how do I get anything to work?
EDIT#2 :: Thank you again Niranjan. With the changes you suggested and a few of my own, I now have the code below. The issue I have right now, Is my array is being populated far too many times. the JSON file has 8 entries in total and because of my _.each statement in my template it is looping 8 times where I only want it to loop once and then to split the array into separate entries. I tried first splitting it during my response parse but this didn't work, do you have any advice for this?
below the code is links to the live views of code and html/broswer content including a link to the JSON file.
My end goal is to click on one title and have the corresponding content show.
(function(){
var NewsFeedModel = Backbone.Model.extend({
defaults: {
title: '',
content: ''
}
});
var NewsFeedCollection = Backbone.Collection.extend({
model: NewsFeedModel,
url : 'newsFeed.js',
parse: function(response) {
console.log('collection and file loaded');
return response.responseData.feed.entries;
}
});
var NewsFeedView = Backbone.View.extend({
el : '.newsContainer ul',
template: _.template($("#feedTemp").html()),
initialize: function(){
var scopeThis = this;
_.bindAll(this, 'render');
this.collection.fetch({
success: function(collection){
scopeThis.render();
}
});
this.collection.bind( 'add', this.render, this);
console.log('View and Template read');
},
render: function () {
this.$el.html(this.template({
feed: this.collection.toJSON()
}));
console.log(this.collection.toJSON());
}
});
var newsFeedCollection = new NewsFeedCollection();
var newsFeedView = new NewsFeedView({
collection: newsFeedCollection
});
var title = newsFeedCollection.find('title');
var content = newsFeedCollection.find('content > title');
$(document).on("click", "#add", function(title, content) {
console.log("I have been clicked");
if($(title) == $(content)){
console.log('they match');
}
else{
console.log('they dont match');
}
$('.hide').slideToggle("slow");
});
}());
This is my underscore template.
<div class="span12">
<script id="feedTemp" type="text/template">
<% _.each(feed, function(data) { %>
<div id = "titleContent">
<%= data.title %>
<div id="content" class="hide">
<%= data.content %>
</div>
</div>
<% }); %>
</script>
</div>
I am using google drive as a testing ground; links for the full html/code.
https://docs.google.com/file/d/0B0mP2FImEQ6qa3hFTG1YUXpQQm8/edit [code View]
https://googledrive.com/host/0B0mP2FImEQ6qUnFrU3lGcEplb2s/feed.html [browser View]
https://docs.google.com/file/d/0B0mP2FImEQ6qbnBtYnVTWnpheGM/edit [JSON file]
There are lot more things in your code that can be improved.
Here is the JSFIDDLE.
Please go through the comments mentioned in the code.
For trying out things in Underscore's template, check Underscore's Template Editor.
Template:
<button id=add>Add</button>
<div class="newsConatiner">
<ul></ul>
</div>
<script id="feedTemp">
<% _.each(feed, function(data) { %>
<div id = "titleContent">
<h2> <%= data.title %> </h2>
<div id="content">
<%= data.content %>
</div>
</div>
<% }); %>
</script>
Code:
(function () {
var NewsFeedModel = Backbone.Model.extend({
//url: 'newsFeed.js',
defaults: {
title: '',
content: ''
}
});
var NewsFeedCollection = Backbone.Collection.extend({
model: NewsFeedModel,
url: 'newsFeed.js',
parse: function (response) {
console.log('collection and file loaded');
return response.responseData.feed.entries;
}
});
var NewsFeedView = Backbone.View.extend({
el: '.newsConatiner',
//template should not be inside initialize
template: _.template($("#feedTemp").html()),
initialize: function () {
_.bindAll(this, 'render');
this.render();
//ADD event on collection
this.collection.bind('add', this.render, this);
console.log('View and Template read');
},
/*
This initialize will fetch collection data from newsFeed.js.
initialize: function () {
var self = this;
_.bindAll(this, 'render');
this.collection.fetch({
success: function(collection){
self.render();
}
});
//ADD event on collection
this.collection.bind('add', this.render, this);
console.log('View and Template read');
},
*/
render: function () {
//This is how you can render
//Checkout how this.collection is used
this.$el.html(this.template({
feed: this.collection.toJSON()
}));
}
});
//Initialized collection with sample data
var newsCounter = 0;
var newsFeedCollection = new NewsFeedCollection([{
title: 'News '+newsCounter++,
content: 'Content'
}]);
//Created view instance and passed collection
//which is then used in render as this.collection
var newsFeedView = new NewsFeedView({
collection: newsFeedCollection
});
$('#add').click(function(){
newsFeedCollection.add(new NewsFeedModel({
title: 'News ' + newsCounter++,
content: 'Content'
}));
});
}());

BackboneJS - Fetch data from PHP file and display in View

I'm fetching some data from my MySQL database with a php file and now I want to display this data in my View by passing it through a Model with the json_encode method. So far I created a Router, a Model, a Collection (is it necessary?) and a View. When i console.log the collection in my View, I can see that the data is actually there but my View shows nothing. When i console.log the Model I get the "undefined" message. So it seems that the Model is not instantiated, but I dont really know how to solve it. I use RequireJS and the HandlebarsJS for HTML templating purpose.
So here is my Router.
define(['backbone',
'views/firstpage',
'views/secondpage',
'views/thirdpage',
'collections/BandCollection']),
function( Backbone,FirstpageView, SecondpageView, ThirdpageView,BandCollection ) {
var Router = Backbone.Router.extend({
routes: {
'': 'index',
'firstpage' : 'firstpage',
'secondpage' : 'secondpage',
'thirdpage' : 'thirdpage'
},
initialize: function () {
this.bandCollection = new BandCollection();
this.bandCollection.fetch({
error: function () {
console.log("error!!");
},
success: function (collection) {
console.log("no error");
}
});
},
thirdpage: function() {
var thirdpageView = new ThirdpageView({ el:'#topContent', collection:this.bandCollection}).render();
},
});
return Router;
}
);
My Model looks like this:
define([
"jquery",
"backbone"
],
function($, Backbone) {
var BandModel = Backbone.Model.extend({
url: "../metal/db/bands.php",
defaults: {
"id": '',
"band": '',
"label": ''
}
});
return BandModel;
});
My Collection:
define([
"backbone",
"models/BandModel"
],
function(Backbone, BandModel) {
var BandCollection = Backbone.Collection.extend({
model: BandModel,
url: "../metal/db/bands.php"
});
return BandCollection;
});
My HTML template:
<div>
<p><%= id %></p>
<p><%= band %></p>
<p><%= label %></p>
</div>
And My View looks like this:
define(['backbone','handlebars', 'text!templates/Thirdpage.html'],
function(Backbone,Handlebars, Template) {
'use strict';
var ThirdpageView = Backbone.View.extend({
template: Handlebars.compile(Template),
initialize: function () {
_.bindAll(this, 'render');
this.render();
},
render: function() {
console.log(this.collection);
this.$el.html(this.template(this.collection.toJSON()));
return this;
}
});
return ThirdpageView;
}
);
As said before, the console.log(this.collection) tells me that the data is available..
{length: 6, models: Array[6], _byId: Object, constructor: function, model: function…}
but console.log(this.model) gives me "undefined" - and the View actually displays the HTML mentioned before and not the data, meaning it actually shows
<div>
<p><%= id %></p>
<p><%= band %></p>
<p><%= label %></p>
</div>
So, can anyone help me out? I'm out of ideas...
Change your render() method in your view like this:
render: function() {
var self = this;
console.log(this.collection);
self.collection.each(function(model){
console.log(this.model); // can view all models here
self.$el.append(self.template({id:model.get('id'),band:model.get('band'),label:model.get('label')}));
});
return this;
}
Change your Template like this:
<div>
<p>{{id}}</p>
<p>{{band}}</p>
<p>{{label}}></p>
</div>

Categories