backbone groupby collection is not rendered - backbone.js

I still struggling with render my grouped collection. In my console I can see collection with error. I wanna render each category name and each items in this category. Thanks for opinions !!! This will be very helpful....
There is my whole code :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone test</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<header>
</header>
<content>
<div class="jumbotron">
<div class="container">
<h1>My Items!</h1>
</div>
</div>
<div id="items"></div>
</content>
<footer>
</footer>
<script id="allItemTemlate" type="text/template">
<ul>
<% _.each( data, function( category, i ){ %>
<li>
<h3><%= i %></h3>
<ul>
<% _.each( category, function( item ){ %>
<li>
<%= item.title %>
</li>
<% }) %>
</ul>
</li>
<% }) %>
</ul>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script>
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.vent = _.extend({}, Backbone.Events);
})();
// !models.js
App.Models.Item = Backbone.Model.extend({});
// !collections.js
App.Collections.Items = Backbone.Collection.extend({
model: App.Models.Item,
url: 'api/items.json'
});
// !views.js
App.Views.Items = Backbone.View.extend({
el: '#items',
initialize: function() {
this.render();
var groups = _.groupBy(this.collection.toJSON(), 'category');
console.log(groups);
template = _.template( document.getElementById('allItemTemlate').innerHTML, { data : groups } );
},
render: function() {
document.getElementById('items').innerHTML = template;
},
});
// !router.js
App.Router = Backbone.Router.extend({
routes: {
'':'index',
},
index: function() {
console.log('index page !');
},
});
new App.Router;
Backbone.history.start();
App.items = new App.Collections.Items;
App.items.fetch().then(function() {
new App.Views.Items({ collection: App.items });
});
</script>
</body>
</html>

You need to be more aware of execution order. For example if you need a variable in render, you shouldn't call render before setting that variable. I also propose some changes.
App.Views.Items = Backbone.View.extend({
el: '#items',
initialize: function() {
// listen to collection change event and re-render then
this.listenTo( this.collection, "change", this.render );
// changes: this.template instead of global template
// and not executing it yet, but creating a function
this.template = _.template( document.getElementById('allItemTemlate').innerHTML );
this.render();
},
// a separate method to get the groupped collection
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
// here the template gets executed and writtn to element's innerHTML
// also removed getElementById, as it is readily available as this.el
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});

Related

My Backbone Model View Doesnt Work

The below example that I have written using backbone.js doesnt work.
I want the single record that I enter in the textboxes to show in the emplist ul.
Can someone help please.
Thanks inadvance.
EmpForm.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Emp Form (Backbone.js)</title>
</head>
<form id="empform" align="center" border="1">
Enter Emp Name:<input type="text" id="empname" placeholder="Enter emp name" size="20"/><br>
Enter Emp Job:<input type="text" id="desig" placeholder="Enter emp designation" size="20"/><br>
<button id="addrecord">Add Record</button>
<hr>
<ul id="emplist">
</ul>
</form>
<script id="emptemplate" type="text/template">
<span><strong><%= empname %></strong><blockquote><%= desig %></span>
</script>
<!-- <script type="text/javascript" src="./jslib/underscore1.5.0.js"></script>
<script type="text/javascript" src="./jslib/jquery1.9.1.js"></script>
<script type="text/javascript" src="./jslib/backbone1.0.0.js"></script> -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore.js"></script>
<script src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone.js"></script>
<script type="text/javascript">
(function(){
EmpModel = Backbone.Model.extend({
defaults:{
empname:'guest emp',
desig:'some work'
}
});
EmpView = Backbone.View.extend({
tagName:'emplist',
template:_.template('emptemplate'),
events:{
'click #addrecord':'addRecord'
},
addRecord:function(){
this.render();
},
render: function() {
this.$el.html( this.template(this.model.toJSON()) );
return this;
}
});
var empModel=new window.EmpModel({empname:'sree',desig:'architect'});
var empView=new window.EmpView({model:empModel});
$(document.body).append(empView.render().el);
})();
</script>
</html>
Well. First off, you should definitely review http://backbonejs.org/ and practice some more with javascript in general. But the following should get you started:
var EmpModel = Backbone.Model.extend({
defaults:{
empname:'guest emp',
desig:'some work'
}
});
var EmpView = Backbone.View.extend({
tagName:'form',
el: '#empform',
template: _.template($('#emptemplate').html()),
events:{
'click #addrecord':'addRecord'
},
addRecord:function(){
var empname = $('#empname').val(); // not the best way to do this, btw
var desig = $('#desig').val(); // same thing...
var newModel = new EmpModel({empname: empname, desig: desig});
this.render(newModel);
},
render: function(model) {
this.$el.find('#emplist').append( this.template(model.toJSON()) );
// can also use '... .html( this.template(model.toJSON()) );'
return this;
}
});
var empModel = new EmpModel({empname:'sree',desig:'architect'});
var empView = new EmpView();
empView.render(empModel);
Also add type="button" to the Add Record button.
See fiddle. Cheers!

