Using handlebars with express and Backbone while maintaining SEO - javascript

I'm trying to figure out how to implement Backbone with handlebars (on the server and client) while maintaining SEO.
Consider that I have a RESTful API in where the urls look like:
/api/submissions/:type?/:category?:offset?:limit?
When someone hits mydomain.com/ it initializes my backbone here:
var site = window.site || {};
site.FeedSubmission = Backbone.Model.extend({
});
site.FeedSubmissions = Backbone.Collection.extend({
model: site.FeedSubmission,
url: '/api/submissions/?type=tip&offset=0,30'
});
site.FeedSubmissionView = Backbone.View.extend({
initialize: function() {
console.log(this.collection);
}
});
var submissions = new site.FeedSubmissions();
submissions.fetch({
success: function(coll, res, options) {
var feedSubmissionView = new site.FeedSubmissionView({
collection: submissions
});
feedSubmissionView.render();
},
error: function(coll, res, options) {
console.log('woops')
}
});
var feedSubmissionView = new site.FeedSubmissionView({
collection: submissions
});
This works and instantiates the view with my collection, but if a search engine tries indexing the site, it'll obviously cause trouble because the rendering of the view is being done with JS.
Basically I want the best of both worlds: I want to use Backbone but also get the benefit of serving html to search engines. I'm not going to spin up a phantomjs instance or some headless browser JUST to serve my content to search engines. Primarily because it's too much overhead and flaky at best.
What do I need to do? Bootstrap data into some json and dump that into the page and override Backbone's fetch and sync to populate the models/collections?

Related

Multiple Backbone.js collection options?

