我在 Cordova/AngularJS 应用程序中有这个奇怪的错误。
有时(很少)AngularJS 找不到控制器和 chrashes,让用户看到一半加载无响应的视图。
该错误似乎仅在手机已锁定一段时间并且我几乎不可能定期重新创建时才会发生。它只是有时会发生。
当用户关闭应用程序并重新启动它时,一切都会再次正常运行。
到目前为止,我只收到有关 Android 设备而不是 iOS 设备的此错误的投诉。
编辑:我现在也看到了 iOS 设备上的错误。
我真的希望有人能够阐明这个问题。
我的 index.html 看起来像这样
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="guideApp">
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline'; script-src * 'self' 'unsafe-eval' 'unsafe-inline' https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.js http://connect.facebook.net/en_GB/sdk.js;">
<link rel="stylesheet" href="css/snap.css" />
<link rel='stylesheet' href='css/mapbox.css' />
<link rel="stylesheet" href="css/leaflet.css" />
<link rel="stylesheet" href="css/guideapp.css" />
<link rel="stylesheet" href="css/xpengineicons.css" />
<!--<link rel="stylesheet" href="css/guideappTest.css" />-->
<title></title>
<link rel="icon" type="image/png" href="img/favicon.png"/>
<meta name="application-name" content="GuideApp"/>
<meta name="msapplication-TileColor" content="#000000" />
<meta name="msapplication-TileImage" content="img/favicons/mstile-144x144.png" />
<meta name="msapplication-square70x70logo" content="img/favicons/mstile-70x70.png" />
<meta name="msapplication-square150x150logo" content="img/favicons/mstile-150x150.png" />
<meta name="msapplication-wide310x150logo" content="img/favicons/mstile-310x150.png" />
<meta name="msapplication-square310x310logo" content="img/favicons/mstile-310x310.png" />
</head>
<body ng-controller="globalCtrl" data-snap-ignore="true">
<div class="snap-drawers">
<div class="snap-drawer snap-drawer-left sideMenu">
<a href="#" ng-click="goTo('/home')">{{::langPack.startPage[lang]}}</a>
<a href="#" ng-click="goTo('/tourSelect')">{{::langPack.selectTour[lang]}}</a>
<a href="#" ng-click="goTo('/map')">{{::langPack.map[lang]}}</a>
<a href="#" ng-click="goTo('/news')">{{::langPack.news[lang]}}</a>
<a href="#" ng-click="toggleFoto($event)" class="fotoToggle" ng-class="{'redFotoToggle': photosInUploadQue > 0}">{{::langPack.photoMenu[lang]}}<i class="icon-angle-down fotoToggleAngle"></i></a>
<div class="fotoMenu" id="fotoMenu">
<a href="#" ng-click="toggleUploadPopup()" class="noUploadMenuItem" ng-class="{'uploadMenuItem': photosInUploadQue > 0}" compile="langPack.uploadPhotos[lang]"></a>
<a href="#" ng-click="goTo('/commonTours')">{{::langPack.followAnothersTour[lang]}}</a>
<a href="#" ng-click="broadcastResetPhotoTour();">{{::langPack.endPhototour[lang]}}</a>
<a href="#" ng-click="goTo('/viewtours')">{{::langPack.seePreviousTour[lang]}}</a>
<!--<a href="#" ng-click="selectPicture();">{{::langPack.photoFromGallery[lang]}}</a>-->
<a href="#" ng-click="broadcastSelectPicture();">{{::langPack.selectFromGallery[lang]}}</a>
<!--<a href="#" ng-if="actionRadius>100" ng-click="broadcastToggleActionRadius();">[Smaller radius]</a>-->
<!--<a href="#" ng-if="actionRadius<100" ng-click="broadcastToggleActionRadius();">[Larger radius]</a>-->
<!--<a href="#" ng-click="broadcastToggleActionRadius();">[{{langPack.toggleActionRadius[lang]}}]</a>-->
<!--<a href="#" ng-click="goTo('/end')">[End waypoint tour]</a> -->
</div>
<a href="#" ng-click="goTo('/about')">{{::langPack.about[lang]}}</a>
<a href="#" ng-click="sync.checkUpdates();">
{{::langPack.sync[lang]}}
<span id="updateCheck" class="updateCheck"><div class="circle"></div>{{::langPack.checkForUpdates[lang]}}</span>
<span id="uptodate" class="updateCheck uptodate"><i class="icon-ok"></i>{{::langPack.upToDate[lang]}}</span>
</a>
<a href="#" ng-click="showIntroOverlay()">{{::langPack.showIntroOverlay[lang]}}</a>
</div>
</div>
<div id="content" class="snap-content content" ng-style="customBg">
<nav id="header" ng-controller="cameraCtrl">
<a href="" ng-click="toggleMenu()" class="menuBtn show" id="menuBtn"><img src="img/menu-regular.svg"><!--<div class="menuNotification" ng-if="photosInUploadQue > 0">{{photosInUploadQue}}</div>--></a>
<a href="#" ng-click="goTo(backDirection)" class="backBtn" id="backBtn"><i class="icon-angle-up"></i></a>
<h1></h1>
<img ng-src="{{customLogo}}" id="logo">
<!-- Kamera når det virker -->
<a href="#" ng-click="takeGuidePicture()" class="cameraBtn" ng-if="showCam === true"><i class="icon-camera"></i></a>
<!-- <a href="#" ng-click="selectPicture()" class="galleryPhotoBtn" ng-if="showCam === true"><i class="icon-angle-down"></i></a> -->
<!-- <a href="#" ng-click="openUploadPopup($event)" id="photoUpload" class="uploadBtn"><i class="icon-angle-up"></i></a> -->
<!-- <a onclick = "window.open('http://guideapp.tmts.dk/', '_blank', 'location=yes'); return false;" id="viewInBrowser" class="uploadBtn"><i class="icon-angle-down"></i></a> -->
<div id="uploadPopupOverlay" class="popupOverlay" ng-click="closeUploadPopup();"></div>
<section id="uploadPopup">
<p>{{::langPack.uploadAndShare[lang]}}</p>
<form novalidate ng-submit="uploadPhotosSubmittedByPopup($event)">
<label for="emailInput" ng-show="showEmail === true">
{{::langPack.yourEmail[lang]}}
<div class="error" ng-if="emailMsg">{{::emailMsg}}</div>
</label>
<input type="email" value="{{emailAdress}}" required name="emailInput" ng-model="emailAdress" ng-show="showEmail === true" id="emailAdress">
<label for="titleInput">
{{::langPack.yourTitel[lang]}}
<div class="error" ng-if="titleMsg">{{::titleMsg}}</div>
</label>
<input type="text" value="{{tourTitle}}" name="titleInput" ng-model="tourTitle" id="uploadTitle" required>
<p class="emailExists" ng-show="showEmail === false"><span compile="langPack.sendTo[lang]"></span> <a href="#" ng-click="editEmail($event)">[{{::langPack.editEmail[lang]}}]</a></p>
<button class="btn left" ng-click="closeUploadPopup();">{{::langPack.cancel[lang]}}</button>
<button type="submit" class="btn right">{{::langPack.ok[lang]}}</button>
</form>
</section>
</nav>
<div class="slideInLeft" ng-view></div>
</div>
<aside ng-class="{active: showIntro}" id="intro" ng-swipe-left="nextSlide()" class="" ng-swipe-right="prevSlide()">
<div>
<div id="introSwipe">
<section id="intro{{$index+1}}" class="introSlide" ng-class="{'active': $index === 0}" ng-repeat="slide in introSlides" style="background-image: url('{{slide}}')"></section>
</div>
<section id="noShow">
<div class="slideNavigation">
<button ng-click="prevSlide()" ng-class="{disabled: firstSlide}">{{::langPack.prevSlide[lang]}}</button>
<div class="slideProgress">{{slideProgress}}/{{introSlides.length}}</div>
<button ng-click="nextSlide()" ng-class="{disabled: lastSlide}">{{::langPack.nextSlide[lang]}}</button>
</div>
</section>
</div>
<a id="closeIntro" href="#" ng-click="toggleIntro(); $event.preventDefault()">{{::langPack.toggleIntroText[lang]}}</a>
</aside>
<section id="confirmOverlay" class="popupOverlay" ng-click="confirmValue = false;">
<section class="checkPopup confirmPopup" id="confirmPopup">
<p id="confirmMessage"></p>
<button class="btn right" ng-click="confirmValue = true;">{{::langPack.ok[lang]}}</button>
<button class="btn left" ng-click="confirmValue = false;">{{::langPack.cancel[lang]}}</button>
</section>
</section>
<section id="spinnerOverlay" class="popupOverlay">
<section class="checkPopup spinnerPopup" id="spinnerPopup">
<div class="circle"></div>
<div class="spinnerText" id="spinnerText"></div>
</section>
</section>
<section id="statusOverlay" class="popupOverlay" ng-click="status.hide()">
<section class="checkPopup statusPopup" id="statusPopup">
<p id="statusText"></p>
<button class="btn" ng-click="status.hide()">Ok</button>
</section>
</section>
<!-- JavaScript libraries -->
<script src="cordova.js"></script>
<script src="js/libs/angular.min.js"></script>
<script src="js/libs/angular-locale_da-dk.js"></script>
<script src="js/libs/angular-route.min.js"></script>
<script src="js/libs/angular-animate.min.js"></script>
<script src="js/libs/angular-touch.min.js"></script>
<script src="js/libs/snap.min.js"></script>
<script src="js/libs/md5.js"></script>
<script src="js/libs/angular-listview.min.js"></script>
<!-- Mapbox -->
<!--<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.js'></script>-->
<!-- <script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script> Draw GPX 20151014 -->
<!-- leaflet -->
<script src="js/libs/leaflet.js"></script>
<script src='js/libs/leaflet-omnivore.min.js'></script> <!-- Draw GPX 20151014 -->
<script src='js/libs/leaflet-tilelayer-cordova.js'></script> <!-- Offline maps -->
<!-- Custom script files -->
<script src="js/app.js"></script>
<script src="js/index.js"></script>
<!-- included controllers -->
<script src="controllers/globalCtrl.js"></script>
<script src="controllers/homeCtrl.js"></script>
<script src="controllers/aboutCtrl.js"></script>
<script src="controllers/tourSelectCtrl.js"></script>
<script src="controllers/mapCtrl.js"></script>
<script src="controllers/cameraCtrl.js"></script>
<script src="controllers/viewtoursCtrl.js"></script>
<script src="controllers/commonToursCtrl.js"></script>
<script src="controllers/highscoreCtrl.js"></script>
<script src="controllers/downloadCtrl.js"></script>
<script src="controllers/waypointCtrl.js"></script>
<script src="controllers/waypointGalleryCtrl.js"></script>
<script src="controllers/endCtrl.js"></script>
<script src="controllers/newsCtrl.js"></script>
</body>
</html>
AngularJS 加载了 app.js:
// Init angular.js
var guideApp = angular.module('guideApp', ['ngRoute', 'ngAnimate', 'ngTouch']);
guideApp.config(['$routeProvider', '$compileProvider', function ($routeProvider, $compileProvider) {
$routeProvider.when('/', {
controller: 'homeCtrl',
templateUrl: 'views/home.html'
}).when('/home', {
controller: 'homeCtrl',
templateUrl: 'views/home.html'
}).when('/tourSelect', {
controller: 'tourSelectCtrl',
templateUrl: 'views/tourSelect.html'
}).when('/map', {
controller: 'mapCtrl',
templateUrl: 'views/map.html'
}).when('/about', {
controller: 'aboutCtrl',
templateUrl: 'views/about.html'
}).when('/viewtours', {
controller: 'viewtoursCtrl',
templateUrl: 'views/viewtours.html'
}).when('/commonTours', {
controller: 'commonToursCtrl',
templateUrl: 'views/commonTours.html'
}).when('/highscore', {
controller: 'highscoreCtrl',
templateUrl: 'views/highscore.html'
}).when('/download', {
controller: 'downloadCtrl',
templateUrl: 'views/download.html'
}).when('/waypoint', {
controller: 'waypointCtrl',
templateUrl: 'views/waypoint.html'
}).when('/waypointGallery', {
controller: 'waypointGalleryCtrl',
templateUrl: 'views/waypointGallery.html'
}).when('/end', {
controller: 'endCtrl',
templateUrl: 'views/end.html'
}).when('/news', {
controller: 'newsCtrl',
templateUrl: 'views/news.html'
}).otherwise({
redirectTo: '/'
});
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|cdvfile|file):/);
}]);
guideApp.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
// custom on touchstart directive
/*guideApp.directive('guideappTouchstart', [function () {
return function(scope, element, attr) {
element.on('touchstart', function (event) {
scope.$apply(function () {
scope.$eval(attr.guideappTouchstart);
});
});
};
}]);*/
index.js 文件如下所示:
var http,
app = {
initialize: function () {
this.bindEvents();
},
bindEvents: function () {
if (!window.cordova) {
this.onDeviceReady();
} else {
document.addEventListener('deviceready', this.onDeviceReady, false);
}
},
onDeviceReady: function () {
app.receivedEvent('deviceready');
console.log('Device ready');
if (window.cordova) {
// find and set the filesystem root
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(1, 0, function (fileSystem) {
console.log("got filesystem: "+fileSystem.name); // displays "persistent"
//console.log(fileSystem.root.fullPath); // displays "/"
window.rootFS = fileSystem.root;
});
}
},
receivedEvent: function (id) {
//angularInit();
this.platform();
},
server: function (wwwroot) {
if (httpd) {
httpd.getURL(function (url) {
if(url.length > 0) {
document.getElementById('url').innerHTML = "server is up: <a href='" + url + "' target='_blank'>" + url + "</a>";
} else {
httpd.startServer({
'www_root' : wwwroot,
'port' : 8080
}, function (url) {
//updateStatus();
}, function (error) {
console.log('server error ', error);
});
}
}, function () {});
} else {
console.log('CorHttpd plugin not available/ready');
}
},
platform: function () {
var deviceType;
if (navigator.userAgent.match(/Android/i)) { // Android
if (navigator.userAgent.match(/Mobile/i)) { // Android smartphone
console.log('Android smartphone');
deviceType = 'mobile';
} else { // Android tablets
//console.log('Android tablet');
deviceType = 'tablet';
}
} else if (navigator.userAgent.match(/iPad/i)) { // IOS ipad
//console.log('IOS tablet');
deviceType = 'tablet';
} else if (navigator.userAgent.match(/iPhone/i)) { //IOS smartphone
//console.log('IOS smartphone');
deviceType = 'mobile';
} else { // Dekstop as default
//console.log('desktop');
deviceType = 'tablet';
}
}
};
app.initialize();