Caching best practice for AngularJS dynamic sites? - angularjs

I'm working on an AngularJS project that will have considerable traffic.
While in development I'm stumbled upon the issue with partials cached and not updated on different actions. Sure, I can get rid of this using .run with $templateCache.removeAll(); for example,
but want to make sure this is actually a good idea.
Some partials are updated dynamically (user input, or automatically in intervals), while some are static or updated very infrequently.
What would be the best approach to caching in this case?
With non-angular sites I prefer to keep responsibilities cleanly split, for ex.:
1. Cache-headers are set on app level
2. nginx - just to serve sites per se
3. Varnish doing FPC + CDN for static assets or CDN doing full page caching (depending on a client/project, etc.) etc. etc. etc.
Key point: every part has its own distinct responsibility.
With this project I can use Varnish and CDN for static assets,
multi-server setup. Possibility to re-use Varnish for loadbalancing as well, i.e. I may have Varnish above several web-nodes. I have some flexibility in terms of infrastructure.
Please share your thoughts on the optimal setup?
In particular: is it still worth caching partials?
If yes, what would be the best place to set CC headers?
What would be the best way to flush their cache then, esp. if I need to flush only some sub-selection?
Thank you!!
D.

Posting the answer myself, as cannot select rob's reply as an answer. My solution is based on his suggestion which helped me a lot to move forward:
During the production build:
I'm using "gulp-rev-easy" gulp module for revving the previously
concatenated and uglified css & js files.
However, no existing revving modules could provide me with functionality I needed: replacing specific strings in an index.html template file with my own paths to CDN both for CSS and JS scripts. So I had to add my own function/override reveasy a little.
As per rob's I've started using templatecache and its works great in conjunction with revving.
Images are just going through a S3 -> CDN pair.
Some quick notes:
Using revving for "breaking" cache turned out to be the fastest and
easiest way to deal with caching, as CloudFront is taking approx. 20
minutes to invalidate a path, other CDNs - less, but you still have
to issue the request etc.
For dynamically updated images having same names, like avatar images
that a user can update via his profile areas, I suggest adding a
timestamp or some random string to the file name. While adding a
random URL path sounds easier, you won't be able to cache this.
Hope this helps,
Dennis

Related

Custom Angular setup for MIT library/project

Hi I need your help trying to figure it out something.
First a little background, I was used to code in Django, it was fast to code and good, but times change and Node is taking over most of new apps, then I move to Express a couple of years ago, however I still miss a lot of the Django functionality, then I start coding a little library to help me with common tasks, then start growing until the current point where you have a core library and plug-able “apps” to do recurrent tasks, like notifications, auth and more, or at least that’s the plan.
Right now an app works something like this:
./controllers (All renders)
./endpoints (Restfull API endpoints)
./models (Static and DB models)
./public (Public files)
./style (Scss styles with bootstrap injected)
./views (Default templates, distributed with the app as example, loaded by default)
…/…/views (Custom views to rewrite the default ones from the app)
On start, JSloth compile everything for you and run the server (hot reload included):
Now, that works fine using an static multipage environment, but I will love to use Angular for that, changes will be needed but I want you guys to lead me in the right direction.
So far I have 2 ideas:
1- Split Routes/Html apps and Restful/Endpoints, then basically use an standard set up on that kind of apps with webpack and AngularSSR.
The big downside is, you can’t give an out the box frontend implementation for apps.
2- Figure it out a way to provide an Angular app for each JSloth app, in this way if you install/copy the auth app you will be provided of user lists, interfaces to update your profile, etc.
I’m thinking that may be a problem talking about performance because in this way you will have a lot of Angular apps, am I wrong?
I need a easy way to allow the final user to share footers and headers at least, maybe even styles or scss variables for colors, in that way it will not look like a huge change.
Do you have any other option? Any better idea?
Thank you so much for the help, this is the repository: https://github.com/chrissmejia/JSloth
Edit 1: Forgot to add the models folder

Make Angular Web App available offline

