Goal:
The goal is for the end user to see the updated website without having to manually clear the cache.
Setup :
Client side : Angular 1.4
Server Side : Azure Web Apps
Deployment method : Azure links to a branch on a bitbucket account and detects updates pushed to this account from git.
Issue :
Quality Assurance has reported that she could not see the updates. I mentioned that this is probably the cache and to clear the cache to see the updates as mentioned in the following website:
https://pixpa.zendesk.com/hc/en-us/articles/201555604-My-website-changes-are-not-showing-up-How-can-I-clear-my-browser-s-cache-
She pointed out that the users of this website would not know about clearing their cache to see an update (Even it is as easy as pressing ctrl+shift+r).
Research and steps taken to try and resolve the issue:
I researched this topic and added the following meta tags into the index.html to stop caching :
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
I tested this change by updating the title of the page and this seemed to work on her end in that the title of the page updated without her needing to clear her cache. However, today the QA has reported that the page was not displaying correctly and she needed to refresh her cache to see the page correctly. A lot of changes were made in the Model/view/whatever structure so I cannot determine where in the structure this breaking change would have occurred.
To me, this seems like a very common requirement for the users to see website updates. What is a best practice approach for the end user to automatically see website updates? Any helpful advice or steps I could follow would be greatly appreciated.
This should probably solve your issue. I found it here:
https://www.reddit.com/r/angularjs/comments/3j77a2/prevent_browser_template_caching/
$httpProvider.interceptors.push(['constants', function(constants){
return {
request: function(config){
if(config.url.indexOf('partials/') > -1){
var separator = config.url.indexOf('?') === -1 ? '?' : '&';
config.url = config.url + separator + 'c=' + constants.cache;
}
return config;
}
};
}]);
This will add a cache-busting parameter to your requests to get template files. The example looks for all calls to /partials, but I would suggest changing this to look for requests ending in .html.
The simple method of doing this is just using +new Date() instead of constants.cache. That way it will always pull the most current version of the template file. If you don't care about caching the template files when they haven't been changed, this is the easy fix.
$httpProvider.interceptors.push([function(){
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
return {
request: function(config){
if(!endsWith(config.url, '.html')) return config;
config.url = config.url + '?c=' +new Date();
return config;
}
};
}]);
Browsers should only cache partials if the .html file that was served had a cache control header that told them to cache it.
This answer shows the header required to prevent any caching: How to control web page caching, across all browsers?
This will only apply going forward of course. Users who have already cached a partial will need to refresh.
In the interests of reducing load on your server you may want to modify the header, for example to cache for one day, or 1 hour, or whatever meets your release cycle / user requirements.
I don't think your approach of using HTML meta tags will work in this case as partials aren't loaded/rendered as html pages by the browser as part of a normal page pipeline. They are loaded asynchronously and compiled by Angular. So you need to get the cache control in at a lower level (i.e. on the header). This may be browser specific, but if they aren't working I'd expect that is why.
If the view is not displayed correctly this is css and/or javascript issue (the browser cache the css and js). The easiest work around is to put random number or in my case I put my web version in my css and js file and change it when I'm updating my website for example
example.com/style.css?v=11009
with this, your client browser will always retrieve your website css if the version is changed (because browser will assume that they are two different file)
Related
We have built a project (Web Application) in React .net core using react in client-side rendering.
We've used react-helmet for dynamically assigning meta tags.
The issue being when the app renders in the browser. The browser gets only the static HTML on initial load which does not include the dynamic meta tags we have set. However on inspecting you get those meta tags under "Elements".
Also, if we use these URL for sharing on any social media, like WhatsApp or Facebook, the URL does not render any metadata as it should.
Tried searching for solutions to our problem, the most obvious answer we came across was to try server-side rendering instead. We get that, but it is not a solution to try out at this juncture when we're ready with app to roll it out.
Others we came across were "react-snap", "react-snapshot", but no luck
with react-snap, it requires to upgrade React's version to 16+, which we did but I guess not all dependencies were upgraded, there was an error saying "
hydrate is not a function
(hydrate concerns the react-dom)
With react-snapshot, we could not find the necessary type definition, which is required in react .net core to function properly
Please guide for the next probable step (except the paid ones like prerender, etc)?
Main goal: Social Applications should render the meta data when we paste/share the URL within them.
Prerender is the only solution.
I used a node dependency called "prerender" -> https://github.com/prerender/prerender
It works enabling a web server wich make http requests. Assigning value to a boolean: window.prerenderReady = true; in your website tells your server when the page is ready to "take the photo" and it returns the Html when so. You need to program an easy script that parses all the site urls and save those html contents to files. Upload them to your server and using .htaccess or similar target the crawlers external-hit-facebook,twitterbot,googlebot, etc.. to show them the prerendered version and 'the real site' to the rest of user-agents.
It worked for me.
The meta tags for Open Graph need to be present in the HTML which is sent back to the client when fetching a URL. Browsers or bots will not wait until the app is rendered on the client side to determine what the metatags are - they will only look at the initially loaded HTML.
If you need the content of your Open Graph metadata to be dynamic (showing different content depending on the URL, device, browser etc.) you need to add something like react-meta-tags into your server code.
There are no type definitions available for any of the react meta tags libraries, but you can add your own. It can be a bit tricky, but check out the official documentation and the templates they have provided to get started.
If you don't need it to be dynamic, you could add the tags into the static parts of the <head>-tag in your index.html.
I had the same issue today. I had two React Web applications that need this. Here is how I solved it:
put your preview image in the public folder
still in public folder, Open index.html, add the line <meta property="og:image" content="preview.png"/>
or <meta property="og:image" content="%PUBLIC_URL%/preview.png"/>.
Go to https://www.linkedin.com/post-inspector/ to check if it works.
I hope this would help!
I see two issues with AngularJS application regarding search engines and SEO:
1) What happens with custom tags? Do search engines ignore the whole content within those tags? i.e. suppose I have
<custom>
<h1>Hey, this title is important</h1>
</custom>
would <h1> be indexed despite being inside custom tags?
2) Is there a way to avoid search engines of indexing {{}} binds literally? i.e.
<h2>{{title}}</h2>
I know I could do something like
<h2 ng-bind="title"></h2>
but what if I want to actually let the crawler "see" the title? Is server-side rendering the only solution?
(2022) Use Server Side Rendering if possible, and generate URLs with Pushstate
Google can and will run JavaScript now so it is very possible to build a site using only JavaScript provided you create a sensible URL structure. However, pagespeed has become a progressively more important ranking factor and typically pages built clientside perform poorly on initial render.
Serverside rendering (SSR) can help by allowing your pages to be pre-generated on the server. Your html containst the div that will be used as the page root, but this is not an empty div, it contains the html that the JavaScript would have generated if it were allowed to run.
The client downloads the HTML and renders it giving a very fast initial load, then it executes the JavaScript replacing the content of the root div with generated content in a process known as hydration.
Many newer frameworks come with SSR built in, notably NextJS.
(2015) Use PushState and Precomposition
The current (2015) way to do this is using the JavaScript pushState method.
PushState changes the URL in the top browser bar without reloading the page. Say you have a page containing tabs. The tabs hide and show content, and the content is inserted dynamically, either using AJAX or by simply setting display:none and display:block to hide and show the correct tab content.
When the tabs are clicked, use pushState to update the URL in the address bar. When the page is rendered, use the value in the address bar to determine which tab to show. Angular routing will do this for you automatically.
Precomposition
There are two ways to hit a PushState Single Page App (SPA)
Via PushState, where the user clicks a PushState link and the content is AJAXed in.
By hitting the URL directly.
The initial hit on the site will involve hitting the URL directly. Subsequent hits will simply AJAX in content as the PushState updates the URL.
Crawlers harvest links from a page then add them to a queue for later processing. This means that for a crawler, every hit on the server is a direct hit, they don't navigate via Pushstate.
Precomposition bundles the initial payload into the first response from the server, possibly as a JSON object. This allows the Search Engine to render the page without executing the AJAX call.
There is some evidence to suggest that Google might not execute AJAX requests. More on this here:
https://web.archive.org/web/20160318211223/http://www.analog-ni.co/precomposing-a-spa-may-become-the-holy-grail-to-seo
Search Engines can read and execute JavaScript
Google has been able to parse JavaScript for some time now, it's why they originally developed Chrome, to act as a full featured headless browser for the Google spider. If a link has a valid href attribute, the new URL can be indexed. There's nothing more to do.
If clicking a link in addition triggers a pushState call, the site can be navigated by the user via PushState.
Search Engine Support for PushState URLs
PushState is currently supported by Google and Bing.
Google
Here's Matt Cutts responding to Paul Irish's question about PushState for SEO:
http://youtu.be/yiAF9VdvRPw
Here is Google announcing full JavaScript support for the spider:
http://googlewebmastercentral.blogspot.de/2014/05/understanding-web-pages-better.html
The upshot is that Google supports PushState and will index PushState URLs.
See also Google webmaster tools' fetch as Googlebot. You will see your JavaScript (including Angular) is executed.
Bing
Here is Bing's announcement of support for pretty PushState URLs dated March 2013:
http://blogs.bing.com/webmaster/2013/03/21/search-engine-optimization-best-practices-for-ajax-urls/
Don't use HashBangs #!
Hashbang URLs were an ugly stopgap requiring the developer to provide a pre-rendered version of the site at a special location. They still work, but you don't need to use them.
Hashbang URLs look like this:
domain.example/#!path/to/resource
This would be paired with a metatag like this:
<meta name="fragment" content="!">
Google will not index them in this form, but will instead pull a static version of the site from the escaped_fragments URL and index that.
Pushstate URLs look like any ordinary URL:
domain.example/path/to/resource
The difference is that Angular handles them for you by intercepting the change to document.location dealing with it in JavaScript.
If you want to use PushState URLs (and you probably do) take out all the old hash style URLs and metatags and simply enable HTML5 mode in your config block.
Testing your site
Google Webmaster tools now contains a tool which will allow you to fetch a URL as Google, and render JavaScript as Google renders it.
https://www.google.com/webmasters/tools/googlebot-fetch
Generating PushState URLs in Angular
To generate real URLs in Angular, rather than # prefixed ones, set HTML5 mode on your $locationProvider object.
$locationProvider.html5Mode(true);
Server Side
Since you are using real URLs, you will need to ensure the same template (plus some precomposed content) gets shipped by your server for all valid URLs. How you do this will vary depending on your server architecture.
Sitemap
Your app may use unusual forms of navigation, for example hover or scroll. To ensure Google is able to drive your app, I would probably suggest creating a sitemap, a simple list of all the URLs your app responds to. You can place this at the default location (/sitemap or /sitemap.xml), or tell Google about it using webmaster tools.
It's a good idea to have a sitemap anyway.
Browser support
Pushstate works in IE10. In older browsers, Angular will automatically fall back to hash style URLs
A demo page
The following content is rendered using a pushstate URL with precomposition:
http://html5.gingerhost.com/london
As can be verified, at this link, the content is indexed and is appearing in Google.
Serving 404 and 301 Header status codes
Because the search engine will always hit your server for every request, you can serve header status codes from your server and expect Google to see them.
Update May 2014
Google crawlers now executes javascript - you can use the Google Webmaster Tools to better understand how your sites are rendered by Google.
Original answer
If you want to optimize your app for search engines there is unfortunately no way around serving a pre-rendered version to the crawler. You can read more about Google's recommendations for ajax and javascript-heavy sites here.
If this is an option I'd recommend reading this article about how to do SEO for Angular with server-side rendering.
I’m not sure what the crawler does when it encounters custom tags.
Let's get definitive about AngularJS and SEO
Google, Yahoo, Bing, and other search engines crawl the web in traditional ways using traditional crawlers. They run robots that crawl the HTML on web pages, collecting information along the way. They keep interesting words and look for other links to other pages (these links, the amount of them and the number of them come into play with SEO).
So why don't search engines deal with javascript sites?
The answer has to do with the fact that the search engine robots work through headless browsers and they most often do not have a javascript rendering engine to render the javascript of a page. This works for most pages as most static pages don't care about JavaScript rendering their page, as their content is already available.
What can be done about it?
Luckily, crawlers of the larger sites have started to implement a mechanism that allows us to make our JavaScript sites crawlable, but it requires us to implement a change to our site.
If we change our hashPrefix to be #! instead of simply #, then modern search engines will change the request to use _escaped_fragment_ instead of #!. (With HTML5 mode, i.e. where we have links without the hash prefix, we can implement this same feature by looking at the User Agent header in our backend).
That is to say, instead of a request from a normal browser that looks like:
http://www.ng-newsletter.com/#!/signup/page
A search engine will search the page with:
http://www.ng-newsletter.com/?_escaped_fragment_=/signup/page
We can set the hash prefix of our Angular apps using a built-in method from ngRoute:
angular.module('myApp', [])
.config(['$location', function($location) {
$location.hashPrefix('!');
}]);
And, if we're using html5Mode, we will need to implement this using the meta tag:
<meta name="fragment" content="!">
Reminder, we can set the html5Mode() with the $location service:
angular.module('myApp', [])
.config(['$location',
function($location) {
$location.html5Mode(true);
}]);
Handling the search engine
We have a lot of opportunities to determine how we'll deal with actually delivering content to search engines as static HTML. We can host a backend ourselves, we can use a service to host a back-end for us, we can use a proxy to deliver the content, etc. Let's look at a few options:
Self-hosted
We can write a service to handle dealing with crawling our own site using a headless browser, like phantomjs or zombiejs, taking a snapshot of the page with rendered data and storing it as HTML. Whenever we see the query string ?_escaped_fragment_ in a search request, we can deliver the static HTML snapshot we took of the page instead of the pre-rendered page through only JS. This requires us to have a backend that delivers our pages with conditional logic in the middle. We can use something like prerender.io's backend as a starting point to run this ourselves. Of course, we still need to handle the proxying and the snippet handling, but it's a good start.
With a paid service
The easiest and the fastest way to get content into search engine is to use a service Brombone, seo.js, seo4ajax, and prerender.io are good examples of these that will host the above content rendering for you. This is a good option for the times when we don't want to deal with running a server/proxy. Also, it's usually super quick.
For more information about Angular and SEO, we wrote an extensive tutorial on it at http://www.ng-newsletter.com/posts/serious-angular-seo.html and we detailed it even more in our book ng-book: The Complete Book on AngularJS. Check it out at ng-book.com.
You should really check out the tutorial on building an SEO-friendly AngularJS site on the year of moo blog. He walks you through all the steps outlined on Angular's documentation. http://www.yearofmoo.com/2012/11/angularjs-and-seo.html
Using this technique, the search engine sees the expanded HTML instead of the custom tags.
This has drastically changed.
http://searchengineland.com/bing-offers-recommendations-for-seo-friendly-ajax-suggests-html5-pushstate-152946
If you use:
$locationProvider.html5Mode(true);
you are set.
No more rendering pages.
Things have changed quite a bit since this question was asked. There are now options to let Google index your AngularJS site. The easiest option I found was to use http://prerender.io free service that will generate the crwalable pages for you and serve that to the search engines. It is supported on almost all server side web platforms. I have recently started using them and the support is excellent too.
I do not have any affiliation with them, this is coming from a happy user.
Angular's own website serves simplified content to search engines: http://docs.angularjs.org/?_escaped_fragment_=/tutorial/step_09
Say your Angular app is consuming a Node.js/Express-driven JSON api, like /api/path/to/resource. Perhaps you could redirect any requests with ?_escaped_fragment_ to /api/path/to/resource.html, and use content negotiation to render an HTML template of the content, rather than return the JSON data.
The only thing is, your Angular routes would need to match 1:1 with your REST API.
EDIT: I'm realizing that this has the potential to really muddy up your REST api and I don't recommend doing it outside of very simple use-cases where it might be a natural fit.
Instead, you can use an entirely different set of routes and controllers for your robot-friendly content. But then you're duplicating all of your AngularJS routes and controllers in Node/Express.
I've settled on generating snapshots with a headless browser, even though I feel that's a little less-than-ideal.
A good practice can be found here:
http://scotch.io/tutorials/javascript/angularjs-seo-with-prerender-io?_escaped_fragment_=tag
As of now Google has changed their AJAX crawling proposal.
Times have changed. Today, as long as you're not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers.
tl;dr: [Google] are no longer recommending the AJAX crawling proposal [Google] made back in 2009.
Google's Crawlable Ajax Spec, as referenced in the other answers here, is basically the answer.
If you're interested in how other search engines and social bots deal with the same issues I wrote up the state of art here: http://blog.ajaxsnapshots.com/2013/11/googles-crawlable-ajax-specification.html
I work for a https://ajaxsnapshots.com, a company that implements the Crawlable Ajax Spec as a service - the information in that report is based on observations from our logs.
I have found an elegant solution that would cover most of your bases. I wrote about it initially here and answered another similar Stack Overflow question here which references it.
FYI this solution also includes hard coded fallback tags in case JavaScript isn't picked up by the crawler. I haven't explicitly outlined it, but it is worth mentioning that you should be activating HTML5 mode for proper URL support.
Also note: these aren't the complete files, just the important parts of those that are relevant. I can't help with writing the boilerplate for directives, services, etc.
app.example
This is where you provide the custom metadata for each of your routes (title, description, etc.)
$routeProvider
.when('/', {
templateUrl: 'views/homepage.html',
controller: 'HomepageCtrl',
metadata: {
title: 'The Base Page Title',
description: 'The Base Page Description' }
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl',
metadata: {
title: 'The About Page Title',
description: 'The About Page Description' }
})
metadata-service.js (service)
Sets the custom metadata options or use defaults as fallbacks.
var self = this;
// Set custom options or use provided fallback (default) options
self.loadMetadata = function(metadata) {
self.title = document.title = metadata.title || 'Fallback Title';
self.description = metadata.description || 'Fallback Description';
self.url = metadata.url || $location.absUrl();
self.image = metadata.image || 'fallbackimage.jpg';
self.ogpType = metadata.ogpType || 'website';
self.twitterCard = metadata.twitterCard || 'summary_large_image';
self.twitterSite = metadata.twitterSite || '#fallback_handle';
};
// Route change handler, sets the route's defined metadata
$rootScope.$on('$routeChangeSuccess', function (event, newRoute) {
self.loadMetadata(newRoute.metadata);
});
metaproperty.js (directive)
Packages the metadata service results for the view.
return {
restrict: 'A',
scope: {
metaproperty: '#'
},
link: function postLink(scope, element, attrs) {
scope.default = element.attr('content');
scope.metadata = metadataService;
// Watch for metadata changes and set content
scope.$watch('metadata', function (newVal, oldVal) {
setContent(newVal);
}, true);
// Set the content attribute with new metadataService value or back to the default
function setContent(metadata) {
var content = metadata[scope.metaproperty] || scope.default;
element.attr('content', content);
}
setContent(scope.metadata);
}
};
index.html
Complete with the hardcoded fallback tags mentioned earlier, for crawlers that can't pick up any JavaScript.
<head>
<title>Fallback Title</title>
<meta name="description" metaproperty="description" content="Fallback Description">
<!-- Open Graph Protocol Tags -->
<meta property="og:url" content="fallbackurl.example" metaproperty="url">
<meta property="og:title" content="Fallback Title" metaproperty="title">
<meta property="og:description" content="Fallback Description" metaproperty="description">
<meta property="og:type" content="website" metaproperty="ogpType">
<meta property="og:image" content="fallbackimage.jpg" metaproperty="image">
<!-- Twitter Card Tags -->
<meta name="twitter:card" content="summary_large_image" metaproperty="twitterCard">
<meta name="twitter:title" content="Fallback Title" metaproperty="title">
<meta name="twitter:description" content="Fallback Description" metaproperty="description">
<meta name="twitter:site" content="#fallback_handle" metaproperty="twitterSite">
<meta name="twitter:image:src" content="fallbackimage.jpg" metaproperty="image">
</head>
This should help dramatically with most search engine use cases. If you want fully dynamic rendering for social network crawlers (which are iffy on JavaScript support), you'll still have to use one of the pre-rendering services mentioned in some of the other answers.
With Angular Universal, you can generate landing pages for the app that look like the complete app and then load your Angular app behind it.
Angular Universal generates pure HTML means no-javascript pages in server-side and serve them to users without delaying. So you can deal with any crawler, bot and user (who already have low cpu and network speed).Then you can redirect them by links/buttons to your actual angular app that already loaded behind it. This solution is recommended by official site. -More info about SEO and Angular Universal-
Use something like PreRender, it makes static pages of your site so search engines can index it.
Here you can find out for what platforms it is available: https://prerender.io/documentation/install-middleware#asp-net
Crawlers (or bots) are designed to crawl HTML content of web pages but due to AJAX operations for asynchronous data fetching, this became a problem as it takes sometime to render page and show dynamic content on it. Similarly, AngularJS also use asynchronous model, which creates problem for Google crawlers.
Some developers create basic html pages with real data and serve these pages from server side at the time of crawling. We can render same pages with PhantomJS on serve side which has _escaped_fragment_ (Because Google looks for #! in our site urls and then takes everything after the #! and adds it in _escaped_fragment_ query parameter). For more detail please read this blog .
The crawlers do not need a rich featured pretty styled gui, they only want to see the content, so you do not need to give them a snapshot of a page that has been built for humans.
My solution: to give the crawler what the crawler wants:
You must think of what do the crawler want, and give him only that.
TIP don't mess with the back. Just add a little server-sided frontview using the same API
I have done a full Single Page Application (SPA) application using Angularjs.
So far so good.
As anyone knows, all javascript files are loaded in the first time access. Or, some file are loaded in lazy mode style when needed.
So far so good...
The situation is: the server updates all files (html partials, javascripts, css's) and the client remain with a lot of files out-dated.
This would be simply solved refreshing the browser, hit F5 key, control+f5, or refresh button in the browser. But this concept does not exists when working with SPA.
I'm not sure how to solve this problem.
I could detect somehow (doing a ping maybe) and just to re-load that specific file. With document.write strategy. But now rises another problem, I have a single javascript file with all javascript minified.
I could try to force a full reload in the browser or force to re-login (and reload because login are SPA part).
But reloading is an ugly solution, imagine the client lose all data in the form because he was unlucky the server have just updated. And worse, I must now create some "auto-save" feature just because of this.
I'm not sure how to handle this, if possible, doing in "angular way".
I wonder how google gmail handles this because I stay logged for many many hours without logging of.
As others have already suggested, keep the logged user on the old version of your webapp.
Not only what you ask is difficult to do in Angular, but it can also lead to a bad user experience and surprising behaviour, since there may not be a mapping between what the user is doing with the old version and what the new version provides. Views may be removed, renamed, split or merged. The behaviour of the same view may have changed, and doing so without notice for the user may cause mistakes.
You made an example with Gmail, but may have noticed that changes to the UI always happen after you logout, never while you're using it.
First of all, if your app is an intranet website used during office time, just update it while nobody is using it. This is a much simpler solution.
Otherwise, if you need to provide 24/24 availability, my suggestion is:
When you deploy the new version of your SPA, keep the old version in parallel with the new version, keep the current users on the old version, and log new users to the new version. This can be made in a number of ways depending on your setup, but it's not difficult to do.
Keep the old version around until you're confident that nobody is still using it or you're pretty sure that the new version is ok and you don't need to rollback to the old version.
The backend services should be backward-compatible with the old version of the frontend. If that's not possible you should keep multiple version of the backend services too.
As the rest of the guys said a solution can be to versioning your files. So every time that your browser check those files out the browser notice that the files are different to the ones that are in the server so the browser cache the new files.
I suggest to use some build tool like gulp, grunt or webpack, the last one is becoming more popular.
By the moment I use gulp for my projects. I´m moving to webpack though.
if you are interested in gulp you can have a look to gulp-rev and gulp-rev-replace plugins.
What do they do?
let´s say that we have the next file in your project app.js what you get after apply gulp-rev to your project is something like app-4j8888dp.js then your html file where the app.js is injected is still pointing to app.js so you need to replace it. To do that you can use gulp-rev-replace plugin.
eg. gulp task where
var gulp = require('gulp');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var useref = require('gulp-useref');
var filter = require('gulp-filter');
var uglify = require('gulp-uglify');
var csso = require('gulp-csso');
gulp.task("index", function() {
var jsFilter = filter("**/*.js", { restore: true });
var cssFilter = filter("**/*.css", { restore: true });
var indexHtmlFilter = filter(['**/*', '!**/index.html'], { restore: true });
return gulp.src("src/index.html")
.pipe(useref()) // Concatenate with gulp-useref
.pipe(jsFilter)
.pipe(uglify()) // Minify any javascript sources
.pipe(jsFilter.restore)
.pipe(cssFilter)
.pipe(csso()) // Minify any CSS sources
.pipe(cssFilter.restore)
.pipe(indexHtmlFilter)
.pipe(rev()) // Rename the concatenated files (but not index.html)
.pipe(indexHtmlFilter.restore)
.pipe(revReplace()) // Substitute in new filenames
.pipe(gulp.dest('public'));
});
if you want to know further details see the links bellow.
https://github.com/sindresorhus/gulp-rev
https://github.com/jamesknelson/gulp-rev-replace
A single page application is that, a single stack that controls the client logic of your application. Thus, any navigation done through the application should be handled by your client, and not by the server. The goal is to have a one single "fat" HTTP request that loads everything you need, and then perform small HTTP requests.
That's why you can only have one ng-app in your apps. You are not suppose to have multiple and just load the modules you need (although the AngularJS team wants to move that way). In all cases, you should serve the same minified file and handle everything from your application.
It seems to me that you are more worried about the state of your application. As Tom Dale (EmberJS) described in the last Cage Match, we should aim to have applications that can reflect the same data between server and client at any point of time. You have many ways to do so, either by cookies, sessions or local storage.
Usually a SPA communicates with a REST based server, and hence perform idempotent operations to your data.
tl;dr You are not supposed to refresh anything from the server (styles or scripts, for instance), just the data that your application is handling. An initial single load is what SPA is all about.
separate your data and logic and reload the data using ajax whenever you want, for that i will suggest you use REST API to get the data from server.
SPA helps you to reduce the HTTP request again and again but its also require some http request to update a new data to view.
Well, you would have to unload the old existing code (i.e. the old AngularJS app, modules, controllers, services and so on). Theoretically, you could create a custom (randomized) app name (with all modules have this prefix for each unique build!) and then rebuild your app in the browser. But seriously.. that's a) very complex and b) will probably fail due memory leaks.
So, my answer is: Don't.
Caching issues
I would personally recommend to name/prefix all resources depended by a build with a unique id; either the build id, a scm hash, the timestamp or whatever like that. So, the url to the javascript is not domain.tld/resources/scripts.js but domain.tld/resources-1234567890/scripts.js which ensures that this path's content will never conflict with a (newer) version. You can choose your path/url like you want (depending on the underlaying structure: it is all virtually, can you remap urls, etcpp). It would be even not required that each version will exist forever (i.e. map all resources-(\d+)/ to resources/; however, this would be not nice for the concept of URLs.
Application state
Well, the question is how often will the application change that it would be important that such reloads are required. How long is the SPA open in a browser? Is it really impossible to support two versions at the same time? The client (the app in the browser) could even send its own version within the HTTP requests.
In the beginning of a new application, there are a lot of changes that would require a reload. But soon after your application has a solid state, the changes will be smaller and would not require a reload. The user itself will make more refreshs.. more than we ever expected :/
As with what everyone else is saying...
Don't, and while socket.io could work it's asking for trouble if you are VERY careful.
You have two options, upon server update invalidate any previous session (I would also give users a half hours notice or 5 minutes depending on application before maintenance would be done.
The second option is versioning. If they are on version 10, then they communicate with backend 10. If you release version 11 and they are still on 10 then they can still communicate with backend 10.
Remember Google wave? It failed for a reason. Too many people writing one source as the same time causes more problems then it solves.
use $state service. create state during loading page using ctor. after specified time re create state and load page.
function(state) {
state.stateCtor(action);
$state.transitionTo(action + '.detail', {}, {
notify: true
});
}
Versioning your files, so on every update increment version number and the browser will update it automaticallly.
My solution consists of several points.
While this is not important, but I send one JavaScript file to the client side, I use grunt to prepare my release. The same grunt file adds to the JavaScript tag a query with version number. Regardless of whether you have one file or lots of files, you need to add a version number to them. You may need to do this for resources as well. (check at the end for an example)
I send back in all my responses from the server (I use node) the version number of the app.
In Angular, when I receive any response, I check the version number against the version number loaded, if it has changed (this means the server code has been updated) then I alert the user that the app is going to reload. I use location.reload(true);
Once reloaded the browser will fetch all new files again because the version number in the script tag is different now, and so it will not get it from cache.
Hope this helps.
<script src="/scripts/spa.min.js?v=0.1.1-1011"></script>
Right now I've embedded my Backbone application in an html page ( a classic index.html ).
When I edit something and I go back to see the page, to see current changes I need to hit F5.
What would be the best practice in a case where the user is using an old version due to the page being cached by the browser?
I was thinking about using a jsp or a php page but I honestly was not able to find so much on Google.
Can you help me?
There is a difference between reloading a page automagically when a file changes (watching a folder) and making sure that new users have the right version of your javascript application.
In the first case, I am guessing this is for development purposes and you will most likely want to use something like Live.js to achieve this.
In the second case, each time a returning user enters the application ( loads the index.html ), the server should issue a 304 - Not changed for the javascript file if the application has not changed and the file is cached in the browser or a 200 - OK with the file if it has changed and serve the new version. This has nothing to do with Backbone and the caching strategies are implemented on the server side.
If however you would like the application to periodically check for new versions while the user is using it and reload if there is a change ( I personnally think this is a bad idea, but it is possible ), here is a solution that would work.
You will need to have an entry point on the server side that only returns the application's version. This could be a semantic versioning, a hash, a name, etc... As long as it is different for each version.
main.js
var currentVersion;
// Assuming that the '/api/app-version' access point returns a string or number.
$.ajax({ url : '/api/app-version'})
.done(function(data) { currentVersion = data });
// Poll the server every minute to watch for a new version.
// If the version is different, reload the application.
setInterval( function() {
$.ajax({ url : '/api/app-version'})
.done(function(data) { if ( data !== currentVersion ) window.location.reload() });
}, 60000);
I want the users to not be able to view the partials directly when accessing their url in the browser. For example if my partial view is located in:
/partials/myPartial.html
I can still directly access it and see the odd markup. Is there a way to prevent it and make the partial views only available through angular? Thanks.
I put a <script> at the top of every view partial that checks if the variable containing the main app Module exists, if not redirect to index.html
If in app.js the appModule is defined in this way:
var appModule = angular.module('myApp',["ngRoute","ngResource"]);`
At the top of the partial insert:
<script type="text/javascript">
if(typeof appModule === 'undefined'){
document.location.href="index.html";
}
</script>
Well, not really.
At the end of a day a browser needs to be able to access a partial to download it. If a browser can do this your users will be able to do so as well. You might eventually make it a bit harder for them to hit partials directly (for example to by configuring your server so it only responds to request with a certain header and configure $http to send this header on each XHR request).
The other possibility is to pre-load partials up-front as described here and not expose them via HTTP at all (actually this is a good practice for production deployments anyway).
Otherwise it is hard to propose a meaningful solution without knowing what is the exact problem (functionally speaking) that you are trying to solve.