app.js | |
---|---|
This is a simple app that demonstrates how to use the Backbone.js couch-connector. It is sort of a real time chat with private messages support. | $(function(){ |
Fill this with your database information.
| Backbone.couch_connector.config.db_name = "backbone_connect";
Backbone.couch_connector.config.ddoc_name = "backbone_example";
|
If set to true, the connector will listen to the changes feed and will provide your models with real time remote updates. Backbone.couchConnector.enableChanges = true; | |
Enables Mustache.js-like templating. | _.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g
};
var UserModel = Backbone.Model.extend({
defaults : {
name : "Anonymus"
}
});
window.CurrentUser = new UserModel();
|
The model for a message is kinda simple. We only need a name, a text and a date. | var MessageModel = Backbone.Model.extend({
initialize : function(){
if(!this.get("date")){
this.set({"date": new Date().getTime()});
}
}
});
|
Now let's define a new Collection of Messages | var MessagesList = Backbone.Collection.extend({
db : {
view : "messages",
changes : true,
filter : Backbone.couch_connector.config.ddoc_name + "/messages"
}, |
The couchdb-connector is capable of mapping the url scheme proposed by the authors of Backbone to documents in your database, so that you don't have to change existing apps when you switch the sync-strategy | url : "/messages",
model : MessageModel, |
The messages should be ordered by date | comparator : function(comment){
return comment.get("date");
}
});
var Messages = new MessagesList();
var PrivateMessage = MessageModel.extend({
});
|
Private messages get an own collection because they need a filter | var PrivateMessageList = Backbone.Collection.extend({
db : {
view : "none__",
changes : false, |
The filter avoids that private messages appear in the public stream.
If you don't know what filters are in CouchDB, then read it up here:
http://guide.couchdb.org/draft/notifications.html#filters
Look up how the filter works in | filter : Backbone.couch_connector.config.ddoc_name + "/private_messages"
},
url : "/private_messages",
model : PrivateMessage
});
var PrivateMessages = new PrivateMessageList();
|
Displays the current user's name and the message input field. | var InputView = Backbone.View.extend({
el : $('#input'),
regex : /@(\w+)/,
events : {
"click #send" : "onSubmit",
"keypress #message" : "keypress"
},
initialize : function(){
_.bindAll(this, "onSubmit", "nameChanged", "keypress");
CurrentUser.bind("change:name", this.nameChanged);
},
onSubmit : function(){
var message = $("#message").val(); |
Sanitize messages a bit before we send them to the server. | message = message.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');
if(message.length > 0){
var executed = this.regex.exec(message); |
Do a regex check to see if it is a private message | if(executed != null)
PrivateMessages.create({
"from" : CurrentUser.get("name"),
"to" : executed[1],
"message" : message.replace(executed[0], "")
});
else
Messages.create({
"from" : CurrentUser.get("name"),
"message" : message
});
}
$("#message").val("");
},
nameChanged : function(){
$('#name').text(CurrentUser.get('name'));
},
keypress : function(ev){ |
Send message on ENTER | if(ev.keyCode == 13)
this.onSubmit();
},
fillAndFocus : function(text){
$('#message').val(text).focus();
}
});
|
Represents a message entry | var EntryView = Backbone.View.extend({
tagName : "li",
template : _.template($("#entry-template").html()),
|
If there's a change in our model, rerender it | initialize : function(){
_.bindAll(this, 'render');
this.model.bind('change', this.render);
},
render : function(){
var content = this.model.toJSON();
$(this.el).html(this.template(content));
return this;
}
});
|
A private message | var PrivateEntryView = EntryView.extend({
className : "private",
template : _.template($("#private-entry-template").html())
});
|
The view for the chat messages | var MessagesList = Backbone.View.extend({
el: $("#messages"),
initialize : function(){
_.bindAll(this, 'reseted', 'addRow', 'addPrivateRow');
Messages.bind("reset", this.reseted);
Messages.bind("add", this.addRow);
PrivateMessages.bind("add", this.addPrivateRow);
},
|
Adds an entry row | addRow : function(comment){
var view = new EntryView({model: comment});
var rendered = view.render().el;
this.el.append(rendered);
},
addPrivateRow : function(private_message){
var view = new PrivateEntryView({model: private_message});
var rendered = view.render().el;
this.el.append(rendered);
},
|
Renders all comments into the table | reseted : function(){ |
reset the table | this.el.html("");
if(Messages.length > 0){ |
add each element | Messages.each(this.addRow);
}
}
});
var UserSession = Backbone.Model.extend({
});
var UserListCollection = Backbone.Collection.extend({
db : {
changes : true
},
url : "/user_list",
model : UserSession
});
var UserList = new UserListCollection();
var UserListEntry = Backbone.View.extend({
tagName : "li",
className : "user",
initialize : function(){
_.bindAll(this, 'remove_me');
|
When the session gets destroyed, the row will be destroyed too | this.model.bind("remove", this.remove_me)
},
render : function(){
this.el = $(this.el);
this.el.html("");
this.el.unbind();
this.el.text(this.model.get("name")); |
Insert "@username" into the input field | var temp = "@" + this.model.get("name") + " ";
this.el.click(function(){
Input.fillAndFocus(temp);
});
return this.el;
},
remove_me : function(){
that = this;
this.el.fadeOut(function(){
that.el.remove();
})
}
});
|
The list where all usernames are displayed | var UserListView = Backbone.View.extend({
el : $('#userlist'),
initialize : function(){
_.bindAll(this, 'reseted', 'addRow');
|
The view listens to the realtime updates of the couchdb changes feed | UserList.bind("add", this.addRow);
UserList.bind("reset", this.reseted);
},
addRow : function(model){
this.el.append(new UserListEntry({model:model}).render());
},
reseted : function(){
UserList.each(this.addRow);
}
});
var UserList = new UserListCollection(); |
The App router initializes the app by calling | var App = Backbone.Router.extend({
initialize : function(){
console.log("start");
UserList.fetch();
}
}); |
The current session will be stored in here | var CurrentSession = null;
var Input = new InputView();
|
Booststrap app after delay to avoid continuous activity spinner | _.delay(function(){
|
Destroy the current session on unload | $(window).unload(function(){
$.ajaxSetup({
async : false
});
if(CurrentSession != null)
CurrentSession.destroy();
});
|
Includes the couchlogin check it out here: https://github.com/couchapp/couchdb-login-jquery | $('#login').couchLogin({
loggedIn : function(user){
CurrentUser.set(user);
PrivateMessages.listen_to_changes(); |
Only add a User if it's not already in the list | if(!UserList.detect(function(user){return user.get("name") == CurrentUser.get("name");})){
CurrentSession = UserList.create({
"name" : CurrentUser.get("name"),
"logged_in_at" : new Date().getTime()
});
}
},
loggedOut : function(){
PrivateMessages.stop_changes();
CurrentUser.set(new UserModel().toJSON());
CurrentUser.trigger("change:name");
if(CurrentSession != null)
CurrentSession.destroy();
}
});
|
Bootstrapping | new MessagesList();
new UserListView();
new App();
}, 100);
});
|