AngularJS/PouchDB app stops syncing to CouchDB when cache.manifest added - angularjs

I have a single page web app written using AngularJS. It uses PouchDB to replicate to a CouchDB server and works fine.
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.
In Chrome it just says "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.
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).
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.
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>

Try changing your cache.manifest file to this:
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).

Related

Ionic/AngularJS & Wordpress API

I'm somewhat new to the JS world, so I'm struggling a bit as to what I did wrong. My sample data from wordpress API is not working. Any ideas what I did wrong:
app.controller('FeedCtrl', function($http, $scope, $ionicLoading) {
console.log("Loading FeedCtrl");
$scope.stories = [];
function loadStories(params, callback) {
$http.get('http://public-api.wordpress.com/rest/v1/freshly-pressed/', {params: params})
.success(function(response) {
var stories = [];
angular.forEach(response.data.children, function(child) {
stories.push(child.data);
});
callback(stories);
});
}
$scope.loadOlderStories = function() {
var params = {};
if ($scope.stories.length > 0) {
params['after'] = $scope.stories[$scope.stories.length - 1].name;
}
loadStories(params, function(olderStories) {
$scope.stories = $scope.stories.concat(olderStories);
$scope.$broadcast('scroll.infiniteScrollComplete');
});
};
$scope.loadNewerStories = function() {
var params = {'before': $scope.stories[0].name};
loadStories(params, function(newerStories) {
$scope.stories = newerStories.concat($scope.stories);
$scope.$broadcast('scroll.refreshComplete');
});
};
I've made a simplified example with your data.
Click the 'Load more' button to retrieve some posts. You should see a list with the title and the author of a post.
EDIT: There appears to be some cross-domain request issues, that's why the 'Load stories' button won't work. Just try to reflect this code inside your controller, it should work.
var app = angular.module('myApp', []);
app.controller('feedCtrl', function ($scope, $http) {
$scope.stories = [];
$scope.loadStories = function loadStories() {
console.log('loading stories');
$http.get('http://public-api.wordpress.com/rest/v1/freshly-pressed/')
.then(function onSuccess(response) {
console.log(response);
$scope.stories = response.data.posts;
}, function onFailed(error) {
console.error('Error:', error)
});
}
});
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="feedCtrl">
<button data-ng-click="loadStories()">Load stories</button>
<ul>
<li data-ng-repeat="story in stories">Title: {{ story.title }} - {{ story.author.first_name }} {{ story.author.last_name }}</li>
</ul>
</div>
</body>
</html>
Normally we wouldn't handle $http calls in our angular.controller. This needs to be done in an angular.service.

Can't send a POST request with $http in Angular - ReferenceError: $http is not defined

So I keep getting this ReferenceError: $http is not defined, even though I have included $http in the controller, which seems to be the most common cause of this error message. I've tried also passing $http into the function itself, but that doesn't solve it.
I feel like I am missing something SUPER obvious, so any help would be much appreciated, thank you!
I've included the entire script, just for clarity's sake. You can see the post request towards the end of the script, inside the finaliseDay function.
Thanks!
Here is the error:
ReferenceError: $http is not defined
at l.$scope.finaliseDay (http://localhost:8888/goalzy.js:69:12)
at ib.functionCall (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:198:303)
at Dc.(anonymous function).compile.d.on.f (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:214:485)
at l.$get.l.$eval (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:125:305)
at l.$get.l.$apply (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:126:6)
at HTMLAnchorElement.<anonymous> (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:215:36)
at HTMLAnchorElement.c (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js:32:389)angular.js:11607 (anonymous function)angular.js:8557 $getangular.js:14502 $get.l.$applyangular.js:21440 (anonymous function)angular.js:3014 c
Here is the HTML first
<!doctype html>
<html ng-app="goalzy">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js"></script>
<script src="goalzy.js"></script>
</head>
<body>
<div class="container">
<div class="well">
<h2>Goalzy</h2>
Dev TODO
<ul>
<li>Hook up the API to persist data</li>
</ul>
<div ng-controller="TodoController">
<span>{{remaining()}} of {{todos.length}} remaining today</span>
<span>You're at {{percentComplete()}}% completion</span>
[ finalise day ]
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form ng-submit="addTodo()">
<input type="text" ng-model="todoText" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
<hr>
<div class="historial" ng-repeat="h in historicalDailyPercentages">
<ul>
<li>Date: {{h.date}}</li>
<li>Percentage of Daily Tasks Completed: {{h.percent}}%</li>
<li><div>Tweet it!</div></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
And here is the JS:
//Goalzy.js
angular.module('goalzy', [])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8';
});
.controller('TodoController', ['$scope','$http', function($scope, $http) {
$scope.todos = [];
$scope.historicalDailyPercentages = [];
$scope.addTodo = function() {
if ($scope.todoText != "") {
if ($scope.todos.length < 3) {
$scope.todos.push({text:$scope.todoText, done:false});
$scope.todoText = '';
//Save to DB
}
else {
alert("You can only have 3 todos per day!");
$scope.todoText = '';
}
} else {
alert("you must write something");
}
};
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.percentComplete = function() {
var countCompleted = 0;
angular.forEach($scope.todos, function(todo) {
countCompleted += todo.done ? 1 : 0; //Simply calculates how many tasks have been completed
console.log(countCompleted);
});
var totalCount = $scope.todos.length;
var percentComplete = countCompleted / totalCount * 100;
return percentComplete;
}
$scope.finaliseDay = function(percentComplete) {
alert("You're finalising this day with a percentage of: " + percentComplete);
var today = new Date();
var alreadyPresent = $scope.historicalDailyPercentages.some(function (item) {
return item.date.getFullYear() === today.getFullYear() &&
item.date.getMonth() === today.getMonth() &&
item.date.getDate() === today.getDate();
});
//Confirm that nothing has alreayd been posted for today
if (!alreadyPresent) {
$scope.historicalDailyPercentages.push({
percent: percentComplete,
date: today
});
// Simple POST request example (passing data) :
$http.post('/api/postDailyPercentage.php', {msg:'hello word!'}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
console.log("data" + data);
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log("data" + data);
});
}
else {
alert("You're all set for today - see you tomorrow!");
}
console.log($scope.historicalDailyPercentages);
}
}]);
Provider won't be available inside controller with suffix 'Provider', you can do access them by provider name only here it would be $http only, also remove ;
after config initialization
$httpProvider setting should be done inside the angular config phase
CODE
angular.module('goalzy', [])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8';
}]);
.controller('TodoController', ['$scope', '$http', function($scope, $http) {
//controller code here
}]);
Note: For sure you should remove $httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8'; line from controller
Working Plunkr
You don't have to use "$httpProvider" in controller, use $http instead.
e.g.
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
As best practice, do not configure provider ($http) in controller. Do it in config section. as below
var app = angular.module('goalzy', []);
app.config(function ($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
});
app.controller('TodoController', ['$scope','$http', function($scope, $http) {
$scope.title="scope title";
//$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
}]);
see working plunk at http://run.plnkr.co/plunks/4mY4izqc48P8wVQFumZ8/ with your html.

