I somehow made basic to-do list, but without ability to save tasks on a server. Now started implementing server side, but having a hard time grasping the whole idea of how is that all supposed to communicate with each other. Here's what I tried to do: in a TodoView (which is rendering the list) I made a model with input value and via save() method tried to post in on a server. As i said I lack grasping a general idea, how is that json supposed to be saved on a server (will it be saved in a tasks.json file which I passed in Collection?). Any help, tips and directions would be greatly appreciated!
Here's my Backbone code
var Model = Backbone.Model.extend({
default: {
task: '',
completed: false
}
});
var Collection = Backbone.Collection.extend({
model: Model,
url: '/tasks.json'
});
var ItemView = Backbone.View.extend({
tagName: 'li',
render: function () {
this.$el.data('cid', this.model.cid);
this.$el.html(this.model.toJSON().task);
return this;
}
});
var TodoView = Backbone.View.extend({
el: '#todo',
initialize: function () {
this.collection = new Collection();
this.collection.on('add', this.render, this);
// this.collection.fetch();
},
events: {
'click .add': 'add',
'dblclick li': 'destroy',
'keydown': 'keyEvent'
},
add: function () {
// this.collection.add(new Model({ //adding input as an model to collection
// task: this.$('#todo').val(),
// completed: false
// }));
var taskData = {
task: this.$('#todo').val(),
completed: false
};
var newlyCreatedModel = new Model({
task: this.$('#todo').val(),
completed: false
});
newlyCreatedModel.save({
// url: '/tasks.json',
data: JSON.stringify(taskData),
type: 'POST',
contentType: 'application/json'
});
this.$('#todo').val(''); //clearing input field
this.$('#todo').focus(); //focusing input after adding task
},
keyEvent: function (e) {
if (e.which === 13) {
this.add();
}
},
destroy: function (e) {
// e.preventDefault();
var id = $(e.target).data('cid');
var model = this.collection.get(id);
model.destroy();
e.target.remove();
},
render: function (newModel) {
var self = this,
todoView;
todoView = new ItemView({
model: newModel
});
self.$('.list').append(todoView.render().el);
return this;
}
});
var trigger = new TodoView();
Here's my html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Validation</title>
</head>
<body>
<div id="todo">
<label for="todo">Write your task</label>
<input type="text" id='todo'><button class='add'>add</button>
<ul class='list'>
</ul>
</div>
<script src="jquery.js"></script>
<script src="underscore.js"></script>
<script src="backbone.js"></script>
<script src="main.js"></script>
</body>
</html>
and finally here's my server written on node.js with help of express.js
var express = require('express');
var server = express();
server.use(express.static(__dirname + '/test'));
server.post('/somepath', function (req, res) {
var bodyStr = '';
req.on('data', function (chunk) {
bodyStr += chunk.toString();
});
req.on('end', function (chunk) {
// res.send(bodyStr);
console.log(bodyStr);
});
});
var server = server.listen(8888);
Related
I'm writing my first Backbone blog app but when i try to add new post it throws an error.
Here is my app.js (all of backbone related components are in this file):
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
var Post = Backbone.Model.extend({});
var Posts = Backbone.Collection.extend({
model : Post,
url : "/posts"
});
var PostListView = Backbone.View.extend({
tagName: "li",
template: _.template("<a href='/posts/{{id}}'>{{title}}</a>"),
events: {
'click a': 'handleClick'
},
handleClick: function (e) {
e.preventDefault();
postRouter.navigate($(e.currentTarget).attr("href"),
{trigger: true});
},
render: function () {
this.el.innerHTML = this.template(this.model.toJSON());
return this;
}
});
var PostsListView = Backbone.View.extend({
template: _.template("<h1>My Blog</h1><a href='/post/new' class='newPost'>New</a> <ul></ul>"),
events: {
'click .newPost': 'handleNewClick'
},
handleNewClick: function (e) {
e.preventDefault();
postRouter.navigate($(e.currentTarget).attr("href"),
{trigger: true});
},
render: function () {
this.el.innerHTML = this.template();
var ul = this.$el.find("ul");
this.collection.forEach(function (post) {
ul.append(new PostListView({
model: post
}).render().el);
});
return this;
}
});
var PostView = Backbone.View.extend({
template: _.template($("#postView").html()),
events: {
'click a': 'handleClick'
},
render: function () {
var model = this.model.toJSON();
model.pubDate = new Date(Date.parse(model.pubDate)).toDateString();
this.el.innerHTML = this.template(model);
return this;
},
handleClick: function (e) {
e.preventDefault();
postRouter.navigate($(e.currentTarget).attr("href"),
{trigger: true});
return false;
}
});
var PostFormView = Backbone.View.extend({
tagName: 'form',
template: _.template($("#postFormView").html()),
initialize: function (options) {
this.posts = options.posts;
},
events: {
'click #submitPost': 'createPost',
'click .back' : 'backButton'
},
render: function () {
this.el.innerHTML = this.template();
return this;
},
backButton: function (e) {
e.preventDefault();
postRouter.navigate($(e.currentTarget).attr("href"),
{trigger: true});
return false;
},
createPost: function (e) {
e.preventDefault();
var postAttrs = {
content: $("#postText").val(),
title: $("#postTitle").val(),
pubDate: new Date(),
};
this.posts.create(postAttrs);
postRouter.navigate("/", { trigger: true });
return false;
}
});
var PostRouter = Backbone.Router.extend({
initialize: function (options) {
this.posts = options.posts;
this.main = options.main;
},
routes: {
'': 'index',
'posts/:id': 'singlePost',
'post/new': 'newPost'
},
index: function () {
var pv = new PostsListView({ collection: this.posts });
this.main.html(pv.render().el);
},
singlePost: function (id) {
var post = this.posts.get(id);
var pv = new PostView({ model: post });
this.main.html(pv.render().el);
},
newPost: function () {
var pfv = new PostFormView({ posts: this.posts });
this.main.html(pfv.render().el);
}
});
I also have some view templates in my index file :
<!DOCTYPE html>
<html>
<head>
<title> Simple Blog </title>
</head>
<body>
<div id="main"></div>
<script src="/jquery.js"></script>
<script src="/underscore.js"></script>
<script src="/backbone.js"></script>
<script type="text/template" id="postFormView">
All Posts<br />
<input type="text" id="postTitle" placeholder="post title" />
<br />
<textarea id="postText"></textarea>
<br />
<button id="submitPost"> Post </button>
</script>
<script type="text/template" id="postView">
<a href='/'>All Posts</a>
<h1>{{title}}</h1>
<p>{{pubDate}}</p>
{{content}}
</script>
<script src="/app.js"></script>
<script>
var postRouter = new PostRouter({
posts: new Posts(<%- posts %>),
main: $("#main")
});
Backbone.history.start({pushState: true});
</script>
</body>
</html>
Viewing posts and home page works fine but when I try to create a new post I get this error from the dev tools console:
Uncaught ReferenceError: id is not defined
at child.eval (eval at _.template (http://localhost:3000/underscore.js:1:1), <anonymous>:6:8)
at child.template (http://localhost:3000/underscore.js:1214:21)
at child.render (http://localhost:3000/app.js:27:34)
at http://localhost:3000/app.js:48:16
at Array.forEach (native)
at Function._.each._.forEach (http://localhost:3000/underscore.js:79:11)
at child.Collection.(anonymous function) [as forEach] (http://localhost:3000/backbone.js:956:24)
at child.render (http://localhost:3000/app.js:45:25)
at child.index (http://localhost:3000/app.js:118:27)
at Object.callback (http://localhost:3000/backbone.js:1242:30)
The server is a simple nodejs server and output for creating a post is something like this:
{"result":{"ok":1,"n":1},"ops":[{"content":"kljhlkjh","title":"jkhjklh","pubDate":"2016-10-29T10:21:47.793Z","id":12,"_id":"5814783b732bbe153461eca4"}],"insertedCount":1,"insertedId
s":["5814783b732bbe153461eca4"]}
Where is the error?
First, you need to find what is causing the error, and where does it comes from.
The error comes from the following line inside the PostListView's render function:
this.el.innerHTML = this.template(this.model.toJSON());
And the error is thrown by underscore's template rendering. Which comes down to:
template: _.template("<a href='/posts/{{id}}'>{{title}}</a>"),
See the {{id}}? It's that one that is not defined when the error occurs.
What "{{id}} not defined" means?
You're passing this.model.toJSON() as the data for the template. So, it means that this.model's id attribute is not defined yet.
Why is my model id not defined yet?
It's because creating a new model through the collection's create function is asynchronous.
this.posts.create(postAttrs);
// the model hasn't received an id from the server yet.
postRouter.navigate("/", { trigger: true });
How to wait after a collection's create call?
Backbone offers success and error callbacks for most (if not all) its asynchronous functions.
The options hash takes success and error callbacks which will both be
passed (collection, response, options) as arguments.
So, you could change your createPost function to the following, adding a onPostCreated callback.
createPost: function(e) {
e.preventDefault();
var postAttrs = {
content: $("#postText").val(),
title: $("#postTitle").val(),
pubDate: new Date(),
};
this.posts.create(postAttrs, {
context: this,
success: this.onPostCreated
});
return false;
},
onPostCreated: function() {
// you don't need to use the router, so your views are self-contained
Backbone.history.navigate("/", { trigger: true });
},
I'd like to apologize for the mess below - having some difficulty with the following code. Attempting to display each model view through the collection view which while not succinct is required. Any help would be greatly appreciated. Tips and pointers as well. Thank you in advance.
$(function() {
/* Model */
var Publication = Backbone.Model.extend({
defaults: {
title: "",
published: ""
}
});
/* Collection */
var PublicationCollection = Backbone.Collection.extend({
model: Publication,
url: 'http://www.stellarbiotechnologies.com/media/press-releases/json'
});
/* Model View */
var PublicationView = Backbone.View.extend({
tagName: 'li',
className: 'publication',
el: 'displayHere',
template: _.template($('#publicationTemplate').html()),
initialize: function() {
this.model.on('destroy', this.remove, this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
/* Collection View */
var AppView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
var pubs = this.collection;
pubs.fetch;
pubs.bind('reset', this.render);
pubs.bind('add', this.add, this);
pubs.bind('remove', this.remove, this);
},
render : function() {
this.collection.each(this.add, this);
return this;
},
add: function(pub) {
var pub = new PublicationView({model: Publication});
this.$el.html(pub.render().el);
},
remove: function(pub) {
var pubs = this.collection;
pubs.remove(pub);
pubs.render();
},
});
var App = new AppView({collection: PublicationCollection});
});
HTML:
<body>
<ul id="displayHere"></ul>
</body>
Template:
<script id="publicationTemplate" type="text/template">
<td class="id"><%= id %></td>
<td class="title"><%= title %></td>
<td class="published"><%= published %></td>
</script>
Here we go
!DOCTYPE html>
<html lang="EN">
<head>
<meta charset="UTF-8">
<title>Help 7</title>
</head>
<body>
<ul id="displayHere"></ul>
<script id="publicationTemplate" type="text/template">
<td class="title"><%= title %></td>
<td class="published"><%= published %></td>
</script>
<script src="js/jquery.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script>
$(function() {
/* The initialization of the models is correct, according to data json page you supply */
/* Model */
var Publication = Backbone.Model.extend({
defaults: {
title: "",
published: ""
}
});
/* They need to manipulate the data received since apparently come masked in the variable "news", this variable contains the main array with which it is going to work. */
/* Collection */
var PublicationCollection = Backbone.Collection.extend({
model: Publication,
url: 'http://www.stellarbiotechnologies.com/media/press-releases/json',
/*
for this we will use the "parse" function that provides us backbone, which performs this function is handled in the manner in which the data received before storing in the collection needed
*/
parse: function(response){
return response.news;
}
});
/*
Here you must not set the item as "#displayHere"
*/
/* Model View */
var PublicationView = Backbone.View.extend({
tagName: 'li',
className: 'publication',
template: _.template($('#publicationTemplate').html()),
initialize: function() {
this.model.on('destroy', this.remove, this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
/* Collection View */
var AppView = Backbone.View.extend({
/*
this is where you establish your main item as "#displayHere"
*/
el: '#displayHere',
/*
Here is a somewhat tricky part when receiving data from somewhere, and it takes establish the way in which they will work and much depends on your project, then what we'll do is add a listener to the collection, this means that when you run the "fetch" this will execute the "sync" event which is to be this outstanding.
*/
initialize: function() {
this.listenTo(this.collection, "sync", this.render);
},
render : function() {
this.collection.each(this.add, this);
return this;
},
add: function(newModel) {
var pub = new PublicationView({model: newModel});
this.$el.append(pub.render().el);
},
/* Remove is not used until now */
remove: function(pub) {
var pubs = this.collection;
pubs.remove(pub);
pubs.render();
}
});
/*
First we have to create a collection, you can not just send the constructor PublicationCollection
*/
var AppPublicationCollection = new PublicationCollection();
/*
And created the collection, then we can send it
*/
var App = new AppView({collection: AppPublicationCollection});
/*
And finally we have to run the "fetch" function to send for data
*/
AppPublicationCollection.fetch();
});
</script>
</body>
</html>
i am trying to make my first backbone app, and have run into a problem that i just cant solve..
I have a list of links, each link has a counter next to it,
when i click on a link i want the counter to increment by 1. (i have made this, and it is working)
Next i want the link i clicked to move up in the list IF the counter value is higher than the link above.
like this.
first link (4)
second link (3)
third link (3) <-- if i click on this link i want it to move up above second link.
I have tried using comparator and sortBy, but each time i try something i just cant seem to re-render the view and also have the link move up one spot.
I did manage to sort the list initially, when the main view is initialized.
But updating the view and list placement after i click one of the links i cant figure out how to accomplish.
my code:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
//Modellen
App.Models.Task = Backbone.Model.extend({
defaults: {
name: 'Foo Bar Baz',
uri: 'http://www.google.com',
counter: 0
},
validate: function(attr) {
if ( ! $.trim(attr.name) ) {
return 'En opgave kræver en title.';
};
}
});
//Collection
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
comparator: function(task) {
return task.get('counter');
},
});
//Singel view
App.Views.TaskView = Backbone.View.extend({
tagName: 'li',
template: template('Tasks'),
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit' : 'retTask',
'click .delete' : 'destroy',
'click .uriLink' : 'addCounter'
},
retTask: function() {
var newTaskNavn = prompt('Hvad skal det nye navn være', this.model.get('name'));
if ( !newTaskNavn ) return;
this.model.set('name', newTaskNavn);
},
destroy: function() {
this.model.destroy();
},
addCounter: function(e) {
e.preventDefault();
var newCounter = this.model.get('counter');
this.model.set('counter', newCounter + 1);
},
remove: function() {
this.$el.remove();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()) );
return this;
}
});
//Collection View
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.render();
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
}
});
App.Views.AddTask = Backbone.View.extend({
el: '#addTask',
initialize: function() {
},
events: {
'submit' : 'submit'
},
submit: function(e) {
e.preventDefault();
var taskNavn = $(e.currentTarget).find('.navnClass').val(),
uriNum = $(e.currentTarget).find('.uriClass').val();
if ( ! $.trim(taskNavn)) {
var test = prompt('opgaven skal have et navn', '');
if ( ! $.trim(test)) return false;
taskNavn = test;
}
if( uriNum.indexOf( "http://" ) == -1 ) {
addedValue = 'http://',
uriNum = addedValue + uriNum;
}
$(e.currentTarget).find('input[type=text]').val('').focus();
//var task = new App.Models.Task({ name: taskNavn, uri: uriNum });
this.collection.add({ name: taskNavn, uri: uriNum });
}
});
// new tasks collection
var tasks = new App.Collections.Tasks([
{
name: 'Foo',
uri: 'www.google.com',
counter: 3
},
{
name: 'Bar',
uri: 'http://google.com',
counter: 2
},
{
name: 'Baz',
uri: 'http://www.google.com',
counter: 1
}
]);
// tasks.comparator = function(task) {
// return task.get("counter");
// };
tasks.sort();
// new collection view (add)
var addTaskView = new App.Views.AddTask({ collection: tasks});
// new collection view
var tasksView = new App.Views.TasksView({ collection: tasks });
$('.tasks').html(tasksView.el);
})();
My HTML: (if someone wanna try to replicate the scenario :)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LinkList</title>
</head>
<body>
<h1>Mine opgaver</h1>
<form action="" id="addTask">
<input class="navnClass" type="text" placeholder="Link name"><input clas s="uriClass" type="text" placeholder="www.url-here.com">
<button class="nyOpgave">Ny opgave</button><br />
</form>
<div class="tasks">
<script type="text/template" id="Tasks">
<span class="linkNavn"><%= name %></span> - <%= uri %> : [<span class="counterClass"><%= counter %></span>] <button class="edit">Edit</button> <button class="delete">Delete</button>
</script>
</div>
<script src="js/underscore.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="js/jquery.js"></script>
<script src="js/backbone.js"></script>
<script src="main.js"></script>
</body>
</html>
can anyone please help me figure this one out ?
/Cheers
Marcel
Okay , i have created the application for you , as you have intended it to run.I'm going to try and explain you the entire code , what i have written and why i have written.
First , take a look at the JSfiddle : here
Next , let me explain :
1.This is my model that stores the name of the link , href , the id(not used in my example but its just good practise to assign a unique id to each model) and finally the number of clicks to a link(model).
var myModel = Backbone.Model.extend({
defaults:{
'id' : 0,
'name' : null,
'link' : '#',
'clicks' : 0
}
});
2.This the collection , that stores all my models , i have added a comparator function so that when ever you add a model to a collection , it will sort the collection.
Note : i have added a - sign to sort the collection in descending order of clicks (link with maximum click to appear first)
var myCollection = Backbone.Collection.extend({
model: myModel,
comparator: function(item) {
return -item.get('clicks');
}
});
3.Now this is my main view , what do i mean main view ? This view does the main rendering of the list , that you want to show.Pretty self explanatory code here.One thing , the this.coll.bind('add change',this.render,this) , i have added a 'change' because whenever any of the models in this collection change , we want to re-render the entire list , this happens when i change the count of any link , on clicking it , i want to re-render the entire list.
var myView = Backbone.View.extend({
el: $("#someElement"),
tagName : 'ul',
initialize: function() {
this.coll = new myCollection();
this.coll.bind('add change',this.render,this);
},
events: {
"click #add": "add"
},
add: function(e){
e.preventDefault();
var mod = new myModel();
var name = $('#name').val();
var link = $('#link').val();
mod.set({'id':mod.cid, 'name':name,'link':link});
this.coll.add(mod);
},
render : function(){
$('#list').empty();
this.coll.sort();
this.coll.forEach(function(model){
var listItem = new printView({ model: model});
$('#list').append(listItem.render().el);
});
}
});
4.This is my sub-view , why do i ever make a second view , why isnt 1 view sufficient ?
Well this consider a scenario, with every link you have a delete button (for instance) when i click the delete button (and i have just 1 view) how do i identify which model to destroy(remove from collection ? ) , 1 possible way would be to associate a cid with each model and then on click i can do a this.coll.getByCid() , but this isnt such a good way to do it , IMHO , so i created a separate view for each model.This View renders each model and returns nothing more.
var printView = Backbone.View.extend({
tagName: 'li',
initialize : function(options) {
_.bindAll(this, "render");
},
events:{
"click a": "count"
},
render:function(){
var linkName = this.model.get("name");
var link= this.model.get("link");
var clicks = this.model.get("clicks");
this.$el.append("<a class='link' href='"+link+"'>"+linkName+"</a> ("+clicks+")");
return this;
},
count:function(e){
e.preventDefault();
e.stopPropagation();
var clicks = this.model.get("clicks");
clicks++;
this.model.set({'clicks':clicks});
}
});
5.Initializing my (main) myView
new myView();
Note: I do believe that this application/code can be written in much better way , with several improvements but with my calibre and with the fact that it works ( :p ) i think it can help you.
The collection comparator is only executed when new models are added to the collection: it doesn't update the collection order when properties change. In order to achieve this, you need to call collection.sort():
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
initialize: function() {
this.on('change:counter', this.sort);
},
comparator: function(task) {
return task.get('counter');
}
});
In the list view you can listen to the collection's sort event, and re-render your view:
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.collection.on('sort', this.render, this);
this.render();
},
render: function() {
//if there are existing child views, remove them
if(this.taskViews) {
_.each(this.taskViews, function(view) {
view.remove();
});
}
this.taskViews = [];
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
//keep track of child views
this.taskViews.push(taskView);
}
});
I am new to backbone.js and is having some problem with my app. I have a collection which relies on a json data source. I am able to console.log the json in my parse method. Is that enough to bind the collection to my view? I don't understand the use of fetch method.
My collection looks like as follows,
(function (collections,model) {
collections.Directory = Backbone.Collection.extend({
initialize : function(){
console.log('we are here');
},
model:model.item,
url:'collections/json/data.json',
parse:function(response){
console.log(response);
return response;
}
});
})(app.collections,app.models);
And my master view looks like this,
(function(views,collections){
views.masterView = Backbone.View.extend({
el : $("#contacts"),
initialize : function(){
console.log('view initialize inside render');
this.render();
this.$el.find("#filter").append(this.createSelect());
this.on("change:filterType", this.filterByType, this);
this.collection.on("reset", this.render, this);
this.collection.on("add", this.renderContact, this);
//console.log('we are here'+app.collections.CollectionItems.fetch());
console.log('view initialize');
},
render : function(){
this.$el.find("article").remove();
_.each(this.collection.models,function(item){
this.renderContact(item);
},this);
},
renderContact: function (item) {
views.contactView = new app.views.ContactView({
model: item
});
this.$el.append(contactView.render().el);
},
getTypes : function () {
return _.uniq(this.collection.pluck("Qno"));
},
createSelect : function () {
var select = $("<select/>", {
html: "<option value='all'>All</option>"
});
_.each(this.getTypes(), function (item) {
var option = $("<option/>", {
value: item.toLowerCase(),
text: item.toLowerCase()
}).appendTo(select);
});
return select;
},
events: {
"change #filter select": "setFilter",
"click #add": "addContact",
"click #showForm": "showForm"
},
setFilter : function(e){
this.filterType = e.currentTarget.value;
this.trigger("change:filterType");
},
filterByType: function () {
if (this.filterType === "all") {
this.collection.reset(contacts);
routerURL.navigate("filter/all");
} else {
this.collection.reset(contacts, { silent: true });
var filterType = this.filterType,
filtered = _.filter(this.collection.models, function (item) {
return item.get("type").toLowerCase() === filterType;
});
this.collection.reset(filtered);
routerURL.navigate("filter/"+filterType);
}
},
addContact : function(e){
e.preventDefault();
var contModel = {};
$("#addContact").children("input").each(function(i, el){
if($(el).val() !== "")
contModel[el.id] = $(el).val();
});
contacts.push(contModel);
if (_.indexOf(this.getTypes(), contModel.type) === -1) {
this.collection.add(new Contact(contModel));
this.$el.find("#filter").find("select").remove().end().append(this.createSelect());
} else {
this.collection.add(new Contact(contModel));
}
},
showForm : function(){
this.$el.find("#addContact").slideToggle();
}
});
})(app.views,app.collections);
my model is very simple and looks like this,
(function ( models ) {
models.Item = Backbone.Model.extend({
defaults :{Qno:'1',Desc:'hello'}
});
})( app.models );
ihave one js file instantiatin viewsand collections
(function () {
window.app = {};
app.collections = {};
app.models = {};
app.views = {};
app.mixins = {};
$(function(){
app.collections.CollectionItems = new app.collections.Directory();
//app.collections.CollectionItems.fetch();
//console.log(app.collections.CollectionItems.fetch());
app.collections.CollectionItems.fetch({
success: function (collection,response) {
console.log(response);
}
});
//console.log(app.collections.CollectionItems.toJSON());
console.log('coll started');
app.views.app = new app.views.masterView({collection: app.collections.CollectionItems});
console.log('view is jus about fine!!');
//app.views.pagination = new app.views.PaginatedView({collection:app.collections.paginatedItems});
});
var ContactsRouter = Backbone.Router.extend({
routes: {
"filter/:type": "urlFilter"
},
urlFilter: function (type) {
master.filterType = type;
master.trigger("change:filterType");
}
});
var routerURL = new ContactsRouter();
Backbone.history.start();
})();
my landing page looks like this with a template in it
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Backbone.js Web App</title>
<link rel="stylesheet" href="css/screen.css" />
</head>
<body>
<div id="contacts">
</div>
<script id="contactTemplate" type="text/template">
<h1><%= Qno %></h1>
</script>
<script src="js/jquery.js"></script>
<script src="js/underscore-min.js"></script>
<script src="js/backbone-min.js"></script>
<script src="app.js"></script>
<script src="collections/Directory.js"></script>
<script src="models/item.js"></script>
<script src="views/masterView.js"></script>
<script src="views/simpleView.js"></script>
<!--<script src="js/backbone.paginator.js"></script>-->
</body>
</html>
I just can't get my head around this. The view is not rendered with the collection data. Please help!
I think it's because the fetch method on your collection is executed asynchronously and has therefore not completed when you create your view (if you look at the console I would expect the log statement in the success callback to display after the log statements underneath). This means that your view render method is called before the collection is populated and the reset event (which you're binding to in your view) is never triggered.
Try updating the code which instantiates everything as follows:
$(function(){
app.collections.CollectionItems = new app.collections.Directory();
//app.collections.CollectionItems.fetch();
//console.log(app.collections.CollectionItems.fetch());
app.collections.CollectionItems.fetch({
success: function (collection,response) {
console.log(response);
app.views.app = new app.views.masterView({collection: app.collections.CollectionItems});
}
});
});
I need to maintain the order of models in a collection that are in a model and be able to save it to the server. I know I can save the individual models, but when I save the "parent model" that contains the collection the attribute holding the collection is now saved.
Is there a way to do this? Below is my code. And I know it probably isn't the best, I am still learning.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="underscore-min.js"></script>
<script type="text/javascript" src="backbone-min.js"></script>
<script type="text/javascript">
$(function() {
var WO = Backbone.Model.extend({
wonum: null,
part: null,
desc: null,
initialize: function()
{
this.url = '/rest/wo/'+this.get('wonum');
}
});
var WOView = Backbone.View.extend({
tagName: "tr",
className: "wo",
initialize: function(options)
{
this.render = _.bind(this.render, this);
},
render: function()
{
$(this.el).html("<td>"+this.model.get('wonum')+"</td><td>"+this.model.get('part')+"</td><td>"+this.model.get('desc')+"</td>");
return this;
}
});
var WOs = Backbone.Collection.extend({
model: WO,
url: '/rest/section/wos/'
});
var Section = Backbone.Model.extend({
defaults: {
name : "Section"
},
wos: [],
url: '/rest/section/',
initialize: function()
{
this.wos = new WOs();
this.wos.url += this.id;
this.url += this.id;
}
});
var SectionView = Backbone.View.extend({
tagName: "table",
initialize: function()
{
_(this).bindAll('add','remove');
var that = this;
this._woViews = [];
this.model.wos.each(this.add);
this.model.wos.bind('add', this.add);
},
add: function(woObj)
{
var wov = new WOView({
model: woObj,
id: woObj.get('wonum')});
this._woViews.push(wov);
if(this._rendered)
$(this.el).append(wov.render().el);
this.model.save();
},
render: function()
{
this._rendered = true;
var that = this;
$(this.el).empty();
$(this.el).append("<thead><th>WO</th><th>Part</th><th>Desc</th></thead>");
$(this.el).append("<tbody>");
_(this._woViews).each(function(wov)
{
$(that.el).append(wov.render().el);
});
$(this.el).append("</tbody>");
return this;
}
});
var sec = new Section({id: 1});
sec.wos.add({
wonum: 1001,
part: 'p1001',
desc: 'd1001'});
sec.wos.add({
wonum: 1002,
part: 'p1002',
desc: 'd1002'});
var secv = new SectionView({model: sec, id: sec.get('id')});
$("body").append(secv.render().el);
$("#addwo").bind("click",function()
{
secv.add(new WO({
wonum: 1005,
part: 'p1005',
desc: 'd1005'}));
});
});
</script>
</head>
<body>
<button id='addwo'>Add WO</button>
</body>
</html>
I would consider using the Collection's comparator function (which you can set) to preserve the order of the collection. This comparator would potentially use a property of the models; so for instance, the model's name or even a position property.
Using this approach, you would just save each model independently, but have the collection potentially refresh itself (where it would use the comparator to preserve the desired order).