The node.js server has two roles:
RESTFul API on routes with prefix /api
Renders website pages on the others routes (/ /plans /features /terms ...)
For the moment, all my pages render a "Loading page..." combined with the Backbone application that replaces the DOM when the Backbone.router starts.
I would like to build the website pages server-side when an user reaches a page and let Backbone handles the next part of the navigation when the user navigates on the website.
I know how to do it server-side but client-side when a page is loaded with the DOM already built, the Backbone.router loads and then replaces the DOM because it does not know that the view is already preloaded.
How to fix it client-side?
Code:
The router: http://pastebin.com/aUuXaVm9
Home view: http://pastebin.com/qS1tHUfq
Terms view: http://pastebin.com/et1mrbLK
Update: the new code: https://gist.github.com/mathieug/d50c861e63dd647f1c2b
Now I need the runSlider method to be called at the first load.
When you start the History, make sure to pass {silent: true} as an option to let Backbone know that you've already loaded a complete page. This will prevent the router from replacing the DOM when the view is already preloaded (the first time).
From the Backbone.js docs:
If the server has already rendered the entire page, and you don't want the initial route to trigger when starting History, pass silent: true.
So, your code should look like:
Backbone.history.start({pushState: true, silent:true});
Related
So, this is not that simple to explain, but I have my Ember app, with my routes, controllers and views etc, it's about jobs listings, this is the most important part of the app, but I also have other models, for users and companies. So, in the ApplicationRoute I'm doing this thing here:
renderTemplate: function() {
this.render({ controller: 'jobs' });
}
because on my jobs controller I need to count the amount of listings I have, to display in a menu that shows up at every page, like this:
I'm getting this number by doing this at the jobsController:
jobsCount: function() {
return this.get('model.length');
}.property('#each')
which works fine because my homepage is the listing of jobs, which calls /api/jobs/ with all of them. The problem happens when I start the app in any other page, like accessing /jobs/:id/ directly, /admins/ or anything else, the count would be zero, since the jobsController is not loaded and I don't get the number of jobs from the api (accessing /api/jobs/:id/ directly, for example). After accessing the homepage the number of ads load, since it calls /api/jobs/ with all the jobs on it. So, one of the workarounds for this was setting ApplicationsRoute's model to be jobs as well, which resulted in two requests every time the application loads for the first time, that was working fine but I don't like the idea of having to do two requests just to get a number, especially when most of the time the first page to be loaded is the homepage, resulting in two requests to the same URL (/api/jobs/).
Is there anyway to do this without doing two requests, like if I visit a job listing (/jobs/:id/) to force it to load /api/jobs/ and then look up for the id, instead of loading /api/jobs/:id/, or this is not a good solution as well? Should I have a specific URL on the API just to load this numbers?
Is the question clear enough?
Thanks.
I had a similar problem and ultimately I decided to break DRY. In your example this would mean rendering the job_count in the json rendered by all routes that could possibly be called when the user enters the application. This makes the app's design slightly messy, but means that you never have to make multiple calls.
However, if your app gets to where there is a lot of unrelated data that ember needs access to when the user first loads the app, then you should make a url specifically for loading that data and yes, make two requests upon initial load. As far as UX goes, a marginally longer initial load time is irrelevant because of the improvement ember gives for every route entered there-after. Remember, by rendering client-side ember will always be slower upon initial load than a traditional server-rendered app anyway.
I'm afraid that's trade-off that has to be made.
I would do it something more like this. In your JobsRoute, you can do something like this:
App.JobsRoute = Ember.Route.extend({
model: function(params) {
var jobs = this.store.find('job');
jobs.then(function() {
this.store.find('job', params.job_id);
});
}
});
First, we'd find all the jobs by calling this.store.find('job'). This will return a promise, which we wait for it to resolve and populate the store with all the jobs. Then, we find the job in particular we're looking for. This way all the jobs are loaded, in just one request, and only one job is represented by your job controller.
I am creating single page application using marionette.js.My url looks like when user clicks on privacy is http://192.168.1.4:8080/Client_bit/index.html#privacy I am trying to make SEO freindly urls like http://192.168.1.4:8080/Client_bit/index.html/privacy is it possible with marionette.js?
Yes, and it's done the same way as Backbone: you simply start the router with pushState: true (see http://backbonejs.org/#History-start)
However, make sure your webserver returns a valid page for EVERY URL your marionette app generates, or pushState won't work...
Imagine I'm on:
http://example.com/
I click in a link containing a hashbang fragment identifier, let's say:
http://example.com/#!/login
I would like it to show the login form without reloading the page. How is this done?
Have a look at jQuery Ajax to find out how to load new content without refreshing the page, then maybe move on to BackboneJS and use its routers to get the hash thing working.
Edit:
Here's a tutorial, I'm not going to write one for you. But it comes down to having a server side which is able to provide the needed content for you (a log in form), whether it does so asynchronously or synchronously is besides the point. Then using a Backbone Router which will read the hash bang and call the right JavaScript function based on this hash bang, this JavaScript function should live inside a Backbone Controller, and it should handle the instantiating of a new Backbone View and adding it to the DOM. The Backbone View could be added to a predefined Backbone Region, and could be loading an UnderscoreJS template to make things even easier.
If you would like a much simpler solution you should take a look at another concise answer here: Handle URL anchor change event in js. In summary, you periodically check window.location.hash for changes and run your javascript to bring up the login content.
I have a simple OAuth verification set up using Backbone, and it's working fairly well. My question is somewhat nitpicky (though... I am also new to Backbone), but I'm hoping to find somebody who might know how to solve this.
I have a Session model which, at initialization, sets a #authenticated value based on the presence of a value in localStorage. There's also a method in here, authenticate(), which checks the #authenticated value for pass/fail. If the value check fails, it uses my router to navigate to the login route. If the value check passes, an optional callback passed in by the user is run.
In my main AppView (the first View run at application start) I run Session.authenticate(), and if it passes, route to "#home" (and my Router handles loading additional Views).
My question is this: as an un-authenticated user, if I type http://url.com/#home into my browser, I am successfully routed to "#login", but if I bring up my DevTools, I can see a request being made for an image in my "HomeView" view. What am I not understanding about how Backbone flows through this process? Shouldn't the route for "#home" not even run until after the application initializes, and therefore not even attempting to load the "HomeView"?
What kind of templating engine are you using? If your templates are for instance inline, inside the HTML template where your backbone app lives, then I believe any images inside those are rendered when the page loads. I may be wrong. Also, ensure your HomeView is not running by logging something to the console in the view's initialize method.
I have just begun to port a layered single page js app onto backbone.js and was trying to understand how to handle composite url parameters with routes and spalts in backbone.js. The backend has rails and sends JSON.
There are various entities (models) like filters, dimensions, features, questions which can be passed via request parameters.
URL 1
/display/#widget?id=42&fon=1,2,4&foff=6,9,19&q=1a2bc3abc4d
URL 2
/display/#widget?id=42&compare=345,567,90&fon=1,2,4&foff=6,9,19&q=1a2bc3abc4d
How to i structure these non-restful urls, keep the same functionality and allow bookmarkability.
Thanks
Backbone's router, for the purpose of invoking views, cares only about the hash portion of window.location. However, it does keep track of the search portion for the purpose of maintaining the browser history.
Therefore, the decision about bookmarkability is your responsibility: the hash will invoke a specific route, and what views that route hides or shows is up to you. How those views parse the search string and react is also up to you.
I can see what you want to do: change a model through the search function, then render it. It's a bit of a two-step trigger: hash-change -> model-sync -> show-view. Structuring that sounds like it'll be fun. But Backbone is capable.