ChildView error with Marionette app

I am new to backbone and marionette, and learning that from the book Getting Started with Backbone Marionette by Packpub publication.
I am getting the error as Uncaught NoChildViewError: A "childView" must be specified
and my code is
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Marionette test app</title>
<link rel="stylesheet" media="screen" href="{{ url_for('static', filename='bootstrap.css') }}">
<style type="text/css">
.container {
margin-top: 10px;
}
body {
padding-top: 60px;
padding-bottom: 40px;
}
</style>
</head>
<body>
<div id="container" class="well">
<h2>Marionette Views- CollectionView</h2>
</div>
<script id="categoryTemplate" type="text/template">
<a href="#<%= name%>" ><%= name%> (<%= booksOnCategory %>)</a>
<button class="info">Details</button>
</script>
<script src="{{ url_for('static', filename='jquery.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap.js') }}"></script>
<script src="{{ url_for('static', filename='underscore.js') }}"></script>
<script src="{{ url_for('static', filename='backbone.js') }}"></script>
<script src="{{ url_for('static', filename='backbone.marionette.js') }}"></script>
<script type="application/javascript">
// lets create new application as below;
var BookStoreApp = new Backbone.Marionette.Application();
var BookStoreController = Backbone.Marionette.Controller.extend({
displayBooks : function(){
console.log("I will display books");
}
});
var BookStoreRouter = Backbone.Marionette.AppRouter.extend({
controller : BookStoreController,
appRoutes : {
"":"displayBooks"
}
});
BookStoreApp.addInitializer(function(){
var controller = new BookStoreController();
var router = new BookStoreRouter({controller: controller});
console.log("Hello from addInit");
});
BookStoreApp.on("initialize:after", function(){
if (Backbone.history){
Backbone.history.start();
console.log("hello from initialize:after.. .");
}
});
BookModel = Backbone.Model.extend({
defaults : {
name : "",
booksOnCategory:0
}
});
BookCollection = Backbone.Collection.extend({
model : BookModel
});
var bookModel = new BookModel({name:"Math", booksOnCategory:3});
var bookModel2 = new BookModel({name:"Art", booksOnCategory:5});
var bookModel3 = new BookModel({name:"Science", booksOnCategory:6});
var bookModel4 = new BookModel({name:"Biology", booksOnCategory:1});
var bookCollection = new BookCollection([bookModel, bookModel2, bookModel3, bookModel4]);
var BookView = Backbone.Marionette.ItemView.extend({
template : '#book-template',
tagName: 'li',
events:{
"mouseenter .info": "showDetails",
"mouseleave .info": "hideDetails"
},
showDetails: function(){
this.$(".info").popover({
title: this.model.get('name'),
content: "we have "+ this.model.get("booksOnCategory") + " left"
});
this.$(".info").popover("show");
},
hideDetails: function(){
this.$(".info").popover("hide");
}
});
CategoriesVieww = Backbone.Marionette.CollectionView.extend({
tagName: 'ul',
className: "unstyled",
itemView: BookView
});
var CategoriesView = new CategoriesVieww({
collection: bookCollection,
el: "#container" });
CategoriesView.render();
BookStoreApp.start();
</script>
</body>
</html>
note: i am testing this app on flask-jinja2 app; so ignore the url_for in script tag; as they are required by the jinja2
Looks like the problem is exactly what the error message says ;) A CollectionView is generated as a sequence of child views, and you need to tell the collection view which view type you want to use for those children.
But you haven't. Perhaps just a typo? Try changing your CategoriesVieww to
CategoriesVieww = Backbone.Marionette.CollectionView.extend({
tagName: 'ul',
className: "unstyled",
childView: BookView // <-- here
});
If you are using collectionView OR a compositeView of Marionette 2.2.0 or above, be aware that the API keywords have changed as follows:
itemView is now called childView
itemViewContainer is now called childViewContainer
BEFORE 2.2.0
CategoriesView = Backbone.Marionette.CompositeView.extend({
template: '#template',
className: "unstyled",
itemView: BookView // <-- here
itemViewContainer: "#books-list"
});
It took me a while to figure it out through Firebug debugger. You can refer to https://docs.google.com/document/d/1fuXb9N5LwmdPn-teMwAo3c8JTx6ifUowbqFY1NNSdp8/edit# for API changes. Not all changes are documented anywhere I can find.
AFTER 2.2.0 :
CategoriesView = Backbone.Marionette.CompositeView.extend({
template: '#template',
className: "unstyled",
childView: BookView // <-- here
childViewContainer: "#books-list"
});

