mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(templateCache): automatically cache template files to prevent flicker on page navigation and improve performance
State templates are cached automatically, but you can optionally cache other templates.
```js
$ionicTemplateCahce('myNgIncludeTemplate.html');
```
Optionally disable all preemptive caching with the `$ionicConfigProvider` or individual states by setting `prefetchTemplate`
in the $state definition
```js
$ionicTemplateCahce('myNgIncludeTemplate.html');
```js
angular.module('myApp', ['ionic'])
.config(function($stateProvider, $ionicConfigProvider) {
// disable preemptive template caching globally
$ionicConfigProvider.prefetchTemplates(false);
// disable individual states
$stateProvider
.state('tabs', {
url: "/tab",
abstract: true,
prefetchTemplate: false,
templateUrl: "tabs-templates/tabs.html"
})
.state('tabs.home', {
url: "/home",
views: {
'home-tab': {
prefetchTemplate: false,
templateUrl: "tabs-templates/home.html",
controller: 'HomeTabCtrl'
}
}
});
});
```
This commit is contained in:
142
js/angular/service/templateCache.js
vendored
Normal file
142
js/angular/service/templateCache.js
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// closure to keep things neat
|
||||
(function() {
|
||||
var templatesToCache = [];
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $ionicTemplateCache
|
||||
* @module ionic
|
||||
* @description An angular service that preemptively caches template files to reduce flicker and boost performance.
|
||||
* @usage
|
||||
* State templates are cached automatically, but you can optionally cache other templates.
|
||||
* ```js
|
||||
* $ionicTemplateCahce('myNgIncludeTemplate.html');
|
||||
* ```
|
||||
*
|
||||
* Optionally disable all preemptive caching with the `$ionicConfigProvider` or individual states by setting `prefetchTemplate`
|
||||
* in the $state definition
|
||||
* ```js
|
||||
* $ionicTemplateCahce('myNgIncludeTemplate.html');
|
||||
* ```js
|
||||
* angular.module('myApp', ['ionic'])
|
||||
* .config(function($stateProvider, $ionicConfigProvider) {
|
||||
*
|
||||
* // disable preemptive template caching globally
|
||||
* $ionicConfigProvider.prefetchTemplates(false);
|
||||
*
|
||||
* // disable individual states
|
||||
* $stateProvider
|
||||
* .state('tabs', {
|
||||
* url: "/tab",
|
||||
* abstract: true,
|
||||
* prefetchTemplate: false,
|
||||
* templateUrl: "tabs-templates/tabs.html"
|
||||
* })
|
||||
* .state('tabs.home', {
|
||||
* url: "/home",
|
||||
* views: {
|
||||
* 'home-tab': {
|
||||
* prefetchTemplate: false,
|
||||
* templateUrl: "tabs-templates/home.html",
|
||||
* controller: 'HomeTabCtrl'
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
IonicModule
|
||||
.factory('$ionicTemplateCache', [
|
||||
'$http',
|
||||
'$templateCache',
|
||||
'$timeout',
|
||||
'$ionicConfig',
|
||||
function($http, $templateCache, $timeout, $ionicConfig) {
|
||||
var toCache = templatesToCache,
|
||||
hasRun = false;
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicTemplateCache
|
||||
* @description Add template(s) to be cached.
|
||||
* @param {string | array} string or array of strings of templates to cache.
|
||||
*/
|
||||
function $ionicTemplateCache(templates){
|
||||
if(toCache.length > 500) return false;
|
||||
if(typeof templates === 'undefined')return run();
|
||||
if(isString(templates))templates = [templates];
|
||||
forEach(templates, function(template){
|
||||
toCache.push(template);
|
||||
});
|
||||
// is this is being called after the initial IonicModule.run()
|
||||
if(hasRun) run();
|
||||
}
|
||||
|
||||
// run through methods - internal method
|
||||
var run = function(){
|
||||
if($ionicConfig.prefetchTemplates === false)return
|
||||
//console.log('prefetching', toCache);
|
||||
//for testing
|
||||
$ionicTemplateCache._runCount++;
|
||||
|
||||
hasRun = true;
|
||||
// ignore if race condition already zeroed out array
|
||||
if(toCache.length === 0)return;
|
||||
//console.log(toCache);
|
||||
var i = 0;
|
||||
while ( i < 5 && (template = toCache.pop()) ) {
|
||||
// note that inline templates are ignored by this request
|
||||
if (isString(template)) $http.get(template, { cache: $templateCache });
|
||||
i++;
|
||||
}
|
||||
// only preload 5 templates a second
|
||||
if(toCache.length)$timeout(function(){run()}, 1000);
|
||||
};
|
||||
|
||||
// exposing for testing
|
||||
$ionicTemplateCache._runCount = 0;
|
||||
// default method
|
||||
return $ionicTemplateCache;
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc config
|
||||
* @name $ionicTemplateCache
|
||||
* @module ionic
|
||||
* @private
|
||||
*
|
||||
* Intercepts the $stateprovider.state() command to look for templateUrls that can be cached
|
||||
*/
|
||||
.config([
|
||||
'$stateProvider',
|
||||
'$ionicConfigProvider',
|
||||
function($stateProvider, $ionicConfigProvider) {
|
||||
var stateProviderState = $stateProvider.state;
|
||||
$stateProvider.state = function(stateName, definition) {
|
||||
// don't even bother if it's disabled. note, another config may run after this, so it's not a catch-all
|
||||
if($ionicConfigProvider.prefetchTemplates() !== false){
|
||||
var enabled = definition.prefetchTemplate != false;
|
||||
if(enabled && isString(definition.templateUrl))templatesToCache.push(definition.templateUrl);
|
||||
if(angular.isObject(definition.views)){
|
||||
for (var key in definition.views){
|
||||
enabled = definition.views[key].prefetchTemplate != false;
|
||||
if(enabled && isString(definition.views[key].templateUrl)) templatesToCache.push(definition.views[key].templateUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stateProviderState.call($stateProvider, stateName, definition);
|
||||
};
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc run
|
||||
* @name $ionicTemplateCache
|
||||
* @module ionic
|
||||
* @private
|
||||
*
|
||||
* process the templateUrls collected by the $stateProvider, adding them to the cache
|
||||
*/
|
||||
.run(function($ionicTemplateCache) {
|
||||
$ionicTemplateCache();
|
||||
});
|
||||
|
||||
})();
|
||||
112
test/html/tabs-cache.html
Normal file
112
test/html/tabs-cache.html
Normal file
@@ -0,0 +1,112 @@
|
||||
<html ng-app="ionicApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
|
||||
<title>Tabs Example</title>
|
||||
|
||||
<link href="../../../../dist/css/ionic.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ion-nav-bar class="nav-title-slide-ios7 bar-positive">
|
||||
<ion-nav-back-button class="button-icon ion-arrow-left-c">
|
||||
</ion-nav-back-button>
|
||||
</ion-nav-bar>
|
||||
|
||||
<ion-nav-view animation="slide-left-right"></ion-nav-view>
|
||||
|
||||
<script id="tabs-templates/contact.html" type="text/ng-template">
|
||||
<ion-view title="Contact">
|
||||
<ion-content>
|
||||
<div class="list">
|
||||
<div class="item">
|
||||
@IonicFramework
|
||||
</div>
|
||||
<div class="item">
|
||||
@DriftyTeam
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../../../../dist/js/ionic.bundle.js"></script>
|
||||
<script>
|
||||
angular.module('ionicApp', ['ionic'])
|
||||
|
||||
.config(function($stateProvider, $ionicConfigProvider) {
|
||||
|
||||
$ionicConfigProvider.prefetchTemplates(false);
|
||||
|
||||
$stateProvider
|
||||
.state('tabs', {
|
||||
url: "/tab",
|
||||
abstract: true,
|
||||
templateUrl: "tabs-templates/tabs.html"
|
||||
})
|
||||
.state('tabs.home', {
|
||||
url: "/home",
|
||||
views: {
|
||||
'home-tab': {
|
||||
templateUrl: "tabs-templates/home.html",
|
||||
controller: 'HomeTabCtrl'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.facts', {
|
||||
url: "/facts",
|
||||
views: {
|
||||
'home-tab': {
|
||||
templateUrl: "tabs-templates/facts.html"
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.facts2', {
|
||||
url: "/facts2",
|
||||
views: {
|
||||
'home-tab': {
|
||||
templateUrl: "tabs-templates/facts2.html"
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.about', {
|
||||
url: "/about",
|
||||
views: {
|
||||
'about-tab': {
|
||||
templateUrl: "tabs-templates/about.html"
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.navstack', {
|
||||
url: "/navstack",
|
||||
views: {
|
||||
'about-tab': {
|
||||
templateUrl: "tabs-templates/nav-stack.html",
|
||||
prefetchTemplate:false
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.contact', {
|
||||
url: "/contact",
|
||||
views: {
|
||||
'contact-tab': {
|
||||
templateUrl: "tabs-templates/contact.html"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$urlRouterProvider.otherwise("/tab/home");
|
||||
|
||||
})
|
||||
|
||||
.controller('HomeTabCtrl', function($scope, $ionicTemplateCache) {
|
||||
$ionicTemplateCache('test');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10
test/html/tabs-templates/about.html
Normal file
10
test/html/tabs-templates/about.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<ion-view title="About">
|
||||
<ion-content class="padding">
|
||||
<h3>Create hybrid mobile apps with the web technologies you love.</h3>
|
||||
<p>Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly interactive apps.</p>
|
||||
<p>Built with Sass and optimized for AngularJS.</p>
|
||||
<p>
|
||||
<a class="button icon icon-right ion-chevron-right" href="#/tab/navstack">Tabs Nav Stack</a>
|
||||
</p>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
13
test/html/tabs-templates/facts.html
Normal file
13
test/html/tabs-templates/facts.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<ion-view title="Facts">
|
||||
<ion-content class="padding">
|
||||
<p>Banging your head against a wall uses 150 calories an hour.</p>
|
||||
<p>Dogs have four toes on their hind feet, and five on their front feet.</p>
|
||||
<p>The ant can lift 50 times its own weight, can pull 30 times its own weight and always falls over on its right side when intoxicated.</p>
|
||||
<p>A cockroach will live nine days without it's head, before it starves to death.</p>
|
||||
<p>Polar bears are left handed.</p>
|
||||
<p>
|
||||
<a class="button icon ion-home" href="#/tab/home"> Home</a>
|
||||
<a class="button icon icon-right ion-chevron-right" href="#/tab/facts2">More Facts</a>
|
||||
</p>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
14
test/html/tabs-templates/facts2.html
Normal file
14
test/html/tabs-templates/facts2.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<ion-view title="Also Factual">
|
||||
<ion-content class="padding">
|
||||
<p>111,111,111 x 111,111,111 = 12,345,678,987,654,321</p>
|
||||
<p>1 in every 4 Americans has appeared on T.V.</p>
|
||||
<p>11% of the world is left-handed.</p>
|
||||
<p>1 in 8 Americans has worked at a McDonalds restaurant.</p>
|
||||
<p>$283,200 is the absolute highest amount of money you can win on Jeopardy.</p>
|
||||
<p>101 Dalmatians, Peter Pan, Lady and the Tramp, and Mulan are the only Disney cartoons where both parents are present and don't die throughout the movie.</p>
|
||||
<p>
|
||||
<a class="button icon ion-home" href="#/tab/home"> Home</a>
|
||||
<a class="button icon ion-chevron-left" href="#/tab/facts"> Scientific Facts</a>
|
||||
</p>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
10
test/html/tabs-templates/home.html
Normal file
10
test/html/tabs-templates/home.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<ion-view title="Home">
|
||||
<ion-content class="padding">
|
||||
<p>Example of Ionic tabs. Navigate to each tab, and
|
||||
navigate to child views of each tab and notice how
|
||||
each tab has its own navigation history.</p>
|
||||
<p>
|
||||
<a class="button icon icon-right ion-chevron-right" href="#/tab/facts">Scientific Facts</a>
|
||||
</p>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
5
test/html/tabs-templates/nav-stack.html
Normal file
5
test/html/tabs-templates/nav-stack.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<ion-view title="Tab Nav Stack">
|
||||
<ion-content class="padding">
|
||||
<p><img src="http://ionicframework.com/img/diagrams/tabs-nav-stack.png" style="width:100%"></p>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
15
test/html/tabs-templates/tabs.html
Normal file
15
test/html/tabs-templates/tabs.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<ion-tabs class="tabs-icon-top tabs-positive">
|
||||
|
||||
<ion-tab title="Home" icon="ion-home" href="#/tab/home">
|
||||
<ion-nav-view name="home-tab"></ion-nav-view>
|
||||
</ion-tab>
|
||||
|
||||
<ion-tab title="About" icon="ion-ios7-information" href="#/tab/about">
|
||||
<ion-nav-view name="about-tab"></ion-nav-view>
|
||||
</ion-tab>
|
||||
|
||||
<ion-tab title="Contact" icon="ion-ios7-world" ui-sref="tabs.contact">
|
||||
<ion-nav-view name="contact-tab"></ion-nav-view>
|
||||
</ion-tab>
|
||||
|
||||
</ion-tabs>
|
||||
38
test/unit/angular/service/templateCache.unit.js
Normal file
38
test/unit/angular/service/templateCache.unit.js
Normal file
@@ -0,0 +1,38 @@
|
||||
describe('$ionicTemplateCache', function() {
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
beforeEach(inject(function($ionicTemplateCache, $templateCache) {
|
||||
ionicTemplateCache = $ionicTemplateCache;
|
||||
templateCache = $templateCache;
|
||||
}));
|
||||
|
||||
it('should run during initial startup', function () {
|
||||
var info = templateCache.info();
|
||||
expect(info.size).toBe(0);
|
||||
expect(ionicTemplateCache._runCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should run immediately after a new addition after initial run', inject(function ($templateCache, $httpBackend, $timeout) {
|
||||
$httpBackend.whenGET("/test").respond([{hello:"world"}]);
|
||||
ionicTemplateCache('/test');
|
||||
$timeout.flush();
|
||||
var info = templateCache.info();
|
||||
expect(info.size).toBe(1);
|
||||
expect(ionicTemplateCache._runCount).toBe(2);
|
||||
}));
|
||||
|
||||
it('should cache 5 templates at a time', inject(function ($httpBackend, $timeout) {
|
||||
$httpBackend.whenGET("/test1").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test2").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test3").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test4").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test5").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test6").respond([{hello:"world"}]);
|
||||
$httpBackend.whenGET("/test7").respond([{hello:"world"}]);
|
||||
ionicTemplateCache(['/test1','/test2','/test2','/test3','/test4','/test5','/test6','/test7']);
|
||||
$timeout.flush();
|
||||
var info = templateCache.info();
|
||||
expect(info.size).toBe(7);
|
||||
expect(ionicTemplateCache._runCount).toBe(3);
|
||||
}));
|
||||
});
|
||||
Reference in New Issue
Block a user