Intro:
Building in node.js and express on the backend, I am sending a res.json(details) to the localhost:3000/me route containing the users session information.
So that on the client side I can work with that specific user, for example on the client side initialization I write some code like this.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
You see I fetch the me model and use that to filter the messages in the MessagesCollection.
Question:
In my MessagesCollection I pass options like this.
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
model: MessageModel,
//url: '/api/messages'
});
This is great for getting the desired models when using var messages = new MessagesCollection([], { id: response.get('user_id') });
My question is when somewhere else I run window.App.data.messages.create(Message); this wants to POST to /api/messages/:id when I want to POST to the regular collection?
Theory:
So obviously I would say the best thing is to rethink how I filter the models in the fetch method.
So basically to simplify this question I need to filter the collection upon .fetch() GET... and set no filters upon .create() POST
BTW I am using marionette, maybe that can help with something?
model.url() Returns the relative URL where the model's resource
would be located on the server. If your models are located somewhere
else, override this method with the correct logic. Generates URLs of
the form: "[collection.url]/[id]" by default, but you may override by
specifying an explicit urlRoot if the model's collection shouldn't be
taken into account.
Delegates to Collection#url to generate the URL, so make sure that you
have it defined, or a urlRoot property, if all models of this class
share a common root URL. A model with an id of 101, stored in a
Backbone.Collection with a url of "/documents/7/notes", would have
this URL: "/documents/7/notes/101"
http://backbonejs.org/#Model-url
So you can define method url at MeModel and generate url there ( if there are no other users - you can just return string "/me" or generate in based on model properties ( for example switch if model has id )

Converting Backbone.js App to communicate with parse.com

I am an IT student and I'm learning how to use Backbone.js. I read all the documentation but I find it easier to learn when I use example apps,because I never have been programming this type of apps,so it was hard and confusing to think of a way to build my own app, so I used https://github.com/dperrymorrow/example-backbone-app to make similar edited app. The example app doesn't have a server side.
Now I need to connect the app to use parse.com as a backend(server-side) instead to use local collection.
If someone could please tell me what should I change and transform in the code so it connects example app to parse.com app with REST API so when I edit something in the app to be syncronized with parse.com.
I will be really grateful if someone is willing to explain this in a more descriptive way than saying :"you should read documentatin" because I did,and I still don't get the point :)
Have a nice day.
It's just about having the right backbone models and collections and settings the right url on the collection and urlRoot on the model. Then you can just can just call backbone methods like sync, save or delete.
Best detailled answer covering also the REST explanation probably is this one.
Cant you just swap the backbone collection and model to Parse's ones?
Parse.com is a webservice providing REST interfaces for anything you like, Lets connect that to our Backbone models.
First of all Lets create a new app on Parse.com, mine is called FunkyAppartments.
Insert the script tag for loading Parse javascript lib into index.html or whathever:
<script src="http://www.parsecdn.com/js/parse-1.5.0.min.js"></script>
Switch the backbone model and collection to use parse types instead (and rename the fetch method if you have extended backbones, since we do not want to overide the one of parse):
//var Appartment = Backbone.Model.extend(); Backbone wo. Parse.com
var Appartment = Parse.Object.extend("Appartment");
//var Appartments = Backbone.Collection.extend({ Backbone wo. Parse.com
var Appartments = Parse.Collection.extend({
model: Appartment,
initializeData: function(){
var self = this;
var callback = function (data){console.log(data); self.reset(data)};
S.Appartments.loadAppartments(callback);
},
loadAppartments: function(callback){
debugger;
this.query = new Parse.Query(Appartment);
var result = this.fetch();
callback(result);
return result;
}
});
I added a debugger tag in the load appartments so that developer tools breaks in the middle of the controller, here I have access to the Appartment private type of the controller, hence i can store some data on the parse server and verify by pasting the below in the developer tools console.
var testAppartment = new Appartment();
testAppartment.save({name: "foobars"}).then(function(object) {
alert("yay! it worked");
});
Yei, the data shows up in the parse.com UI for the app we just added there. And more importantly it shows up in our frontend. That was easy!

Node.js server emit data to backbone model

I'm trying to integrate socket.io with backbone.js, so basicly I have a node server that gets data from a database and emit the data to a backbone client, the client's model should somehow retrieve the incoming data but I'm not sure how to put socket.io in the model now, just getting confused after lost of tries. Any expert please enlight me would be much appreciated!
Node server emit data to client in url /pics
app.get('/pics', function(req, res){
db.collection('pics', function(err, collection) {
collection.find().toArray(function(err, items) {
io.sockets.on('connection', function (socket) {
socket.emit('news', items);
});
});
});
});
Client in Backbone model should retrieve the emitted data(this model works for normal http data sending):
window.Pic = Backbone.Model.extend({
urlRoot: "/pics",
idAttribute: "_id",
initialize: function (){},
defaults: {
_id: null,
name: "",
date: "",
}
});
window.PicCollection = Backbone.Collection.extend({
model: Pic,
url: "/pics"
});
I must admit using Backbone on node + socket.io is quite interesting. Still haven't put my mind into it though. First thing first, have you considered using a plugin Backbone for websockets? Some people have created such.
To give another approach, I think you don't really have much of a choice. Using socket.io to listen to incoming data would result in something like that:
socket.on('model', function(attributes) {
// do something
});
So you'd have to have access to some collection where you could update manually your model.
If you wish to really use socket.io INSIDE your model, a more transparent way would be to extend your model to start listening to some custom event on its creation. I'll link a jsfiddle to show what could be done.
Edit: here you go.
Note that this is full of boilerplate. I'm trying to figure out a way to do a generic socketModel. I'll update if I find one.

Javascript Backbone model design

Fairly new to JavaScript so it might be a noobish question.
At the moment for my project I'm using NodeJS for my server and Backbone for the client. The client will send a request to the server and the server will send list of files in the server, my aim was to simply return the list of files and when user click on the file it will send another request to the server to load the file.
Currently in the client level my model and collection is defined something like:
app.MyFile = Backbone.Model.extend({
defaults: {
modifiedDate: new Date(),
path: '',
content: '' // content of the file
}
});
var MyFileList = Backbone.Collection.extend({
model: app.MyFile,
url: '/api/files'
});
// create global collection of files
app.MyFiles = new MyFileList();
app.AppView = Backbone.View.extend({
initialize: function () {
// fetch all files
app.MyFileList.fetch();
}
});
// app.js (point of entry)
$(function() {
// Kick things off by creating the **App**.
new app.AppView();
});
And my server code:
var application_root = __dirname,
express = require("express"),
...
app.get('/api/files', function(req, res) {
...
// return file list
}
app.get('/api/files/:id', function(req, res) {
...
// return file content?
}
Since it doesn't make sense to load all files in the directory and send it back to the client, what I did was I created the model in the server and fill up modifiedDate and path while leaving content to null. But the problem now is that how do I fill up the content when user clicks on the file? I'm not sure how to manually send an HTTP request from Backbone View or controller. Or is there any better way of doing this? One way that I can think of is to create another model that only keeps modifiedDate and path but to me this looks very verbose and repetitive.
Given what you have on the client side, you may not need anything more.
app.MyFiles = new MyFileList();
app.MyFiles.fetch().done(function() {
// your collection is fetched but each model's content is empty.
// now, I assume at this point you will show them in some view/views.
});
Now when one of those things is clicked on, you can fetch the content.
var model = app.MyFiles.get(id);
model.fetch().done(function() {
// now the model's content attribute will be set
});
This might work with no more code than what you showed. Because the url a model uses to fetch is created by default by appending the model's id to the end of its collection's url.
So from your server, you return a json array from '/api/files': [{id:1, path:'foo'}, {id:2, path:'bar'}]
Then from '/api/files/1': {id:1, path:'foo', content:'whatever'}
When user clicks the file, you can call backbone's fetch method on the model. Then your model will be filled with data from the server.
Note that for this to be working you should return collection from the server first, where models at least have id's. Every other field will be filled after fetch call. Also, you should override model url, if it differs from standard (which is collection/id).

Backbone.js in an offline application

I am using Titanium to build a desktop app using web technologies. I decided to use Backbone.js as my mvc. The problem is the application runs not on a server. This is my Backbone model and collection:
window.Student = Backbone.Model.extend({
initialize: function(){
this.bind("save", this.value_change);
},
value_change: function(){
alert("Student model saved for : " + this.attributes.first_name);
},
urlRoot : http://localhost:8080/student/,
});
window.Students = Backbone.Collection.extend({
model: Student,
url: 'http://localhost:8080/students/',
});
and try fetching the values from the server using
var students = new Students
students.fetch()
I get this error:
message: "'undefined' is not an object (evaluating '$.ajax')"
I am assuming this has to do with the url part. It is not able to fetch the values from the server. Any Ideas?
The problem is backbone saves models on a server. It does this by sending ajax requests to your server. What you want to do is overwrite the persistence mechanism
Use backbone.localStorage to save state in localStorage rather then a database
collection.fetch() will fire a reset event on the collection, and not a save event. Save is a Model method to execute a POST request to your server, when you want to persist your instance model on your server.
You should try this instead :
window.Student = Backbone.Model.extend({
});
window.Students = Backbone.Collection.extend({
model: Student,
url: 'http://localhost:8080/students/',
initialize: function(){
this.bind("reset", this.value_change);
},
value_change: function(){
alert("Students fetched ");
},
});
var students = new Students();
students.fetch();
I'm not sure what you mean when you say
The problem is the application runs not on a server
But if your javascript does not run on the same domain as your server, you may have some cross domain javascript issues... Here is post with an exemple using Ruby on Rails : http://www.tsheffler.com/blog/?p=428
Thanks for all your answers. The problem laid in loading jquery after backbone. I loaded jquery first and it worked out fine. Thanks to parshap from the irc of #documentcloud.
Try https://github.com/Ask11/backbone.offline
Allows your Backbone.js app to work offline

Categories