I have a single page web app written using AngularJS. It uses PouchDB to replicate to a CouchDB server and works fine.
我有一个使用AngularJS编写的单页Web应用程序。它使用PouchDB复制到CouchDB服务器并且工作正常。
The problem comes when I try to convert the webpage to be available offline by adding cache.manifest. Suddenly ALL the replication tasks throw errors and stop working, whether working offline or online.
当我尝试通过添加cache.manifest将网页转换为可脱机时,问题就来了。突然间,所有复制任务都会抛出错误并停止工作,无论是脱机还是在线工作。
In Chrome it just says "GET ...myCouchIP/myDB/?_nonce=CxVFIwnEJeGFcyoJ net::ERR_FAILED"
在Chrome中它只是说“GET ... myCouchIP / myDB /?_ nonce = CxVFIwnEJeGFcyoJ net :: ERR_FAILED”
In Firefox it also throws an error but mentions that the request is blocked - try enabling CORS.
在Firefox中它也会抛出错误,但提到请求被阻止 - 尝试启用CORS。
CORS is enabled on the remote CouchDB as per the instructions from PouchDB setup page. Plus it works fine while not using the cache.manifest (i.e. it is quite happy with all the different ip addresses between my desk, the server and a VM - it is a prototype so there are no domain names at this time).
根据PouchDB设置页面的说明,在远程CouchDB上启用CORS。另外,它没有使用cache.manifest工作正常(即它非常满意我的桌面,服务器和VM之间的所有不同的IP地址 - 它是一个原型,所以目前没有域名)。
Incidentally, at this time I am not using any kind of authentication. Admin party is in effect.
顺便说一句,此时我没有使用任何类型的身份验证。管理员方生效。
So what changes when adding the cache.manifest? Clues gratefully welcomed. Thanks in advance.
那么添加cache.manifest会有什么变化?线索感激不尽。提前致谢。
app.js
var app = angular.module('Assets', ['assets.controllers', 'ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', {
controller: 'OverviewCtrl',
templateUrl: 'views/overview.html'
}).
when('/new', {
controller: 'NewMachineCtrl',
templateUrl: 'views/machineForm.html'
}).
otherwise({redirectTo: '/'});
}]);
controller.js
var _control = angular.module('assets.controllers', ['assets.services']);
_control.controller('OverviewCtrl', ['$scope', 'Machine', function($scope, Machine) {
var promise = Machine.getAll();
promise.then(function(machineList) {
$scope.machines = machineList;
}, function(reason) {
alert('Machine list is empty: ' + reason);
});
}]);
_control.controller('UpdateMachineCtrl', ['$scope', '$routeParams', 'Machine',
function($scope, $routeParams, Machine) {
$scope.title = "Update Installation Details";
var promise = Machine.getSingle($routeParams.docId);
promise.then(function(machine) {
$scope.machine = machine;
}, function(reason) {
alert('Record could not be retrieved');
});
$scope.save = function() {
Machine.update($scope.machine);
};
}]);
_control.controller('SyncCtrl', ['$scope', 'Machine', function($scope, Machine) {
$scope.syncDb = function() {
Machine.sync();
Machine.checkConflicts();
};
$scope.checkCors = function() {
// Check CORS is supported
var corsCheck = function(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
console.log('CORS not supported by browser');
}
xhr.onload = function() {
console.log('Response from CORS ' + method + ' request to ' + url + ': ' + xhr.responseText);
};
xhr.onerror = function() {
console.log('Error response from CORS ' + method + ' request to ' + url + ': ' + xhr.responseText);
};
xhr.send();
};
var server = 'http://10.100.3.21:5984/ass_support';
corsCheck('GET', server);
corsCheck('PUT', server);
corsCheck('POST', server);
corsCheck('HEAD', server);
// corsCheck('DELETE', server);
};
}]);
service.js
var _service = angular.module('assets.services', []);
_service.constant('dbConfig',{
dbName: 'assets',
dbServer: 'http://myCouchServerIp:5984/'
});
/**
* Make PouchDB available in AngularJS.
*/
_service.factory('$db', ['dbConfig', function(dbConfig) {
PouchDB.enableAllDbs = true;
var localDb = new PouchDB(dbConfig.dbName);
var remoteDb = dbConfig.dbServer + dbConfig.dbName;
var options = {live: true};
var syncError = function() {
console.log('Problem encountered during database synchronisation');
};
console.log('Replicating from local to server');
localDb.replicate.to(remoteDb, options, syncError);
console.log('Replicating from server back to local');
localDb.replicate.from(remoteDb, options, syncError);
return localDb;
}]);
_service.factory('Machine', ['$q', '$db', '$rootScope', 'dbConfig',
function($q, $db, $rootScope, dbConfig) {
return {
update: function(machine) {
var delay = $q.defer();
var doc = {
_id: machine._id,
_rev: machine._rev,
type: machine.type,
customer: machine.customer,
factory: machine.factory,
lineId: machine.lineId,
plcVersion: machine.plcVersion,
dateCreated: machine.dateCreated,
lastUpdated: new Date().toUTCString()
};
$db.put(doc, function(error, response) {
$rootScope.$apply(function() {
if (error) {
console.log('Update failed: ');
console.log(error);
delay.reject(error);
} else {
console.log('Update succeeded: ');
console.log(response);
delay.resolve(response);
}
});
});
return delay.promise;
},
getAll: function() {
var delay = $q.defer();
var map = function(doc) {
if (doc.type === 'machine') {
emit([doc.customer, doc.factory],
{
_id: doc._id,
customer: doc.customer,
factory: doc.factory,
lineId: doc.lineId,
plcVersion: doc.plcVersion,
}
);
}
};
$db.query({map: map}, function(error, response) {
$rootScope.$apply(function() {
if (error) {
delay.reject(error);
} else {
console.log('Query retrieved ' + response.rows.length + ' rows');
var queryResults = [];
// Create an array from the response
response.rows.forEach(function(row) {
queryResults.push(row.value);
});
delay.resolve(queryResults);
}
});
});
return delay.promise;
},
sync: function() {
var remoteDb = dbConfig.dbServer + dbConfig.dbName;
var options = {live: true};
var syncError = function(error, changes) {
console.log('Problem encountered during database synchronisation');
console.log(error);
console.log(changes);
};
var syncSuccess = function(error, changes) {
console.log('Sync success');
console.log(error);
console.log(changes);
};
console.log('Replicating from local to server');
$db.replicate.to(remoteDb, options, syncError).
on('error', syncError).
on('complete', syncSuccess);
console.log('Replicating from server back to local');
$db.replicate.from(remoteDb, options, syncError);
}
};
}]);
_service.factory('dbListener', ['$rootScope', '$db', function($rootScope, $db) {
console.log('Registering a onChange listener');
$db.info(function(error, response) {
$db.changes({
since: response.update_seq,
live: true,
}).on('change', function() {
console.log('Change detected by the dbListener');
// TODO work out why this never happens
});
});
}]);
cache.manifest
CACHE MANIFEST
# views
views/machineForm.html
views/overview.html
# scripts
scripts/vendor/pouchdb-2.2.0.min.js
scripts/vendor/angular-1.2.16.min.js
scripts/vendor/angular-route-1.2.16.min.js
scripts/app.js
scripts/controllers/controller.js
scripts/services/service.js
index.html
<!DOCTYPE html>
<html lang="en" manifest="cache.manifest" data-ng-app="Assets">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Management</title>
<script src="scripts/vendor/angular-1.2.16.min.js" type="text/javascript"></script>
<script src="scripts/vendor/angular-route-1.2.16.min.js" type="text/javascript></script>
<script src="scripts/vendor/pouchdb-2.2.0.min.js" type="text/javascript"></script>
<script src="scripts/app.js" type="text/javascript"></script>
<script src="scripts/services/service.js" type="text/javascript"></script>
<script src="scripts/controllers/controller.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<nav class="sidebar">
<h3>Options</h3>
<div>
<a class="active" data-ng-href="#/">Overview</a>
<a data-ng-href="#" data-ng-controller="SyncCtrl" data-ng-click="syncDb()">Synchronise</a>
<a data-ng-href="" data-ng-controller="SyncCtrl" data-ng-click="checkCors()">Check CORS</a>
</div>
</nav>
<section class="main">
<div data-ng-view></div>
</section>
</div>
</body>
</html>
overview.html
<h3>Installation Overview</h3>
<table>
<tr>
<th>Customer</th>
<th>Factory</th>
<th>Line Id</th>
<th>PLC Version</th>
</tr>
<tr data-ng-repeat="machine in machines">
<td>{{machine.customer}}</td>
<td>{{machine.factory}}</td>
<td><a data-ng-href="#/view/{{machine._id}}">{{machine.lineId}}</a></td>
<td>{{machine.plcVersion}}</td>
</tr>
</table>
machineForm.html
<h3>{{title}}</h3>
<form name="machineForm" data-ng-submit="save()">
<div>
<label for="customer">Customer:</label>
<div><input data-ng-model="machine.customer" id="customer" required></div>
</div>
<div>
<label for="factory">Factory:</label>
<div><input data-ng-model="machine.factory" id="factory" required></div>
</div>
<div>
<label for="lineId">Line ID:</label>
<div><input data-ng-model="machine.lineId" id="lineId" required></div>
</div>
<div>
<label for="plcVersion">PLC Version:</label>
<div><input data-ng-model="machine.plcVersion" id="plcVersion"></div>
</div>
<div><button data-ng-disabled="machineForm.$invalid">Save</button></div>
</form>
1 个解决方案
#1
10
Try changing your cache.manifest file to this:
尝试将cache.manifest文件更改为:
CACHE MANIFEST
CACHE:
# views
views/machineForm.html
views/overview.html
# scripts
scripts/vendor/pouchdb-2.2.0.min.js
scripts/vendor/angular-1.2.16.min.js
scripts/vendor/angular-route-1.2.16.min.js
scripts/app.js
scripts/controllers/controller.js
scripts/services/service.js
NETWORK:
*
When using a manifest file, all non-cached resources will fail on a cached page, even when you're online. The NETWORK section tells the browser to allow requests to non-cached resources (they'll still fail while offline, of course).
使用清单文件时,即使您处于联机状态,所有非缓存资源也会在缓存页面上失败。 NETWORK部分告诉浏览器允许对非缓存资源的请求(当然,它们在离线时仍然会失败)。
#1
10
Try changing your cache.manifest file to this:
尝试将cache.manifest文件更改为:
CACHE MANIFEST
CACHE:
# views
views/machineForm.html
views/overview.html
# scripts
scripts/vendor/pouchdb-2.2.0.min.js
scripts/vendor/angular-1.2.16.min.js
scripts/vendor/angular-route-1.2.16.min.js
scripts/app.js
scripts/controllers/controller.js
scripts/services/service.js
NETWORK:
*
When using a manifest file, all non-cached resources will fail on a cached page, even when you're online. The NETWORK section tells the browser to allow requests to non-cached resources (they'll still fail while offline, of course).
使用清单文件时,即使您处于联机状态,所有非缓存资源也会在缓存页面上失败。 NETWORK部分告诉浏览器允许对非缓存资源的请求(当然,它们在离线时仍然会失败)。