balderdashy / sails

Realtime MVC Framework for Node.js
https://sailsjs.com
MIT License
22.82k stars 1.95k forks source link

Single page apps with routing + Sails.js #1203

Closed lwansbrough closed 10 years ago

lwansbrough commented 10 years ago

How can I set up a one page app with Sails? I basically need all routes to point to index.html, except for those under /api and any static files served from the public folder. Is this possible?

markschad commented 10 years ago

Your routes won't be serving html content, but rather -- probably -- JSON data. Place index.html in your assets directory, then, your AJAX requests will make requests to your routes.

For example; if you would like your SPA to display a list of all Users, you would make an AJAX request to, say, user/find. Then, in your User controller, create an action that looks something like the follow, depending on how your model is set up:

'find': function(req, res) {

  User.find({}, function(err, users) {

    if (err) return res.send(500, 'Something went wrong');

    if (!users) return res.send(200, 'No users exist.');

    // Sending the list of all users in the form: [ { id: 1 ...}, { id: 2 ...}, ... ]
    return res.send(200, users);

  });

}
lwansbrough commented 10 years ago

Yeah I understand that much. I'm using Backbone for the client and have done something similar to above - however if you're using Backbone with HTML5 History (pushState) then your client side routes must be resolvable: ie, every route that is received by the server other than resources and anything under /api must point to your single page app. I've resolved this by adding a route to my Sails application which points everything (save for resources and api calls) to a controller with a method that responds with the index.html file.

mikermcneil commented 10 years ago

This might be helpful: github.com/mikermcneil/backbone-to-sails

With our team, I've found that it's most effective to use #!/foo-style routes on the client. These are still SEO-able (most easily through PhantomJS or a service like Brombone, and allow you to avoid the complexity and browser compatibility issues that come along with using pushstate. Sailsjs.org is a single-page Backbone app and search engines seem to be able to find it (i.e. try searching google for node framework or sails migration guide )

mikermcneil commented 10 years ago

And btw, great question :) I'm going to go ahead and close this since it's not an issue w/ the Sails core. For future reference, StackOverflow, IRC, or the Google group are the best places to get help with this kind of stuff.

mikermcneil commented 10 years ago

@markschad nice answer

lwansbrough commented 10 years ago

Thanks for your help Mike - much appreciated.

On Dec 14, 2013, at 7:46 PM, "Mike McNeil" notifications@github.com wrote:

Closed #1203.

— Reply to this email directly or view it on GitHub.

MMRandy commented 10 years ago

Sorry if this is off topic, but I'm researching Sails.js for Backbone friendly SPA development and wanted to chime in here as I just handled this on a project last month.

You can still use pushState and maintain "SEO-able" links without having to use the ugly #! in your urls. You simply need to add the following meta tag to your SPA index.html (or ajax page):

<meta name="fragment" content="!" />

That essentially does the same thing as #! for Googlebot, which will force it to re-request the page with the trailing "_escapedfragment=" querystring param which you can then detect and send on to your HTML Snapshot generator (PhantomJS, etc.).

Thanks...Sails sure looks cool! Randy

lwansbrough commented 10 years ago

That's a decent solution - however I'm wondering if this is better than just sniffing for bots. With the latter you can target more than just Google (or whomever is supporting the "fragment" meta).

MMRandy commented 10 years ago

Google, Bing, and Yahoo support that meta-tag, though I question why you would need anything other than Google :)

Also, Google recommends against sniffing for bots as they could change their names at any time, which, as a matter of fact, they are in the process of doing (http://googlewebmastercentral.blogspot.com/2014/01/a-new-googlebot-user-agent-for-crawling.html)

This is of course not a big deal to overcome. But the official Google Spec does recommend the fragment detection methodolgy

npfitz commented 10 years ago

@lwansbrough Could you share what the route you defined was? IE: How do I create a route that encompasses everything except the resources?

Thanks!

lwansbrough commented 10 years ago

I ended up using middleware so that I could do a regex match against the route. If it matches, respond with the index.html file.

npfitz commented 10 years ago

Ahh, fair enough! I was wondering if I was able to use the /:unknownRoute route to point to like, index.ejs or something. But it's not working out :/ lol

pnevyk commented 10 years ago

I was solving the same problem. And my solution is that I always render index view instead of 404 using res.view() in "not found" handler. It's simple but I am not sure if it's good practice (probably not). However, it seems to work. Maybe, better solution would be if there is a configuration option which allow us to render index page if no routes were matched.