How to show page loading between pages - ruby-on-rails

I am not sure if all Rails app behave the same or is it just within my app's setting. For my app when a page is loading (So the moment when a link is clicked until the content is responded from the server) there is no indication of page loading at all. In general website during the page load there would be a spinning wheel in the place of the favicon to show that the page is loading. I am using Rails 4.0.0 on Heroku.

From Turbolinks, something like?
$(document).on('page:fetch', function() {
// show spinner
});
$(document).on('page:change', function() {
// hide spinner
});
Update for Turbolinks 5
For Turbolinks 5:
$document.on('turbolinks:click', function() {
// show spinner
});
$document.on('turbolinks:load', function() {
// hide spinner
});
If you are using jQuery and your spinner is in the div.spinner, you can show and hide it with:
$(document).on("turbolinks:click", function(){
$(".spinner").show();
});
$(document).on("turbolinks:load", function(){
$(".spinner").hide();
});

Rails uses turbolinks by default.
Please take a look at turbolinks and how it works. It basically replace the body of the page instead of making a new request, that's why you don't see the loading indication in the browser.
If you want to see your page loading you can either disable turbolinks, removing it from application.js, or use a gem like this one: https://github.com/caarlos0/nprogress-rails to show the actual loading of the page.
I strongly suggest you to keep turbolinks and go with the second option I gave you.

Those sites are what we call Single Page Applications most of the time. Those sites use a lot of AJAX based content fetching mechanism, and this thing has to do with front-end programming and a little with back-end programming.
If you really want to achieve this functionality, you can use AngularJS, BackboneJS and like other framework who support Single Page Web Applications.
When you fetch another page form server, all of your HTML, CSS, and JS is gone, and the new content is fetched and rendered. So there is no way that you can show a loading-page in between unless you use AJAX based pages.

Related

Dynamically Creating and Removing Pages in jQuery Mobile 1.4.4

After I create some page dynamically and insert it into body, what should I run to let the jquery mobile know that this page is new in order to initialize (or whatever jquery mobile needs to do with it)?
How to remove the page dynamically (and let jquery mobile know, if that is needed)?
I am aware of $.mobile.initializePage(). This goes through all the pages and do something on them. I was hoping that there is something that can be done only on the new page. Is it?
If this is relevant I am using Pagecontainer widget to do all the navigation.

Architecture for jQuery Mobile site with a lot of pages

I've got a website that I'm converting into an app using JQM. I've read about Pages and how the DOM loads but I'm still uncertain how to architect the site.
The main page of the application is based on the Google Maps API which uses JS to load. There are 150+ target pages so I don't want them to load until the user taps the link. All of the target pages also require JS to initialize. When they return to the main page the cached state should be the default but I also need the option to run JS if the query string changes. Content doesn't change often so my preference would be to cache data once loaded but there would need to be some way to flush the cache.
I converted the site to JQM. The target page JS didn't run so I added rel='external' to the links. The JS now runs on the target but when I link back to the main page it reloads the page without running initializing the JS. The obvious solution would be to add rel="external" but then I'd be defeating all performance value. Any recommendations on how I should structure it?
Using rel=external your links will not be loaded with Ajax and you will lose animated page transitions. If you want to run some script when a page displays, use this page event:
$(document).on("pageshow", "#selector", function(event, ui) { /* your code */ });
This and other useful events are described in jQuery Mobile API Documentation.
For example, pagecreate (the now deprecated pageinit) is called once when the page initializes.
About getting query string parameters, see this answer.

How to completely disable Jquery mobile

We encounter the following problems with Jquery Mobile.
Our site is divided in a mobile and a fixed desktop site.
Both use the same database and php code. Only the templates are different.
On our mobile site we use Jquery mobile for a better user experience and that works fine. However we integrated a button "goto desktop".
This link should bring us back to our "normal" desktop site.
But there is the problem. In the desktop-site, Jquery mobile is still activated and it replaces drop down fields, input fields and make a complete mess of the desktop site.
We tried everything to disable JQM but nothing seems to work.
How we can switch from our mobile site template to the desktop site template and disable JQM completely when we are on the desktop template?
Thanks a lot for help!
There are few available solutions but only one will really do.
Working example: http://jsfiddle.net/Gajotres/NvEcW/
Few things are needed, first we need to set this:
<script>
$(document).on('mobileinit', function () {
$.mobile.ignoreContentEnabled = true;
});
</script>
it will give us an ability to programatically turn on/off content enhancement. If you already don't know this mobileinit event must be initialized before jQuery Mobile initialization but after the jQuery initialization. This must always be a part of a page.
There's one last step. When we want to move from mobile to desktop page we need to reload page and use this javascript:
$(document).on('pagebeforecreate', '#index', function(){
$(this).attr('data-enhance','false');
});
Pagebeforecreate event is important because at this point content is still not enhanced and attribute data-enhance = false will prevent any further page enhancement. If you want to turn it on again just set attribute value to true.
If you want more solutions then take a look at my other answer, search for the topic Methods of markup enhancement prevention : jQuery Mobile: Markup Enhancement of dynamically added content.