How to set up widget with Click to show Master/Slave Checkboxes in AngularJS

I'm attempting to write a prototype for a widget that contains 2 sides. On the left side is a list of interest groups, on the right side are the associated interest topics. (i.e. Pets on the left, Birds, Dogs, Cats, on the right). The data is populated by an AJAX call to an endpoint that's making a call to the Twitter API.
I'm not sure I'm approaching this correctly and would like some advice on how to get this set up the "Angular way". I was planning on using a similar approach to this JSFiddle for having the master/slave checkboxes setup. Below is my current code.
index.html
<!doctype html>
<html ng-app="interests">
<head>
<meta http-equiv="Content-type" content="text/html" charset="utf-8">
<title>Twitter Interests</title>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.min.css">
<link rel="stylesheet" content="text/css" href="stylesheets/style.css">
</head>
<body ng-controller="InterestController as interestCtrl">
<div class="container">
<div class="row">
<div class="col-xs-12">
<label>Interests</label>
<input type="text" ng-model="search.$">
</div>
<div class="col-xs-6">
<ul id="groups">
<li group-listing group="{{ group }}" ng-repeat="group in (groups | filter: search) track by $index">
</li>
</ul>
</div>
<div class="col-xs-6">
<div topic-listing topic="{{ topic }}" ng-repeat="topic in topics track by $index">
</div>
</div>
</div>
</div>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-route/angular-route.min.js"></script>
<script src="bower_components/underscore/underscore.js"></script>
<script src="javascripts/main.js"></script>
</body>
</html>
groupListing.html
<div ng-click="showTopics(group)">{{ group }}</div>
interestListing.html
<input type="checkbox" ng-model="topic.isChecked">{{ topic }}
main.js
var app = angular.module('interests', ['ngRoute']);
app.service('InterestService', ['$http', function ($http) {
var getInterests = function (query) {
return $http({
method: 'GET',
url: '/api/interests?=' + query
});
};
return {
getInterests: getInterests
}
}]);
app.controller('InterestController', ['$scope', 'InterestService', function ($scope, InterestService) {
var results = [];
var interests = {};
var resultArray;
var group;
var topic;
$scope.topics = [];
$scope.showTopics = function (group) {
$scope.topics = interests[group];
};
InterestService.getInterests().then(function (result) {
results = result.data.data;
_.each(results, function (result) {
resultArray = result.name.split('/');
group = resultArray[0];
topic = {};
topic.name = resultArray[1];
topic.isChecked = false;
if (_.has(interests, group)) {
interests[group].push(topic);
} else {
interests[group] = [];
interests[group].push({ name: 'All of ' + group, isChecked: false });
interests[group].push(topic);
}
});
$scope.groups = _.keys(interests);
});
}]);
app.directive('groupListing', function () {
return {
restrict: 'EA',
scope: {
group: "#"
},
controller: 'InterestController',
templateUrl: 'templates/groupListing.html'
}
});
app.directive('interestListing', function() {
return {
restrict: 'EA',
scope: {
topic: "#"
},
controller: 'InterestController',
templateUrl: 'templates/interestListing.html',
}
});