Integrate facebook with cordova and angular (Ionic)

I'm trying to add facebook integration to my ionic mobile app that I'm building using cordova. I'm able to get it working either without cordovaa or angular but in no way with both. With the code below, everything goes fine till FB.init gets called after loading all.js . After that, no further code is executed inside _init function, and because of that I cannot subscribe to events or do anything else.
angular facebook directive (uses code from this gist : https://gist.github.com/ruiwen/4722499)
angular.module('facebook', [])
.directive('fb', ['$FB', function($FB) {
return {
restrict: "E",
replace: true,
template: "<div id='fb-root'></div>",
compile: function(tElem, tAttrs) {
return {
post: function(scope, iElem, iAttrs, controller) {
var fbAppId = iAttrs.appId || '';
var fb_params = {
appId: iAttrs.appId || "",
cookie: iAttrs.cookie || true,
status: iAttrs.status || true,
nativeInterface: CDV.FB,
useCachedDialogs: false,
xfbml: iAttrs.xfbml || true
};
// Setup the post-load callback
window.fbAsyncInit = function() {
$FB._init(fb_params);
if('fbInit' in iAttrs) {
iAttrs.fbInit();
}
};
(function(d, s, id, fbAppId) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk', fbAppId));
}
}
}
};
}])
.factory('$FB', ['$rootScope', function($rootScope) {
var fbLoaded = false;
// Our own customisations
var _fb = {
loaded: fbLoaded,
isLoaded : function(){
return this.loaded;
},
authenticated : false,
isAuthenticated : function(){
return this.authenticated;
},
statusUpdated: function(response){
if (response.status == 'connected') {
self.authenticated = true;
alert('logged in');
} else {
alert('not logged in');
}
},
_init: function(params) {
self = this;
if(window.FB) {
// FIXME: Ugly hack to maintain both window.FB
// and our AngularJS-wrapped $FB with our customisations
angular.extend(window.FB, this);
angular.extend(this, window.FB);
// Set the flag
this.loaded = true;
// Initialise FB SDK
FB.init(params);
//THIS CODE IS NOT CALLED
FB.Event.subscribe('auth.statusChange', function(response) {
alert('auth.statusChange event');
});
FB.Event.subscribe('auth.authStatusChange', self.statusUpdated)
if(!$rootScope.$$phase) {
$rootScope.$apply();
}
}
}
}
return _fb;
}]);
My controller :
angular.module('starter.controllers', [])
.controller('StartCtrl', [
'$scope',
'$FB',
'$location',
function($scope, $FB, $location) {
$scope.$watch(function() {
return $FB.isLoaded()
},function(value){
console.log("VALUE",value);
// It needs authentication, this won't work.
if(value){
$scope.facebook_friends = $FB.api('/me/friends', function(response) {
$scope.facebook_friends = response.data;
});
}
},true);
$scope.$watch(function() {
return $FB.isAuthenticated()
},function(value){
alert("VALUE isAuthenticated "+value);
// YEP, this will work.
if(value){
$scope.facebook_friends = $FB.api('/me', function(response) {
$scope.facebook_friends = response.data;
console.log("FRIENDS",response);
});
}
},true);
$scope.FBlogin = function() {
FB.login(null, {scope: 'email'});
};
}
])
index.html :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, user-scalable=no, width=device-width">
<title>Starter</title>
<link href="lib/css/ionic.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet">
<script src="lib/js/ionic.bundle.js"></script>
<script src="cordova.js"></script>
<script src="js/angular-fb.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
<!-- cordova facebook plugin -->
<script src="cdv-plugin-fb-connect.js"></script>
<!-- facebook js sdk -->
<script src="facebook-js-sdk.js"></script>
</head>
<body ng-app="starter" animation="slide-left-right-ios7">
<ion-nav-view></ion-nav-view>
<fb app-id='appid'></fb>
</body>
</html>
and app.js
angular.module('starter', ['ionic', 'starter.services', 'starter.controllers', 'facebook'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('start', {
url: "/start",
templateUrl: "templates/start.html",
controller: 'StartCtrl'
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/start');
});
You should not doing this <script src="facebook-js-sdk.js"></script> actually since it against TOS. Even-though you want to make sure the sdk is loading.
Instead you can try this code below (to make sure you can install cordova-inappbrowser first)
<script>
function statusChangeCallback(response) {
console.log('statusChangeCallback');
console.log(response);
if (response.status === 'connected') {
// Logged into your app and Facebook.
testAPI();
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not your app.
document.getElementById('status').innerHTML = 'Please log ' +
'into this app.';
} else {
document.getElementById('status').innerHTML = 'Please log ' +
'into Facebook.';
}
}
function checkLoginState() {
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
}
window.fbAsyncInit = function() {
FB.init({
appId : '{your-app-id}',
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.5' // use graph api version 2.5
});
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
function testAPI() {
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Successful login for: ' + response.name);
document.getElementById('status').innerHTML =
'Thanks for logging in, ' + response.name + '!';
});
}
</script>
<!--
Below we include the Login Button social plugin. This button uses
the JavaScript SDK to present a graphical Login button that triggers
the FB.login() function when clicked.
-->
<fb:login-button scope="public_profile,email" onlogin="checkLoginState();">
</fb:login-button>
<div id="status">
</div>
To deeper insight
1 first add this plugin
cordova plugin add cordova-plugin-inappbrowser
2 After that create app id follow these steps
https://developers.facebook.com/docs/apps/register
3 After that add this code where you need facebook integration`
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
$cordovaOauth.facebook("447073565475718", ["email", "public_profile"]).then(function(result) {
displayData($http, result.access_token);
var name = result.data.name;
var gender = result.data.gender;
var location = result.data.location;
var picture = result.data.picture;
}, function(error) {
console.log(error);
});
};
function displayData($http, access_token)
{
$http.get("https://graph.facebook.com/v2.2/me", {params: {access_token: access_token, fields: "name,gender,location,picture,email", format: "json" }}).then(function(result)
{
console.log(JSON.stringify(result));
var name = result.data.name;
var gender = result.data.gender;
var location = result.data.location;
var picture = result.data.picture;
var id =result.data.id;
var userid=id;
}, function(error) {
alert("There was a problem getting your profile. Check the logs for details.");
console.log(error);
});
`
une ngcordova, it gives you simple AngularJS wrappers for a massive amount of Cordova plugins, it include a wrapper for facebookConnectPlugin
The phonegap-facebook-plugin version 0.8.1 has more to do than a simple installation via cordova plugin add. phonegap-native-fb-ionic mentions the required one for ionic step by step.
You should integrate with Facebook through the facebookConnectPlugin of cordova.
You will find a deep explanation of how to use it in this link: https://github.com/Wizcorp/phonegap-facebook-plugin.
The plugin gives you everything you need and short examples. But you have to make sure the following things will be done:
Read carefully the instructions of installing the plugin. I choosed the manual way to install the plugin for Android through the cordova CLI.
Remember that you can use all phonegap plugins only after the deviceReady event, so you have to make sure you are calling the api of the facebookConnectPlugin after the device is ready of course.
Good luck!

AngularJS - Templates not working

I am using Restangular to create a simple API using MEAN stack.
Here is my code:
index.html
<!DOCTYPE html>
<html data-ng-app="scotchTodo">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
<title>Node/Angular Todo App</title>
<!-- SCROLLS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"><!-- load bootstrap -->
<style>
html{
overflow-y:scroll;
}
body{
padding-top:50px;
}
</style>
<!-- SPELLS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular-route.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/restangular/1.4.0/restangular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div data-ng-view>
</div>
</body>
</html>
app.js
var scotchTodo = angular.module('scotchTodo', ['restangular','ngRoute']);
//config
scotchTodo.config(['$routeProvider','$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('/',{
templateUrl: 'list.html',
controller: 'ListController'
})
.when('api/todos/:todo_id',{
templateUrl: 'edit.html',
controller: 'EditController'
});
$locationProvider.html5Mode(true);
}]);
//controllers
scotchTodo.controller('ListController', ['$scope', 'Restangular',
function($scope, Restangular) {
//GET ALL
var baseTodo = Restangular.all('api/todos');
baseTodo.getList().then(function(todos) {
$scope.todos = todos;
});
//POST -> Save new
$scope.save = function() {
var baseTodo = Restangular.all('api/todos');
var newTodo = {'text': $scope.text};
baseTodo.post(newTodo).then(function(todos) {
$scope.todos = todos;
$scope.text = '';
});
};
//DELETE
$scope.delete = function(id) {
var baseTodo = Restangular.one('api/todos', id);
baseTodo.remove().then(function(todos) {
$scope.todos = todos;
});
};
}]);
scotchTodo.controller('EditController', ['$scope', 'Restangular','$routeParams',
function($scope, Restangular, $routeParams) {
var baseTodo = Restangular.one('api/todos', id);
baseTodo.getList().then(function(todo) {
$scope.todo = todo[0];
window.test = "dev";
});
//PUT -> Edit
$scope.update = function(id){
var baseTodo = Restangular.one('api/todos', id);
baseTodo.text = "Edited";
baseTodo.put().then(function(todos) {
$scope.todos = todos;
});
};
}]);
list.html
<div>
<div data-ng-repeat="todo in todos">
{{todo.text}}Edit<button data-ng-click="delete(todo._id)">X</button>
</div>
<input type="text" data-ng-model="text"/>
<button data-ng-click="save()">Add</button>
</div>
edit.html
<div>
<input type="text" data-ng-model="text" value="{{todo.text}}" />
<button data-ng-click="update(todo._id)">Save</button>
</div>
server.js
// setup ========================
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
//configuration =================
mongoose.connect('mongodb://127.0.0.1:27017/sl', function(err, db) {
if (!err) {
console.log("We are connected to " + db);
}
});
app.use(express.static(__dirname + '/public'));
app.use(bodyParser());
// application -------------------------------------------------------------
app.get('/', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
//listen ========================
app.listen(8080);
console.log('App started on the port 8080');
//define model ==================
var Todo = mongoose.model('Todo', {
text: String
});
// routes ======================================================================
// api ---------------------------------------------------------------------
//get one todo
app.get('/api/todos/:todo_id', function(req, res) {
// use mongoose to get all todos in the database
Todo.find({
_id: req.params.todo_id
},function(err, todos) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err){
res.send(err);
}
res.json(todos); // return all todos in JSON format
});
});
// get all todos
app.get('/api/todos', function(req, res) {
// use mongoose to get all todos in the database
Todo.find(function(err, todos) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err){
res.send(err);
}
res.json(todos); // return all todos in JSON format
});
});
// create todo and send back all todos after creation
app.post('/api/todos', function(req, res) {
// create a todo, information comes from AJAX request from Angular
Todo.create({
text: req.body.text,
done: false
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err);
res.json(todos);
});
});
});
// update todo and send back all todos after creation
app.put('/api/todos/:todo_id', function(req, res) {
// create a todo, information comes from AJAX request from Angular
Todo.update({
_id: req.params.todo_id
}, {
text:req.body.text
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err);
res.json(todos);
});
});
});
// delete a todo
app.delete('/api/todos/:todo_id', function(req, res) {
Todo.remove({
_id: req.params.todo_id
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err){
res.send(err);
}
res.json(todos);
});
});
});
The first page of my app loads perfectly fine. Here is the screenshot.
But when I click on either of the edit link it is supposed to load edit.html template. But it shows a blank page with no errors in console. Here is the screenshot.
I am unable to figure out what's wrong. Please help. Please ask if any other piece of code is needed. I added almost everything that I did. I know it is annoying and not recommended but I am not sure what part of my code is causing this issue.
EDIT 1:
My farthest guess is that the url for edit.html might not be getting resolved correctly. But I am not sure how to test that! Any help will be appriciated.
EDIT 2: Directory structure
SOLUTION : Courtesy #ashu
The issue was this line in index.html
<script src="app.js"></script>
It should be:
<script src="/app.js"></script>
However, I am not clear why! Page was including app.js either way. It is weird.
You have same routes for angular and express.
.when('api/todos/:todo_id',{
templateUrl: 'edit.html',
controller: 'EditController'
});
and in express
app.get('/api/todos/:todo_id', function(req, res) {
Hence, there is ambiguity. You can remove the 'api' part from angular urls.
.when('/todos/:todo_id', {
templateUrl: 'edit.html',
controller: 'EditController'
})
And in server, you can add a catchall route which will handle all the non-api urls. For doing that you can move your app.get('/', function(req,res) {..}) call at the bottom after defining your api routes.
// < Define APi Routes here >
//catch all route for serving the html template
app.get('/*', function(req, res ) {
res.sendfile('./public/index.html')
});
Also, in your app.js EditController, you forgot to initialise value of id.
var id = $routeParams.todo_id;

Phonegap.js on second html page

I basically have two pages in my phonegap application that I am building with PGB (index.html and main.html), that both use angular.js. Index.html is a login for the app, which redirects to main.html afterwards. All my plugins and phonegap.js are being injected fine into main, but none of the inline JS (alerts on doc ready, device ready, window load) are firing, let alone phonegap.js being loaded as well.
Any advice would be appreciated.
Script Includes:
<script src="phonegap.js"></script>
<script src="cdv-plugin-fb-connect.js"></script>
<script src="facebook-js-sdk.js"></script> <script>alert("inside pg");</script>
<script src="childbrowser.js"></script>
<script src="js/jquery.js"></script>
<script src="js/angular.min.js"></script>
<script>alert("here");</script>
<script src="js/controllers.js"></script>
<script src="js/klass.min.js"></script>
<script src="js/code.photoswipe.jquery-3.0.5.min.js"></script>
<script src="js/maskedInput.js" type="text/javascript"></script>
<script src="js/jquery.joyride.js"></script>
<script src="js/jquery.fancybox.pack.js"></script>
<script src="http://connect.facebook.net/en_US/all.js" type="text/javascript"></script>
Scripts:
alert("p2 adding")
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is loaded and it is now safe to make calls PhoneGap methods
//
function onDeviceReady() {
alert("main.html: device is ready");
}
$(window).load(function(){
alert("window.load happening");
})
</script>
<script>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-42023187-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-42023187-1', 'openvino.com');
ga('send', 'pageview');
</script>
<script type="text/javascript">
var objectToLike = window.location;
var FBactivated = false;
FB.init({
appId : '659381964079214', // App ID
channelURL : '', // Channel File, not required so leave empty
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
oauth : true,
xfbml : true // parse XFBML
});
FB.Event.subscribe('auth.authResponseChange', function(response) {
// Here we specify what we do with the response anytime this event occurs.
if (response.status === 'connected') {
getFriends();
testAPI();
FBactivated = true;
}
});
function getFriends() {
var fbUserIDs = []
FB.api('/me/friends', function(response) {
if(response.data) {
$.each(response.data,function(index,friend) {
var id = friend.id;
fbUserIDs.push(id);
});
var dataString = "fbUserIDs="+fbUserIDs.join();
$.ajax({
type: "POST",
data: dataString,
async: false,
url: "http://m.openvino.com/Scripts/faveMatch.php"
}).done(function(data){
console.log(data);
window.localStorage.setItem("fbFriends", data);
console.log("Saved");
});
} else {
alert("Error!");
}
});
}
function testAPI() {
FB.api('/me', function(response) {
//console.log(response, response.email);
var dataString2 = "id=" + response.id;
dataString2 += "&first_name=" + response.first_name;
dataString2 += "&last_name=" + response.last_name;
dataString2 += "&email=" + response.email;
console.log(dataString2);
$.ajax({
type: "POST",
url: "http://m.openvino.com/Scripts/fbconnect.php",
data: dataString2
}).done(function(data){
var dataJSON = $.parseJSON(data);
if (dataJSON[0].STATUS == "FAILURE") {
//console.log(dataJSON[0].MESSAGE);
return false;
} else if (dataJSON[0].STATUS == "SUCCESS") {
window.localStorage.setItem('email',dataJSON[0].COOKIE.email);
window.localStorage.setItem('password',dataJSON[0].COOKIE.password);
window.localStorage.setItem('name_first',dataJSON[0].COOKIE.name_first);
window.localStorage.setItem('name_last',dataJSON[0].COOKIE.name_last);
window.localStorage.setItem('uID',dataJSON[0].COOKIE.uID);
window.localStorage.setItem('phone',dataJSON[0].COOKIE.phone);
window.localStorage.setItem('firstTime',dataJSON[0].COOKIE.firstTime);
}
});
});
}
function fbLogout() {
if (FBactivated) {
try {
FB.logout(function(response) {
window.location.href = "index.html";
});
} catch (err) {
window.location.href = "index.html";
}
} else {
window.location.href = "index.html";
}
}
$(document).ready(function() {
alert("document.ready loaded");
$("#logmeout").click(function(e){
e.preventDefault();
window.localStorage.clear();
fbLogout();
return false;
});
$('.back_btn').click(function(e) {
$('.profile_menu').hide();
history.back();
});
$(document).click(function(e) {
$('.profile_menu').hide();
})
$('.profile_btn').click(function(e) {
$('.profile_menu').slideToggle();
e.stopPropagation();
e.preventDefault();
return false;
});
$('.profile_menu a').each(function() {
$(this).click(function(e) {
$('.profile_menu').hide();
});
});
});
HTML:
<body ng-app="OpenVino">
<div id="fb-root"></div>
<div class="header-wrap">
<header>
<div ng-show="(page != 'list')" class="back_btn"></div>
<img src="imgs/logo_only.png" alt="OpenVino" />
<div class="profile_btn"></div>
</header>
</div>
<div class="profile_menu">
My Favorites
Contact OpenVino
Images
Logout
</div>
<div class="content {{page}}" ng-view></div>
I fixed it with a simple, but disheartening solution: You have to turn your multipage app into a one page app. Unfortunate how phonegap advertises that you can take your HTML, CSS, and JS and build it natively. All of the .js loaded on the second page wouldnt work until I changed my login to a partial and fooled around with the routing.

Resources