While profiling a large AngularJS app, I started tracking $templateCache.
https://docs.angularjs.org/api/ng/service/$templateCache
What is this object? Does it store the partials as they are stored on disk or the final rendered HTML from the route?
I've added some code like this to my controller to make sure it's not persisting anything:
$scope.$on('$destroy', function cleanup() {
$templateCache.removeAll();
});
EDIT: the reason I am looking into this is that a single (not every) controller is loading a large partial with several loops inside of it, depending on User input it could be about 100 input fields with large <select> statements and many custom directives. The client is requesting it be this way and I want to make sure that all of this is getting disposed during routing.
To clarify the question: $templateCache is only storing the partials as they appear on disk, no routing or rendering information is being stored?
By default, while loading templates, Angular looks inside $templateCache and fetches templates over XHR when it cannot find them locally in its $templateCache.
When the XHR request is slow or our template is large enough, it can seriously negatively impact end users’ experience .
instead of fetching templates XHR, we can fake the template cache loading by wrapping it into a JavaScript file and shipp the JavaScript file along with the rest of the application.
Sample :
angular.module('myApp')
.run(['$templateCache', function($templateCache) {
$templateCache.put('xyz.html', ...);
}]);
So, unless you are very much sure to remove the cached templates form $templateCache, don't do that.
If I remember correctly, it's a object where partials are saved. When making a request to any html file (through routing, directives or include) , angular first looks into $templateCache, and if required file doesn't exist, it will make a request for it and will put it in the cache. But if it does exists, it is taken from there. So if you are using your directive 100 times per page, it will only make one request for your directive file, or if you navigate back and forth your app, it won't request files that were already loaded.
Does it make sense to add $templateCache.removeAll(); on each controller? Only if it's really small application, otherwise I suggest not messing around with it..
Related
Quick Summary:
I need to allow two script files to handle different operations for the same angular app. One needs to initialize the app, the other needs to assign a templateCache object to a piece of JSON in localStorage.
Context:
I have several python files which compile/generate html and I have constructed an angular app with this emitted html for my site (which uses CGIs).
The basic construct of the site comes pieces of HTML, which fit together like so:
|------------Header---------------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|-Navigation-|------Content-------|
|------------Footer---------------|
My Header creates the <head> tag, instantiates my ng-app and uses $templateCache to set up a template that I call from my Navigation code. I had to go with templateCache instead of ngView and ngRoute due to some limitations with how the CGIs emit the html, and the order in which this happens.
My "Navigation" python/html sets up my app with JS like so:
<script>
var responsiveCatalog = angular.module('responsiveCatalog', ['ngStorage']);
....controllers...
....config, etc....
</script>
This Navigation also includes my default templateCache object:
<div ng-include=" 'responsiveItems.html' "></div>
This is all working to show my first templateCache object in the Content section. However, I need to grab many pieces of information from the python generator for the "Content" section (a totally separate file from the "Navigation"), store this data as JSON in localstorage (hence the inclusion of the ngStorage module), and call that as my second templateCache option.
I am not actually sure that I can use two separate instances of Javascript to reference and influence the same Angular app. I know this seems like bad practice, but I am trying to prevent the need to tear down a huge piece of legacy architecture to influence the angular app from two Javascript files in harmony.
Thanks in advance.
You can do
angular.module('myAppName').controllers.... in different files, just make sure the myAppName the same. Bug I don't feel like it work with config, no time to test. If you need any communication between them, check $emit and $broadcast.
I want to have multiple main index pages that should be accessable independent from each other. Means: there is no global page providing static links to all of the main index pages. Each should live on its own:
WebContent/indexA.html
WebContent/indexB.html
...
Question: how can I write an angularjs controller that shows these pages, if I do not call the*.html extension, but a get-query on that path?
I want to be able to call:
localhost/indexA?param=123
localhost/indexB?test=xyz
Each of them will then map to their own html page, and the request params are to be processed by the controller of that page only.
Is that possible? If yes, how?
If I understood correctly, that you need to use same code (Controller) on different HTML pages, loaded with different URLs.
First option is to use directives to encapsulate same logical parts (Each directive can have attributes, you can provide them in HTML, depend on how is your HTML structured). This is good, when you do not have any routing in your app now.
Second option is to use ui-router with HTML5 routing (then you can pick parameters from URL), this will probably require you to move more logic on frontend, but it is clear solution, if this is not small project.
Next option is to put required parameters into script tag of HTML and then load them from your controller - this is dirty, but quick way of solving your issue.
Try ui-router for the same.
Based on the parameters you pass, you can choose the active state where you can have your own controller and templateURL
This issue AngularJS disable partial caching on dev machine suggests using $templateCache.removeAll() to clear the cache templates. However what if you just want to fire this once upon each deployment cycle in order to get visitor browsers to refresh/update the template? Our problem was some browsers were not updating the template html files, we'd end up with new CSS mixed with old HTML. I do not want this function to fire all the time, that would defeat the point of cache templates to begin with (right?).
Per the title question, what's a recommended way to clear $templateCache "once", for example some ideas I've crunched:
Does Angular have an internal method of detecting if the template file has changed? And then if so "update" it.
Does Angular have an internal "version" or "date" we could compare and add a conditional to fire function removeAll()?
Does $templateCache ever itself know to refresh? What were the Angular creator's intentions in forcing templateCache on us if HTML files are bound to change overtime and served to multiple browsers.
I do not want to use grunt to add workflow overhead for something that happens periodically, nor to chop up the html file templates into variables. (Is this a good method for template cache busting in angular?)
The alternative I can see is simply adding and removing removeAll() code manually, that would be silly.
We decided to go with simply tacking on UNIX timestamp version to files which were changed like file.php?v=154325232. The reason being we weren't actually using Angular templateCache, in that we weren't storing the template data itself inside templateCache. I thought Angular automatically stores any async loaded directive template files into templateCache, thats not true, you have to explicitly use templateCache to get a file (Good tutorial on that https://thinkster.io/templatecache-tutorial).
So our solution is just boring old versioning, which is most understandable by browser headers and for non-mega-scaling websites, just fine.
$modifiedTs = filemtime($filename);
if ($modifiedTs != $lastModificationTs){
echo "$filename?v=" . time();
}
How to check if a file has changed?
Lastly the way I read templateCache, its not intended to save data between browser sessions rather its a cache service during the session. It has nothing to do with the browser cache, rather Angular stores it internally. So its meant for websites that dynamically navigate and load URLs (ie: not a true page refresh but an AJAX trick) which is how most of Google websites are today.
Prevent browser cache of angular templates
Best way to manually clear out $templateCache in AngularJS
The current popular method is to create a task using a node module that preprocesses Angular templates into a file in which you add into the js stack.
You could install gulp-ng-html2js and make it a gulp task. This would output a file say templates.js, which you then add to the head.
https://www.npmjs.com/package/gulp-ng-html2js
Then call it out in your app as a module dependency. app.module('myApp',['templates']);
So before each deployment, run this gulp task.
And if needed add a cache breaker query string templates.js?v=123
I'm making a mobile app using Cordova and AngularJS. Currently I have installed ui-router for routing but I'm open to any other alternative for routing.
My desire: I want to cache certain views bound with parameters. In other words I want to cache paths (or pages).
Example situation: let's say that we see some dashboard page, click on some book cover which redirects to the path book/2. This path is being loaded for the first time into app. Router redirects from HomeController to BooksController (whatever the name). Now the BooksController loads data for given $stateParams (book id = 2) and creates view filled with info about chosen book.
What I want in this situation:
I go back to the dashboard page - it is already loaded (cached?)
I choose book #2 again
Controller or router notices that data about this book is already loaded
The view isn't being recreated, instead it's being fetched from cache
Actually, it would be best to cache everything what I visit based on path. Preloading would be cool too.
Reason: performance. When I open some list of books then I want it to show fast. When view is being created every time, then animation of page change looks awful (it's not smooth).
Any help would be appreciated.
EDIT:
First of all, since I believe it's a common problem for many mobile HTML app programmers, I'd like to precise some information:
I'm not looking for hacks but a clear solution if possible.
Data in the views uses AngularJS, so YES, there are things like ng-bind, ng-repeat and so on.
Caching is needed for both data and DOM elements. As far as I know, browser's layout operation is not as expensive as recreating whole DOM tree. And repaint is not what we can omit.
Having separate controllers is a natural thing. Since I could leave without it I cannot imagine how it would work anyway.
I've got some semi-solutions but I'm gonna be strict about my desire.
Solution 1.
Put all views into one file (I may do it using gulp builder) and use ng-show. That's the simplest solution and I don't believe that anyone knowing AngularJS would not think about it.
A nice trick (from #DmitriZaitsev) is to create a helper function to show/hide element based on current location path.
Advantages:
It's easy.
KIND OF preload feature.
Disadvantages:
all views have to be in a single file. Don't ask why it's not convenient.
Since it's all about mobile devices, sometimes I'd like to "clear" memory. The only way I can think of is to remove those children from DOM. Dirty but ok.
I cannot easily cache /book/2 and /book/3 at the same time. I would have to dynamically create DOM children on top of some templates for each view bound with parameters.
Solution 2.
Use Sticky States AND Future States from ui-router-extras which is awesome.
Advantages:
Separated views.
Very clear usage, very simple since it's just a plugin for ui-router.
Can create dynamic substates. So it would be possible to cache book1, book2 but I'm not sure about book/1 and book/2
Disadvantages:
Again, I'm not sure but I didn't found an example with caching a pair/tuple (view, parameters). Other than that it looks cool.
This is precisely the problem I had to solve for my site 33hotels.com. You can check it and play with the tabs "Filter" and "Filter List" (corresponding to different Routes), and see that the View is updated instantly without any delay!
How did I do it? The idea is surprisingly simple - get rid of the Router!
Why? Because the way the Router works is it re-compiles the View upon every single Route change. Yes, Angular does cache the Template but not the compiled View populated with data. Even if data do not change! As the result, when I used the Router in the past, the switch always felt sluggish and non-reactive. Every time I could notice annoying delay, it was a fraction of second but still noticeable.
Now the solution I used? Don't re-compile your Views! Keep them inside your DOM at all times! Then use ng-hide/ng-show to hide/show them depending on the routes:
<div ng-show="routeIs('/dashboard')">
<-- Your template for Dashboard -->
</div>
<div ng-show="routeIs('/book')">
<-- Your template for Book -->
</div>
Then create a function routeIs(string) inside your Controller to test if $location.path() matches string, or begins with string as I am using it. That way I still get my View for all pathes like /book/2. Here is the function I am using:
$scope.routeBegins = function () {
return _.some(arguments, function (string) {
return 0 === $location.path().indexOf(string);
});
};
So no need to be smart with caching - just keep it in the DOM. It will cache your Views for you!
And the best part is - whenever your data is changed, Angular will instantly update all the Views inside your DOM, even the hidden ones!
Why is this awesome? Because, as user, I don't have to wait for all the parsing and compiling at the moment I want to see the result. I want to click the tab and see my results immediately! Why should the site wait for me to click it and then begin all the re-compiling as I am waiting? Especially when this could be easily done before, during the time my computer is idle.
Is there any downside? The only real one I can think of is loading memory with more DOM elements. However, this actual byte size of my views is negligible, comparing e.g. with all JS, CSS and images.
Another possible but avoidable downside is the re-compilation cost of the hidden views. This is where you can get smart and avoid computation-heavy parts depending on the current routes.
Also, you are not re-compiling the whole View, just the parts affected by data changes, which also lowers computational cost.
I find it quite remarkable that everyone is using Routes and seems to be completely unaware (or ignorant) of this problem.
1) About static pages in the app (views), angular takes care of loading them.
for example: for your dashboard page you need not worry about caching the page, as angular will take care of it. Angular will only load the dashboard view once and on all next requests for the dashboard view, angular will just show you the view(not load the file for view), if it is all a static view without any data loaded by ajax calls.
2) if your dashboard is itself loading the book list(or similar data) via ajax, then you can tell your controller to only load the data once and store it to localstorage and on subsequent requests to the dashboard page can only load the data from the localStorage.
3) similar approach can be used when your BooksController loads the data into a view. You can check in your BooksController if the request for a particular book is been previously made and if not your can store it to localstorage or database. and if the data was previously requested then you can load the same data from the storage without making a request to server.
Example situation:
say user makes request for book1, then
your controller i.e BooksController check whether the same data was requested before,
if not, you can load the data via the ajax call from server and also save it to local storage.
if it was loaded before you will load the data stored in the localstorage or in the database.
If you're using ui.router, then you should take a look at ui.router extras, specifically the sticky states module. This allows you to cache the state 'tree' (including rendered views) so they don't have to be compiled or re-rendered on state changes.
http://christopherthielen.github.io/ui-router-extras/
Here's a demo:
http://christopherthielen.github.io/ui-router-extras/example/sticky/#/
When building a web app where every page depends on many data sources, what's the best way to fetch the initial bits of data? When I look at twitter, I see the tweets that are visible on page load are in the HTML source, and more tweets are loaded in using AJAX as you scroll down. But there's no convenient way to get data that's already in the DOM to be inserted into the model.
Making a request for the initial data, immediately after page load seams stupid, because you've just made a lot of roundtrips to the server to fetch css, html and javascript. Would it be a bad idea to insert the data into a javascript tag on the page, so a javascript function can add the initial data?
I'm specifically asking for angularjs, but if there's an general technique, please let me know as well.
You'll be referencing your controller anyway on page load, so you won't have to have an inline script tag.
You can either set a default model and use the attribute ng-bind on initial load, or call a function to pass back data.
It's pretty typical to fetch data on load in angularjs.
Would it be best to couple Angularjs with an HTTP Client in the backend like Zend_Http_Client or Guzzle to let the server fetch the data. Then, pass the data as json to javascript upon render.
I know Angularjs is designed for Single Page applications. That's why it makes sense that it lazy loads the data.
However, if we're going to move to the approach where we still render the page dynamically and still delegate the task of organizing the content to Angularjs. What framework will be suitable to contain the AngularJS views. Right now, project templates like angular-seed are all static..
That is, the idea is the server serves a page with the embedded json object. Then angular, takes over in the client side, Fetching additional content where needed.
So instead of just a single page of contact (e.g: index.html), we would have several pages like profiles.html, products.html. The help of the backend would be particularly helpful say you have a section which doesn't change often like your username on the top right side of the page. For me, I just think it's better to have these data preloaded in your page and not have to ask the server after the page has been loaded.
As bigblind have noticed, this seems to be the way sites like facebook, gmail, twitter does it. They contain the data embedded on page load. Then, load additional content via services afterwards.
The idea is something like below:
Webservice <---------- Backend------------> Frontend
<------------------------------------------
Backend delegates the task of querying the webservice to provide initial data in the rendered page to the client. Then client, can directly connect to webservice to fetch additional content.
Using the above setup.. What is the ideal development stack?
One way to do it is to create a directive that handles the initialization before binding happens.
For example:
app.directive('initdata', function() {
return {
restrict: 'A',
link: function($scope, element, attrs) {
if ( attrs.ngBind !== undefined)
{
$scope[attrs.ngBind] = attrs.initdata ? attrs.initdata : element.text();
}
}
};
});
This directive takes either the attribute value as initial value for the bound $scope property, or the textvalue of the element.
Example usage:
<div initdata="Foo Bar" ng-bind="test1"></div>
<div initdata ng-bind="test2">Lorem Ipsem</div>
Working example on http://jsfiddle.net/6PNG8/
There's numerous way to elaborate on this; for example parsing the initdata as json and merging it with the scope, and making it work for more complicated binds, like $root.someprop. But the basis is remarkably simple.
According to the answers on this question, a JSON object in a script tag on the page seems to be the way to go. If ayone comes up with a better idea, I'll accept your answer.