Getting TypeError : h.has is not a function error

I am trying to test backbone.js , however, I am getting TypeError : h.has is not a function error. My HTML Code:
<html>
<head>
<title></title>
</head>
<body>
<h1>My Theater</h1>
<div id="mainContainer"></div>
<input type="text" value="Enter HashTag" id="hashtag" />
<script type="text/template" id="tmplt-Tweets">
<ul>
</ul>
</script>
<script type="text/template" id="tmplt-Tweet">
<div>*******************************************************</div>
<div><%= url %> </div>
<div><%= text %> </div>
<div><%= html %> </div>
<div><%= date %> </div>
<div><%= id %> </div>
<div><%= img %> </div>
<div><%= name %> </div>
<div><%= rt %> </div>
</script>
<script src="scripts/libs/jquery-1.7.1.js" type="text/javascript"></script>
<script src="scripts/libs/underscore.js" type="text/javascript"></script>
<script src="scripts/libs/backbone-min.js" type="text/javascript"></script>
<script src="scripts/main.js" type="text/javascript"></script>
</body>
</html>
BackBone code:
/// <reference path="../../scripts/libs/jquery-1.7.1.js" />
/// <reference path="../../scripts/libs/underscore.js" />
/// <reference path="../../scripts/libs/backbone-min.js" />
var Tweet = {
Models: {},
Collections: {},
Views: {},
Templates:{}
}
Tweet.Models.Movie = Backbone.Model.extend({})
Tweet.Collections.Movies = Backbone.Collection.extend({
model: Tweet.Models.Movie,
initialize: function(){
console.log("No Tweets Yet")
}
});
Tweet.Templates.movies = _.template($("#tmplt-Tweets").html())
Tweet.Views.Movies = Backbone.View.extend({
el: $("#mainContainer"),
template: Tweet.Templates.movies,
//collection: new Theater.Collections.Movies(), //Not needed
initialize: function () {
//_.bindAll(this, "render", "addOne", "addAll");
this.collection.bind("reset", this.render, this);
this.collection.bind("add", this.addOne, this);
},
render: function () {
console.log("render")
console.log(this.collection.length);
$(this.el).html(this.template());
this.addAll();
},
addAll: function () {
console.log("addAll")
this.collection.each(this.addOne);
},
addOne: function (model) {
console.log("addOne")
view = new Tweet.Views.Movie({ model: model });
$("ul", this.el).append(view.render());
}
})
Tweet.Templates.movie = _.template($("#tmplt-Tweet").html())
Tweet.Views.Movie = Backbone.View.extend({
tagName: "li",
template: Tweet.Templates.movie,
//events: { "click .delete": "test" },
initialize: function () {
//_.bindAll(this, 'render', 'test');
this.model.bind('destroy', this.destroyItem, this);
this.model.bind('remove', this.removeItem, this);
},
render: function () {
return $(this.el).append(this.template(this.model.toJSON())) ;
},
removeItem: function (model) {
console.log("Remove - " + model.get("Name"))
this.remove();
}
})
Tweet.Router = Backbone.Router.extend({
routes: {
"": "http://localhost/assignment/index.php" //http://localhost:22257/Theater/theater.htm
},
defaultRoute: function () {
console.log("defaultRoute");
Tweet.movies = new Tweet.Collections.Movies()
new Tweet.Views.Movies({ collection: Tweet.movies }); //Add this line
Tweet.movies.fetch();
console.log(Tweet.movies.length)
}
})
var appRouter = new Tweet.Router();
Backbone.history.start();
Any idea? Thanks
You probably are using an outdated version of underscore library, please update it to fix the issue.
Also update to latest version of backbone.js if you still face any other issue
I had the same issue and I resolved it by rearranging the order of js files to load in script tag. Make sure that jquery.js file is loaded before backbone.js file. This has resolved my issue.
I agree that this issue is most likely caused by some libraries (backbone, underscore or jQuery) being out of date. To resolve please update to the latest, the below worked for me..
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js" type="text/javascript"></script>

Events not fireing on click and unable to high light the dom element

