My Backbone Model View Doesnt Work - backbone.js

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!

Related

Angular JS. What the order of actions in HTML when creating a directive?

I have just a problem rightly ask a question).
I created angularJS app 'myApp'. And app get list of two groups. And app should to represent on browser two checkboxes: first - checked, second - empty.
But maybe I didn't count the order of action of building of angular's directives.
This is example
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{group.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function(){
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
}
};
});
</script>
</body>
</html>
I get a right 'id' of element on front-end when I inspected code on browser. If I put on template number '769' instead {{info.id}}, the first checkbox will checked.
But I think that controller of directive acts before assignment 'id' of elements.
But I need realize this by that method. Because I try to explain on scratches the situation which I don't understand.
Thanks a lot.
Two issues :
First point
Your template is :
<input type='checkbox' id='{{group.id}}'> {{info.id}}
It should be :
<input type='checkbox' id='{{info.id}}'> {{info.id}}
Second point
You must call your dom modification in a $timeout function. On the first execution of your controller, your html does not exists yet.
$timeout will trigger the code at the next digest. So, your html will exists.
Of course, do not modify your html in your controller (even in a directive). The link function is the place to modify the html.
Corrected snippet
Here is the snippet with the correction. There is a jquery error, I didn't watch what it is. Do not hesitate to edit to throw it away.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{info.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function($timeout){
$timeout(function() {
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
});
}
};
});
</script>
</body>
</html>
Looks like you want to hook the checkbox up
template: "<input type='checkbox' ng-model='info.spam_status' ng-true-value='\'checked\'' id='{{group.id}}'> {{info.id}}"

Underscore templating not working for simple message with Backbone.js

Just trying to make Backbone.js display a simple message on index.html...It fails if I do try with underscore but it will append a message to the quiz_question div element if I try to do something like
questionTemplate: _.template( '<div>Hello <%= msg %></div>')
...What am I missing?
Index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="quiz_question">
<input id="back_id" type="button" value="Back">
<input id="next_id" type="button" value="Next">
</div>
<script type="text/template" id="qtemplate"></script>
<script src="js/jquery-2.0.2.min.js"></script>
<script src="js/underscore-min.js"></script>
<script src="js/backbone-min.js"></script>
<script src="js/backbone.localStorage.js"></script>
<script src="js/questionmodel.js"></script>
<script src="js/questioncollection.js"></script>
<script src="js/questionview.js"></script>
<script src="js/app.js"></script>
<script type="text/template" id="qtemplate">
<div><%= msg %></div>
</script>
</body>
</html>
app.js
var app = app || {};
$(function() {
// Kick things off by creating the **App**.
new app.QuestionView();
});
questionview.js
var app = app || {};
app.QuestionView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: '#quiz_question',
// Our template for the line of statistics at the bottom of the app.
questionTemplate: _.template( $('#qtemplate').html() ),
//questionTemplate: _.template( '<div>Hello <%= msg %></div>'),
// Delegated events for displaying new questions, and clearing existing ones
events: {
'click #back_id': 'displayPreviousQuestion',
'click #next_id': 'displayNextQuestion'
},
// The QuestionView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between a **Question** and a **QuestionView** in this
// app, we set a direct reference on the model for convenience.
initialize: function() {
//app.Questions.fetch();
this.render();
},
render: function(){
// render the function using substituting the varible 'who' for 'world!'.
this.$el.append(this.questionTemplate({msg: "hope floats"}));
//***Try putting your name instead of world.
},
displayPreviousQuestion: function() {
},
displayNextQuestion: function() {
}
});
Your page looks like this:
<script src="js/questionview.js"></script>
<!-- ... -->
<script type="text/template" id="qtemplate">
<div><%= msg %></div>
</script>
so questionview.js will be loaded and executed before #qtemplate is in the DOM. Inside questionview.js you have this:
app.QuestionView = Backbone.View.extend({
//...
questionTemplate: _.template( $('#qtemplate').html() ),
so _.template( $('#qtemplate').html() ) will be executed while questionview.js is being loaded and that happens before there is a #qtemplate available. The result is that you end up doing _.template(undefined) and that doesn't do anything useful.
You can wrap the view definition in a $(function() { ... }) to delay its execution until after the DOM is ready or you could delay creating the template function until you need it with something like this:
initialize: function() {
this.questionTemplate = _.template($('#qtemplate').html());
}
in your QuestionView. There are variations on those two basic approaches but that should get you started.
Above answer explain cause very well, but if you want to do quick fix, moving template tag above other script would fix the problem.

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

Underscore's bindAll: Cannot read property 'bind' of undefined

<!DOCTYPE HTML>
<html>
<head>
<title></title>
</head>
<body>
<div id="menu"></div>
<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>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone-localstorage.js/1.0/backbone.localStorage-min.js" type="text/javascript"></script>
<script type="text/javascript">
var myView = Backbone.View.extend({
events: {
'click #menu': 'insideMenuHandler'
},
//for event click
initialize: function() {
_.bindAll(this, 'insideMenuHandler', 'outsideMenuHandler');
},
render: function() {
// Both <body> and <html> for paranoia.
$('body, html').on('click', this.outsideMenuHandler);
// ...
return this;
},
remove: function() {
// Clean up after ourselves.
$('body, html').off('click', this.outsideMenuHandler);
// ...
},
// ...
outsideMenuHandler: function(e) {
// ...
return false;
}
});
var v=new myView({el: '#view-goes-here'});
v.render();
</script>
</body>
</html>
Hey i am new to backbone.js and not understanding what is wrong in the code below, where do i change my code to make it working? Is there a problem with the bind function or something else is going wrong?
You're calling _.bindAll in the line
_.bindAll(this, 'insideMenuHandler', 'outsideMenuHandler');
but you don't have any function with name insideMenuHandler in the view.

Resources