What's the simplest/modern way of caching partials in angularjs production?
Currently the code looks like:
$routeProvider.when('/error', {templateUrl: 'partials/error.html', controller: 'ErrorCtrl'});
Where the templateUrl is obviously an http path to a separate file. On mobile the loading time for that file is noticeable and I'd love to just cache everything.
The main part of the answer is the $templateCache. An extract:
var myApp = angular.module('myApp', []);
myApp.run(function($templateCache) {
$templateCache.put('templateId.html', 'This is the content of the template');
});
Any of the html templates, can be moved to the $templateCache, and the rest of our application will act as expected (no other changes required)
local-storage as a cache
In case, that we would like to keep the template on the client, we can use the local storage as well. This angular-local-storage extension would simplify lot of stuff.
So, let's adjust the run() to
observe the local-storage, if we do not already have the template on the client
issue the request to load the latest, if needed...
put it into the caches (local-storage and $templateCache)
The adjusted code
.run([ 'localStorageService','$templateCache','$http',
function myAppConfig(localStorageService , $templateCache , $http) {
// The clearAll() should be called if we need clear the local storage
// the best is by checking the previously stored value, e.g. version constant
// localStorageService.clearAll();
var templateKey = "TheRealPathToTheTemplate.tpl.html";
// is it already loaded?
var template = localStorageService.get(templateKey);
// load the template and cache it
if (!template) {
$http.get(templateKey)
.then(function(response) {
// template loaded from the server
template = response.data;
localStorageService.add(templateKey, template);
$templateCache.put(templateKey, template);
});
} else {
// inject the template
$templateCache.put(templateKey, template);
}
}])
So, this way, we do profit from the local-storage. It is filled with the "template" from the server, kept there... and therefore not loaded next time.
NOTE: Very important is also to inject some version key/value and check it. If the Local storage is out-dated... all templates must be reloaded.
Related
In my Angular JS site, I have many modules & many resources (From where I consume Rest API)
I want to add a custom header to all outgoing requests in each & every module.
For eg : Here are 2 modules : common & ABC
//---File 1 common.js
angular.module("common",[])
.config(['$httpProvider',
function($httpProvider)
{
$httpProvider.defaults.headers.common['x-access-token'] =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiQWJkdWwiLCJpYXQiOjE0NjUwMzkwMzgsImV4cCI6MTQ2NTEyNTQzOH0.6BMBuEl2dbL736qUqNYXG29UBn_HRyCyWEmMXSG3euE';
}
])
.service("commonApi",['$resource',
function($resource)
{
this.getBankList = function()
{
return $resource('api/emi/banklist:quoteId', { },{}).query();
}
}]);
//---File 2 abc.js
angular.module("abc",[])
.config(['$httpProvider',
function($httpProvider)
{
$httpProvider.defaults.headers.common['x-access-token'] =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiQWJkdWwiLCJpYXQiOjE0NjUwMzkwMzgsImV4cCI6MTQ2NTEyNTQzOH0.6BMBuEl2dbL736qUqNYXG29UBn_HRyCyWEmMXSG3euE';
}
])
.factory('emiModel', ['$resource',
function($resource) {
return $resource('api/emi/QuoteList:quoteId', { }, {
update: { method: 'PUT' }
});
}])
In the above code, I had to add .config to each module & add the header there.
It is quite time consuming to add it in each module & violates DRY principle.
Is there any simple way by which I can add this configuration to all modules in my app without repeating the code ?
For Carity : I used factory & service just to show that i might be using any thing but I still want the header to be passed.
In the above code, I had to add .config to each module & add the
header there.
It is quite time consuming to add it in each module & violates DRY
principle.
This isn't true. Once the module is loaded, Angular doesn't make a difference between them.
config block affects each and every module in the app that has common module loaded. I.e. all of $http calls will be affected with config in this setup:
angular.module("app",["abc", "common"])...
angular.module("abc",[])...
Though it is recommended to load common module in each submodule that depends on config, too. So they don't break in the case when they are loaded apart from app (e.g. in specs).
My application needs some config values on application startup. Suggestions from the community is to store them as constant as separate module, preferably in separate .js file. This might work for me.
However my configuration values are also stored on the server, and dont want to duplicate those on client side, so i was thinking of making server call to get those.
Im newbie to angular, is it valid design practice to make server call in module's config method? If yes then should i just use $http service to get the values from the server?
var main = angular.module('myapp', ['AdalAngular']);
main.config(['$stateProvider',$httpProvider, adalAuthenticationServiceProvider', function ($stateProvider,$httpProvider,adalProvider) {
// $stateProvider configuration goes here
// ?????CAN I make server call here to get configuration values for adalProvider.init method below???
adalProvider.init(
{
instance: 'someurl',
tenant: 'tenantid',
clientId: 'clientid',
extraQueryParameter: 'someparameter',
cacheLocation: 'localStorage',
},
$httpProvider
);
}]);
main.run(["$rootScope", "$state", .....
function ($rootScope, $state,.....) {
// application start logic
}]);
main.factory("API", ["$http", "$rootScope", function ($http, $rootScope) {
// API service that makes server call to get data
}]);
EDIT1
So based on suggestions below I'm going with declaring constant approach. Basically I will have separate config.js file and during deployment process I will overwrite the config.js file with respective environment based config.js file.
Question
If have to 10 constants then i have to pass them separately to module.config(). Is it possible to declare constant value as JSON object and somehow read it in config function so I don't have pass 10 different parameters?
angular.module('myconfig', [])
.constant('CONFIGOBJECT','{Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}');
and then how do I read the values in config method?
var main = angular.module('myapp',['myconfig']);
main.config(['CONFIGOBJECT',function(CONFIGOBJECT){
?? How do I read CONFIGOBJECT value that is a string not json object?
})
I'll describe the solution used in project that i was working on some time ago.
It's true that you cannot use services in config phase, and it's also true, that you can use providers and constants while config phase.
So we used the next solution:
firstly, we created simple object with config, like
var config = {
someConfig1: true,
someConfig2: false,
someConfigEvents: {
event1: 'eventConfig1',
event2: 'eventConfig2'
}
etc...
}
Then we also declared angular value with jQuery lib:
app.value('jQuery', jQuery);
And now we cannot use services like $http, but we can use jQuery, so we just making ajax call to config server and extending our config:
jQuery.ajax("path/to/config", { async: false, cache: false })
.done(function (data) {
var response = angular.fromJson(data)
if (response) {
angular.extend(config, response.returnData.data);
} else {
alert('error');
}
})
.fail(function () {
alertError();
})
.always(function () {
appInit();
});
You cannot inject a service into the config section.
You can inject a service into the run section.
So, you cannot use - for example $http service to retrieve data from server inside config() , but you can do in inside run(), which initializes the provider's service.
See also more complete answer here.
Hope this helps.
UPDATE:
Why string? Why don't you simply use
.constant('CONFIGOBJECT', {Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}
for
.constant('CONFIGOBJECT', '{Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}'
?
Only providers are available during the config phase, not services. So you can't use $http during this phase.
But you can use it during the execution phase (in a function passed to run()).
An alternative is to have some JavaScript file dynamically generated by the server, and defining the constants you want.
Another alternative is to generate such a JS file during the build, based on some file that would be read by the server-side code.
We have an Angular project where the templates have changed numerous times thanks to our "Agile" environment. Browsers seem to strongly cache the templates because of the html file type. This means that when business goes to our dev site after an update, they occasionally see the old templates. How can we make sure that when changes are made to the templates, the user downloads the new template instead of loading from the cache?
We use Jade and to prevent caching, we have a variable based on the time that gets appended to the end of our JS/CSS includes (style.css?v=2012881). Since we already have an 'appVersion' via this variable, I chose to expose that variable using an angular module and constant:
script.
angular.module('appVersion',[]).constant('appVersion',#{curDate});
In my main Angular module I have:
.config(['$httpProvider','appVersion',function($httpProvider,appVersion){
$httpProvider.interceptors.push(function() {
return {
'request': function(config) {
if(!config.cached && config.url.indexOf('.html') > -1){
if(config.url.indexOf("?") > -1){
config.url = config.url.replace("?","?v="+appVersion+"&");
}
else{
config.url += "?v="+appVersion;
}
}
return config;
}
};
});
}])
Since the templates are loaded using $http.get, I added an interceptor that detects if a request is a request for a template and appends the appVersion to the request if it is. That way we have the same versioning for the CSS, JS, and HTML.
Use tools like grunt-filerev (https://github.com/yeoman/grunt-filerev) for static revisioning. They basically add a file content hash, so that caching becomes impossible.
I have two angular applications in one page, and I need them to communicate. Specifically, I want one application to use a service of another application.
I am able to get the service of the other application using Injector.get(service), but when I change the data using the service in one application, it does not reflect in the view of the other, even though both are supposed to show the same data. You can see a simplified version of the problem in jsFiddle.
To save you the click, this is the relevant script:
//myAppLeft - an angular app with controller and service
var myAppLeft = angular.module('myAppLeft', []);
myAppLeft.factory('Service1',function(){
var serviceInstance = {};
serviceInstance.data = ['a','b','c','d','e'];
serviceInstance.remove = function(){
serviceInstance.data.pop();
console.log(serviceInstance.data);
};
return serviceInstance;
} );
myAppLeft.controller('Ctrl1', ['$scope', 'Service1', function($scope, service1) {
$scope.data = service1.data;
$scope.changeData =function(){
service1.remove();
}
}]);
var leftAppInjector = angular.bootstrap($("#leftPanel"), ['myAppLeft']);
//myAppRight = an angular app with controller which uses a service from myAppLeft
var myAppRight = angular.module('myAppRight', []);
myAppRight.controller('Ctrl2', ['$scope', function($scope) {
$scope.data = leftAppInjector.get('Service1').data;
$scope.changeData =function(){
leftAppInjector.get('Service1').remove();
}
}]);
var rightAppInjector = angular.bootstrap($("#rightPanel"), ['myAppRight']);
I'd be happy to know why my code does not work as expected, and would be even happier to know if and how such thing can work.
I understand that if instead of two angular-apps I would have used one angular-app with two modules this would have worked just as I wanted, but unfortunately I cannot adopt this approach because my application consists of a pure-js core with extensions, each extension can be written in a different library/platform and I want my extensions to be angular ones.
Thanks,
Nurit.
Angular apps are separate entities, even if you use the same service in both. the second app just initializes its own version off it.
What you want can be done using localStorage, and the storage events.
Ping me if you need additional help on this!
I've created a service using the CacheFactory. I was expecting it to be a singleton. I inject it into my controller and it works fine within the scope of the controller. But once I go to a different page with a different scope, I don't seem to have the values in the cache that I stored in the same controller in a different scope. Shouldn't the behavior of the CacheFactory be a singleton where I have those same cached objects everywhere I inject the CacheService?
This is my service as an example:
angular.module('MyService', []).factory('CacheService', function($cacheFactory) {
return $cacheFactory('cacheService', {
capacity: 3 // optional - turns the cache into LRU cache
})
});
Then in my controller:
function MyController($scope, CacheService) {
var results= CacheService.get('storedvalue');
if(!results){
CacheService.put('storedvalue', results);
alert('results not stored');
}
else
alert('results stored');
}
$cacheFactory is actually not the service you want to use - it's a factory that you use to create the singleton service you want to use. Eyeballing your code, it seems like it should work. But I went ahead and created a demo to prove that it does. Here's the service:
.factory('CacheService', function($cacheFactory) {
var cache = $cacheFactory('cacheService', {
capacity: 3 // optional - turns the cache into LRU cache
});
return cache;
});
In this example, CacheService is the singleton that has a local cache created with $cacheFactory, which is what we return from the service. We can inject this into any controller we want and it will always return the same values.
Here's a working Plunker: http://plnkr.co/edit/loKWGms1lMCnmiWa1QA7?p=preview
If for some reason your post doesn't contain what broke your code, please feel free to update my Plunker to break it and we can go from there.