I am making a Backbone.js app, in this i got the issue on the events.. and filter
issue 1:
When i click on the list item events not firing... method is not called
issue 2 : in the Students View "getScore" method not called when i am click on the 'highScoreBtn'..
request :
After i filter the high values from 'scored' - how can i high light that 'li' - to adding some class name ('.highlight)..
what is wrong with my code.. or can any one advice me the right way..
My sample JS:
$(function() {
var student = [
{name:"student0",scored:75},{name:"student1",scored:49},{name:"student2",scored:25},
{name:"student3",scored:96}, {name:"student4",scored:42}, {name:"student5",scored:85},
{name:"student6",scored:68}, {name:"student7",scored:19}, {name:"student8",scored:85},
{name:"student9",scored:26}
]
var model = Backbone.Model.extend({
defaults:{
name:"undefined",
scored:"0"
}
});
var collection = Backbone.Collection.extend({
model:model
});
var studentView = Backbone.View.extend({
tagName:'li',
className:"list",
events:{
'click .list':"called"
},
template:_.template($("#studentTemplate").html()),
render:function(){
this.$el.append(this.template(this.model.toJSON()));
return this;
},
called:function(){
alert("i a called")
}
});
var studentsView = Backbone.View.extend({
el:$(".page"),
events:{
"click #highScoreBtn":"showHighScore"
},
initialize:function(){
this.collection = new collection(student);
this.render();
},
render:function(){
var that = this;
_.each(this.collection.models, function(item){
that.$el.find('ul').append(new studentView({model:item}).render().el);
})
},
getHighSocre:function(){
return _.each(this.collection, function(item){
return item.get('scored') > 60;
})
},
showHighScore:function(){
this.getHighSocre();
}
})
var newStudentList = new studentsView();
});
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,Chrome=1" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<title>User Manager</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<style type="text/css">
li{
border-bottom:1px solid gray;
list-style: none;
padding: 10px;
}
ul{
padding:0;
margin:0;
}
li:last-child{
border: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Student Manager</h1>
<hr>
<div class="page">
<a id="highScoreBtn" class="btn btn-primary" href="#">Show high Score</a>
<hr>
<ul>
</ul>
</div>
</div>
<script id="studentTemplate" type="text/template">
<strong>Name :</strong> <%= name %> <span>Scored:</span> <%= scored %>
</script>
<script type="text/javascript" src="js/lib/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/lib/underscore-min.js"></script>
<script type="text/javascript" src="js/lib/backbone-min.js"></script>
<script type="text/javascript" src="js/student.js"></script>
</body>
</html>
for issue1:
It looks like StudentView are trying to observe itself.
View should observe DOM event of its child element.
var var studentView = Backbone.View.extend({
tagName:'li',
// className:"list", # move to template
events:{
'click .list':"called"
},
....(omitting)
<script id="studentTemplate" type="text/template">
<span class="list"><strong>Name :</strong> <%= name %> <span>Scored:</span> <%= scored %></span>
</script>
for issue2:
Are showHighScore called correctly?
if you add debug code, what is displayed in console of developer tool?
getHighSocre:function(){
console.log('getHighScore is called');
return _.each(this.collection, function(item){
return item.get('scored') > 60;
})
},
showHighScore:function(){
console.log('showHighScore is called');
var highscore = this.getHighSocre();
console.log(highscore);
}
if functions are called, you should add some codes to do something for highlighting in getHighScore function.
-- EDIT --
It may be possible by observing Backbone Model event in StudentView.
This is not a clean way too much, because model's highlight property should be set on model's initialization.
var model = Backbone.Model.extend({
defaults:{
name:"undefined",
scored:"0",
highlight:false
}
});
var studentView = Backbone.View.extend({
...
initialize: function(){
this.model.on("change", this.highlight, this);
},
...
highlight: function(){
if (this.model.get('hilight')){
$('.list', this.el).addClass("hilight");
}
}
})
var studentsView = Backbone.View.extend({
...
getHighSocre:function(){
_.each(this.collection, function(item){
if (item.get('scored') > 60){
item.set(hilight:true);
}
})
},
...
})
I am not sure, but I hope following code help you:
render:function(){
var that = this;
that.$el.find('ul').empty();
_.each(this.collection.models, function(item){
that.$el.find('ul').append(new studentView({model:item}).render().el);
})
},
showHighScore:function(){
this.getHighSocre();
this.render();
}

Resources