I am struggling with some details about finding a solution to make an Angular / C# app available offline.
My idea would be to use upup.js the get the business logic for the SPA in Angular available offline. upup.js uses service workers to do so. I would store the data required for the offline app using angular-localForage which uses IndexedDB and falls back to WebSQL and localStorage in case.
The problem is, that I have to make files and images available offline too without requiring the user to visit the page they are being used on and I am worried about the maximum quotas. I could store them using either upup.js and adding those files as assets, or store them with angular-localForage as blobs. IndexedDB is supposed to be unlimited by now if I am informed correctly? I couldn't find any maximum quota for a service worker though, as the upup.js solution would use. The AppCache is deprecated, so I wouldn't use that... Or maybe I understood something completely wrong, or there is even another, better solution? Anyhow, the question is:
TL;DR: What is the better way to store files for an AngularJS offline application: angular-localForage (IndexedDB etc.) or a Service Worker (upup.js) and what are the maximum quotas for each solution? Or is there an even better solution?
In my opinion Service Worker (SW) is better than tradition local storage. Plus SW can also use indexedDB.
For the implementation, it is very depending. How your app structure, what technologies of front-end being with with your angular app, what is your main goals of using SW...?
1. Traditional JS loading, you are likely merge all the JS file in one... like app.js contains everything.
And you also don't care about Push Notification neither any other cool features that SW offering.
=> For this case it seem like upup.js suite you the best.
NOTE : beware that upup.js attempt to registering SW on it own, so it likely blocking or complexified your work on expanding feature of SW.
2. Advanced AMD user, where almost all of your JS chopped to small pieces... like fooCtrl.js, barCtrl.js, etc...
For sure you don't want to configure like 100+ files of JS, and further more you will got a lot of html template to load.
=> In this case I will suggest you to use sw-toolbox . A very powerful and light weight tool made by Google. At initial if you are not familiar with SW concept yet, you will have a bit of trouble setting it up for your site (but it won't be longer than a day of you are an advanced JS developer)
After all has configured, all become so very simple. For example this is how I cache all of static content in my site.
self.toolbox.router.get(/(.js|.css|.png|.jpg|.json|.html)/, self.toolbox.fastest);
3. You don't care about what kind of technology at front-end side. You just interest with SW.
=> Simply go for sw-toolbox it's a real time-saver for basic configuration. And if you want to expand the usage of SW, you can just expand it by your own will.

Can I NOT use requirejs in marionette/backbone?

People mention requirejs together with marionette, backbonejs and the like.
requirejs seems an asset loader -- executing your rules on when to load what.
I know the first 'page' of my single-page-app already needs most of the files. If I don't mind loading all files in one go, can I simply ignore requirejs?
Technically yes. Only dependencies for marionette-backbone are
jQuery v1.8+
Underscore v1.4.4 - 1.6.0
Backbone v1.0.0 - 1.1.2 are preferred
Backbone.Wreqr (Comes automatically with the bundled build)
Backbone.BabySitter(Comes automatically with the bundled build)
Further require.js can manage use code structure in a manner which give your code much resource efficient code at the end. From my point of view for simple application which you need simple set of views,models and collection with manageable amount of code it ok to proceed without require.js.
But if your application have complex logic and higher number of resources it's good to go require.js. Because it not good to send 15+ like individual resource requests server at very beginning of your application load. Require can make any number of your resource in to one server resource. That's the advantage.
What I prefer is one request of all css, one for all js, one for sprite image for graphic if things are big to handle which allow to create fast performing application.
Take you decision looking at the amount of resources of the project. It's not essential have require.js form the beginning of your application development.

Internationalization with angularjs

I'm thinking of moving my site to angularjs, and I want to start very small, by moving all my static server-side plain-text templating from django to angular (otherwise there will be syntax trouble with the '{{}}').
It seems that the best way to do that will be one of two options:
To have an ajax call that returns a JSON with all the texts of my site. The texts will be stored in a variable which is binded to my HTML elements so angular will update everything.
To store a static js file with the dictionary and include it in my HTML and bind the dictionary with angularjs.
Both options will allow me to switch between languages without reloading the page.
Which one is better? In general, is this a good approach or is there a more correct way?
I tried out a few different options, including Angular Translate, but I liked Angular-gettext the best so far.
One thing that helped tremendously is that there's a working demo for it where they i18n TodoMVC, called angular-gettext-example.
The workflow is simple:
Add the "translate" directive to your templates
Run grunt to extract .pot template(s)
Hand off the .pot to your translation vendor or DIY with POEdit or similar software
Drop the .po translation files back into your project
Run grunt to compile the .po files
Set the default language in your scope
Watch the magic!
I'm sure the other solutions posted here are good as well but I haven't seen an end-to-end example so nicely organized as angular-gettext-example.
Cheers,
JD
First of all, there is a way to change angular's delimiters to other symbols as answered here: Angular JS custom delimiter
The 2. option is easier. You include it once and you have all translations on page load. No async calls, no promises, nice and easy.
And yet i'd go with the first one. Services like $translate would really make your life easier following option 1. Plus it has many options for loading and storing loaded data in LocalStorage and cookies, so there's plenty of space for extension and customization. You can then translate your content with $translate service, directive or filter.
And don't forget that 2 option disables any options of cached requests. On each request to your start page the server has to read static file and include it in the html. With first option the user's browser can cache .json for as long as you like.
AngularJS supports il8n/L10n for currency, date and numbers filters only. According to this book:
(sorry for the low quality! cell phone camera)
I would say follow the first approach and load the translation dynamically. It would involve a lot of work but there's no other way around
Have a look at angular-translate :)
It solves both scenarios!

Question on serving Images on App Engine ( 2 Alternatives )