Using angularjs with turbolinks

I am trying to use Angularjs framework in my app with turbolinks. After page change it do not initialize new eventlisteners. Is it any way to make it work? Thanks in advance!
AngularJS vs. Turbolinks
Turbolinks as well as AnguluarJS can both be used to make a web application respond faster, in the sense that in response to a user interaction something happens on the web page without reloading and rerendering the whole page.
They differ in the following regard:
AngularJS helps you to build a rich client-side application, where you write a lot of JavaScript code that runs on the client machine. This code makes the site interactive to the user. It communicates with the server-side backend, i.e. with the Rails app, using a JSON API.
Turbolinks, on the other hand, helps to to make the site interactive without requiring you to code JavaScript. It allows you to stick to the Ruby/Rails code run on the server-side and still, "magically", use AJAX to replace, and therefore rerender, only the parts of the page that have changed.
Where Turbolinks is strong in allowing you use this powerful AJAX mechanism without doing anything by hand and just code Ruby/Rails, there might come a stage, as your application grows, where you would like to integrate a JavaScript framework such as AngularJS.
Especially in this intermedium stage, where you would like to successively integrate AngularJS into your application, one component at a time, it can make perfectly sense to run Angular JS and Turbolinks together.
How to use AngularJS and Turbolinks together
Use callback to manually bootstrap Angular
In your Angular code, you have a line defining your application module, something like this:
# app/assets/javascripts/angular-app.js.coffee
# without turbolinks
#app = angular.module 'MyApplication', ['ngResource']
This code is run when the page is loaded. But since Turbolinks just replaces a part of the page and prevents an entire page load, you have to make sure, the angular application is properly initialized ("bootstrapped"), even after such partial reloads done by Turbolinks. Thus, replace the above module declaration by the following code:
# app/assets/javascripts/angular-app.js.coffee
# with turbolinks
#app = angular.module 'MyApplication', ['ngResource']
$(document).on 'turbolinks:load', ->
angular.bootstrap document.body, ['MyApplication']
Don't bootstrap automatically
You often see in tutorials how to bootstrap an Angular app automatically by using the ng-app attribute in your HTML code.
<!-- app/views/layouts/my_layout.html.erb -->
<!-- without turbolinks -->
<html ng-app="YourApplication">
...
But using this mechanism together with the manual bootstrap shown above would cause the application to bootstrap twice and, therefore, would brake the application.
Thus, just remove this ng-app attribute:
<!-- app/views/layouts/my_layout.html.erb -->
<!-- with turbolinks -->
<html>
...
Further Reading
AngularJS bootstrapping: http://docs.angularjs.org/guide/bootstrap
Railscasts on Turbolinks (explains callbacks): http://railscasts.com/episodes/390-turbolinks
Demo application: https://github.com/fiedl/rails-5-angular-and-turbolinks-demo
Turbolinks attempt to optimize rendering of pages and would conflict with normal bootstraping of AngularJS.
If you are using Turbolinks in some places of your app and some parts use Angular. I propose this elegant solution:
Each link to a page that is angularapp (where you use ng-app="appname") should have this attribute:
<a href="/myapp" data-no-turbolink>Say NO to Turbolinks</a>.
The second - mentioned on Stackoverflow is explicitly reloading/bootstrapping every ng-app by handling page:load event. I would that's intrusive, not to mention you're potentially loading something that isn't on a page hence wasting resources.
I've personally used the above solution.
Hope it helps
In case of bug
Uncaught Error: [ng:btstrpd] App Already Bootstrapped with this
Element 'document'
after upgrade to angular 1.2.x you can use below to fix problem.
angular.module('App').run(function($compile, $rootScope, $document) {
return $document.on('page:load', function() {
var body, compiled;
body = angular.element('body');
compiled = $compile(body.html())($rootScope);
return body.html(compiled);
});
});
In previous post #nates proposed to change angular.bootstrap(document, ['YourApplication']) to angular.bootstrap("body", ['YourApplication']) but this causes a flash of uncompiled content.
Add the following event handler to your application.
Coffeescript:
bootstrapAngular = ->
$('[ng-app]').each ->
module = $(this).attr('ng-app')
angular.bootstrap(this, [module])
$(document).on('page:load', bootstrapAngular)
Javascript:
function bootstrapAngular() {
$('[ng-app]').each(function() {
var module = $(this).attr('ng-app');
angular.bootstrap(this, [module]);
});
};
$(document).on('page:load', bootstrapAngular);
This will cause the angular application to be started after each page loaded by Turbolinks.
Credit to https://gist.github.com/ayamomiji/4736614
Turbolinks doesn't quite make sense with an client side MVC framework. Turbolinks is used to to strip out the all but the body from server response. With client-side MVC you should just be passing JSON to the client, not HTML.
In any event, turbolinks creates its own callbacks.
page:load
page:fetch
page:restore
page:change
The jquery.turbolinks plugin can trigger bootstrapping of modules via ng-app directives. If you're trying to manually bootstrap your modules, jquery.turbolinks can lead to ng:btstrpd errors. One caveat I've found is that jquery.turbolinks relies on the page:load event, which can trigger before any new page-specific <script> tags finish running. This can lead to $injector:nomod errors if you include module definitions outside of the application.js. If you really want your modules defined in separate javascript files that are only included on certain pages, you could just disable turbolinks on any links to those specific pages via data-no-turbolink.
Based on the comments I've seen, the only valid scenario for using both together in a way where Angular would conflict with Turbolinks (such as where I allow Angular to handle some of the routing) is if I have an existing application that I'm trying to port to Angular.
Personally, if I were to do this from scratch, I think the best solution would be to decide what should handle the routing and stick with that. If Angular, than get rid of Turbolinks -> it won't do much for you if you have something close to a single-page app. If you allow Rails to handle the routing, then just use Angular to organize client-side behavior that can't be processed by the server when serving up the templates.
Am I missing a scenario, here? It doesn't seem elegant to me to try to split the routing responsibilities between different frameworks, even in a large application... Is there some other scenario where Turbolinks would interfere with Angular other than refreshing the page or navigating to a new route?
Using Turbolinks and AngularJS together
+1 to #fiedl for a great answer. But my preference is to make use of page:change in concert with page:load because this affords some flexibility: the DOM can receive a page:load event from sources other than turbolinks, so you might not want to have the same callback fire.
Watching for a page:change, then a page:load should restrict your callback behaviour to solely turbolinks-instigated events.
function boostrapAngularJS () {
angular.bootstrap(document.body, ['My Application']);
addCallbackToPageChange();
}
function addCallbackToPageChange() {
angular.element(document).one('page:change', function () {
angular.element(this).one('page:load', boostrapAngularJS);
});
}
addCallbackToPageChange();
(This will allow/require you to keep your ng-app declaration in your html, as normal when working with AngularJS.)
Turbolinks automatically fetches the page, swaps in its <body>, and merges its <head>, all without incurring the cost of a full page load.
https://github.com/turbolinks/turbolinks#turbolinks
So, instead of append ng-app directive on the <html> element, we can just do it on the <body> element.
<html>
<body ng-app=“yourApplicationModuleName">
</body>
</html>

jquery css not applied with angularjs routing

I wish to implement angularjs with jquery mobile all works fine except that the jquery mobile css is not applied.
It works when I don't use routing in angularjs but with routing, the css stops working.
What do I do? I also included the jquery-mobile-angular-adapter still it's not working.
"if you generate new markup client-side or load in content via Ajax and inject it into a page, you can trigger the create event to handle the auto-initialization for all the plugins contained within the new markup." - http://jquerymobile.com/demos/1.0/docs/pages/page-scripting.html
To solve this problem you have to tell JQuery to initialize all plugins after Angular loads the content, you can do this in your controller.
$scope.$on('$viewContentLoaded', function() {
$(document).trigger('create'); //$(document) or just the part that was loaded with ajax
});
I don't know jquery-mobile-angular-adapter, so I can't help with that.

Resources