planning to launch a comic site which serves comic strips (images).
I have little prior experience to serving/caching images.
so these are my 2 methods i'm considering:
1. Using LinkProperty
class Comic(db.Model)
image_link = db.LinkProperty()
timestamp = db.DateTimeProperty(auto_now=True)
Advantages:
The images are get-ed from the disk space itself ( and disk space is cheap i take it?)
I can easily set up app.yaml with an expiration date to cache the content in user's browser
I can set up memcache to retrieve the entities faster (for high traffic)
2. Using BlobProperty
I used this tutorial , it worked pretty neat. http://code.google.com/appengine/articles/images.html
Side question: Can I say that using BlobProperty sort of "protects" my images from outside linkage? That means people can't just link directly to the comic strips
I have a few worries for method 2.
I can obviously memcache these entities for faster reads.
But then:
Is memcaching images a good thing? My images are large (100-200kb per image). I think memcache allows only up to 4 GB of cached data? Or is it 1 Mb per memcached entity, with unlimited entities...
What if appengine's memcache fails? -> Solution: I'd have to go back to the datastore.
How do I cache these images in the user's browser? If I was doing method no. 1, I could just easily add to my app.yaml the expiration date for the content, and pictures get cached user side.
would like to hear your thoughts.
Should I use method 1 or 2? method 1 sounds dead simple and straightforward, should I be wary of it?
[EDITED]
How do solve this dilemma?
Dilemma: The last thing I want to do is to prevent people from getting the direct link to the image and putting it up on bit.ly because the user will automatically get directed to only the image on my server
( and not the advertising/content around it if the user had accessed it from the main page itself )
You're going to be using a lot of bandwidth to transfer all these images from the server to the clients (browsers). Remember appengine has a maximum number of files you can upload, I think it is 1000 but it may have increased recently. And if you want to control access to the files I do not think you can use option #1.
Option #2 is good, but your bandwidth and storage costs are going to be high if you have a lot of content. To solve this problem people usually turn to Content Delivery Networks (CDNs). Amazon S3 and edgecast.com are two such CDNs that support token based access urls. Meaning, you can generate a token in your appengine app that that is good for either the IP address, time, geography and some other criteria and then give your cdn url with this token to the requestor. The CDN serves your images and does the access checks based on the token. This will help you control access, but remember if there is a will, there is a way and you can't 100% secure anything - but you probably get reasonably close.
So instead of storing the content in appengine, you would store it on the cdn, and use appengine to create urls with tokens pointing to the content on the cdn.
Here are some links about the signed urls. I've used both of these :
http://jets3t.s3.amazonaws.com/toolkit/code-samples.html#signed-urls
http://www.edgecast.com/edgecast_difference.htm - look at 'Content Security'
In terms of solving your dilemma, I think that there are a couple of alternatives:
you could cause the images to be
rendered in a Flash object that would
download the images from your server
in some kind of encrypted format that
it would know how to decode. This would
involve quite a bit of up-front work.
you could have a valid-one-time link
for the image. Each time that you
generated the surrounding web page,
the link to the image would be
generated randomly, and the
image-serving code would invalidate
that link after allowing it one time. If you
have a high-traffic web-site, this would be a very
resource-intensive scheme.
Really, though, you want to consider just how much work it is worth to force people to see ads, especially when a goodly number of them will be coming to your site via Firefox, and there's almost nothing that you can do to circumvent AdBlock.
In terms of choosing between your two methods, there are a couple of things to think about. With option one, where are are storing the images as static files, you will only be able to add new images by doing an appcfg.py update. Since AppEngine application do not allow you to write to the filesystem, you will need to add new images to your development code and do a code deployment. This might be difficult from a site management perspective. Also, serving the images form memcache would likely not offer you an improvement performance over having them served as static files.
Your second option, putting the images in the datastore does protect your images from linking only to the extent that you have some power to control through logic if they are served or not. The problem that you will encounter is that making that decision is difficult. Remember that HTTP is stateless, so finding a way to distinguish a request from a link that is external to your application and one that is internal to your application is going to require trickery.
My personal feeling is that jumping through hoops to make sure that people can't see your comics with seeing ads is solving the prolbem the wrong way. If the content that you are publishing is worth protecting, people will flock to your website to enjoy it anyway. Through high volumes of traffic, you will more than make up for anyone who directly links to your image, thus circumventing a few ad serves. Don't try to outsmart your consumers. Deliver outstanding content, and you will make plenty of money.
Your method #1 isn't practical: You'd need to upload a new version of your app for each new comic strip.
Your method #2 should work fine. It doesn't automatically "protect" your images from being hotlinked - they're still served up on a URL like any other image - but you can write whatever code you want in the image serving handler to try and prevent abuse.
A third option, and a variant of #2, is to use the new Blob API. Instead of storing the image itself in the datastore, you can store the blob key, and your image handler just instructs the blobstore infrastructure what image to serve.

Resources