MSAL JS Directory Chooser (Switch Directory) Component for SPA - azure-active-directory

Does Microsoft have (or is there any custom JS component that someone knows of) a directory chooser to provide "Switch Directory" functionality for an AAD secured SPA (currently using
MSAL JS https://github.com/AzureAD/microsoft-authentication-library-for-js)?

If you want to switch tenants in your SPA by msal.js, seems there is no official demo about it. Per my understanding, if you want to do so, there will be 2 things that you should solve:
Your app should be able to get all tenants that the current account belongs to.
The public client Azure AD App should be a multi-tenant one so that users could be able to log in to different tenants seamlessly.
For point 1, we can use this API to get all user tenants. This API belongs to Azure management rest API, so your public client Azure AD App should be granted with permission below so that users logged in could call this API to get all tenants:
I write a simple demo by msal.js for you that could meet your requirement I think, just try the HTML page code below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Azure AD test</title>
<script type="text/javascript" src="https://alcdn.msauth.net/lib/1.4.4/js/msal.min.js"></script>
</head>
<body>
<div >
<button id="SignIn" onclick="signIn()">Sign in</button><br/>
<div id="WelcomeMessage"/><br/>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
var clientAppID = "<multi tenant public client Azure ad app id>"
var tenantID = "<tenant ID for default login >"
var demoScops = {
scopes:["https://management.azure.com/user_impersonation"]
}
var msalConfig = {
auth: {
clientId: clientAppID,
authority: "https://login.microsoftonline.com/" + tenantID
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function signIn() {
myMSALObj.loginPopup(demoScops).then(function (loginResponse) {
console.log(loginResponse);
initPage();
}).catch(function (error) {
console.log(error);
});
}
function initPage(){
showWelcomeMessage();
getAllTenants();
}
function showWelcomeMessage() {
var divWelcome = document.getElementById('WelcomeMessage');
divWelcome.innerHTML = 'welcome! ' + myMSALObj.account.userName + '</br>';
var loginbutton = document.getElementById('SignIn');
loginbutton.innerHTML = 'sign out';
loginbutton.setAttribute('onclick', 'signOut();');
}
function getAllTenants(){
myMSALObj.acquireTokenSilent(demoScops).then(function (tokenResponse) {
var accessToken = tokenResponse.accessToken;
$.ajax({
url: "https://management.azure.com/tenants?api-version=2020-01-01",
type: "GET",
async: false,
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Bearer '+ accessToken);},
success: function(data) {
var divWelcome = document.getElementById('WelcomeMessage');
divWelcome.innerHTML += " your current tenant: "+ myMSALObj.account.idToken.tid +", all your tenants :</br>"
data.value.forEach(item=>{
var tentantItem = "<div id='"+item.tenantId+"' style='border: 2px solid grey; margin:5px; width:500px' onclick='switchTenant(this)' > name :"+item.displayName+ " Tenant ID:"+ item.tenantId +"</div>"
divWelcome.innerHTML += tentantItem;
})
}
});
}).catch(function (error) {
console.log(error);
})
}
function switchTenant(obj){
var msalConfig = {
auth: {
clientId: clientAppID,
authority: "https://login.microsoftonline.com/" + $(obj).attr('id')
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
myMSALObj.loginPopup(demoScops).then(function (loginResponse) {
console.log(loginResponse);
location.reload();
}).catch(function (error) {
console.log(error);
});
}
function authRedirectCallBack(error, response) {
if (error) {
console.log(error);
}
}
function requiresInteraction(errorCode) {
if (!errorCode || !errorCode.length) {
return false;
}
return errorCode === "consent_required" ||
errorCode === "interaction_required" ||
errorCode === "login_required";
}
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
var msie11 = ua.indexOf('Trident/');
var msedge = ua.indexOf('Edge/');
var isIE = msie > 0 || msie11 > 0;
var isEdge = msedge > 0;
var loginType = isIE ? "REDIRECT" : "POPUP";
if (loginType === 'POPUP') {
if (myMSALObj.getAccount()) {
initPage()
}
}
else if (loginType === 'REDIRECT') {
document.getElementById("SignIn").onclick = function () {
myMSALObj.loginRedirect(requestObj);
};
if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {
initPage()
}
} else {
console.error('Please set a valid login type');
}
function signOut() {
window.localStorage.clear();
myMSALObj.logout();
}
</script>
</html>
Result:
Once I login , it will display my current tenant id and all my tenants:
Once I click a tenant item, a login window prompts out and relod the page after successful login:
finally:

Related

Angular JS Templating

i have a template set for all my view pages which i load using
app.route('/*').get(core.renderIndex);
where and the renderIndex function looks like
exports.renderIndex = function (req, res) {
res.render('modules/core/server/views/index', {
user: req.user || null
});
};
Now when my route has announcement in it i render a different template and not index.server.view.html
app.route('/:shopId/:locationId/announcement/*').get(core.renderAnnouncement);
exports.renderAnnouncement = function (req, res) {
res.render('modules/core/server/views/announcement', {
user: req.user || null,
});
};
The reason why i'am doing this is because i need to change the meta tags over this page i.e. i need to set variables on view before the page renders which comes from another controller.
My question is how can i access variables in a server.view.html file?
<!DOCTYPE html>
<html lang="en" ng-controller="OfferController" ng-init="getAnnouncement()">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<base href="/">
<title>{{ gettitle }}</title>
like the gettitle which is setting from getAnnouncement method of OfferController.
This works if i set a different announcement.client.view.html file and in the server file extend it
In case someone is stuck and needs help here is how i did this
app.get('/:shopId/:locationId/announcement/*',function(req,res,next){
async.waterfall([
function (done) {
var resultsObj = '';
var httpTransport = 'http://';
if (config.secure && config.secure.ssl === true) {
httpTransport = 'https://';
}
var url = httpTransport + req.headers.host+'/api/offer/getbyid/'+req.params[0];
request.get(url, function (err, res, body) {
resultsObj = JSON.parse(body);
done(err, resultsObj);
});
},
function (resultsObj, done) {
var httpTransport = 'http://';
if (config.secure && config.secure.ssl === true) {
httpTransport = 'https://';
}
var url = httpTransport + req.headers.host+'/api/shops/'+req.params.shopId+'/'+req.params.locationId;
request.get(url, function (err, res, body) {
var resultsObjNew='';
resultsObjNew = JSON.parse(body);
done(err, resultsObjNew,resultsObj);
});
},
function (resultsObjNew,resultsObj, done) {
res.render('modules/core/server/views/announcement', {
title: resultsObj[0].title,
imageUrl : resultsObj[0].imageURL,
desc : resultsObj[0].desc,
link:resultsObj[0].redirectLink,
logoLink: resultsObjNew.logoLink,
backgroundImage:resultsObjNew.backgroundImage
});
}
]);
});
where
var request = require('request');
var async = require('async');
var path = require('path');

Common service for sessionstorage in angularjs

Hi in my application i am setting the values in login controller and getting in all the other js files, other than this how to use a common service for setting storage and getting that storage in required js files
My login controller
app.controller('LoginController',function(loginService, $rootScope,$scope, $http,$location) {
$scope.login = function () {
$scope.log=loginService.getLogin( $scope.emailId , $scope.password).
then(function (response) {
console.log($scope.log);
console.log(response)
if (response.data.LoginVerificationResult.length === 0) {
alert('details are not Available for this emailId');
$scope.error=true;
} else {
$rootScope.name=response.data.LoginVerificationResult[0].UserName;
$scope.abc=response.data.LoginVerificationResult[0].UserType
console.log($scope.abc+"from.......");
sessionStorage.setItem("EmaiId",$scope.emailId);
sessionStorage.setItem("User Id",response.data.LoginVerificationResult[0].UserID);
sessionStorage.setItem("UserName",response.data.LoginVerificationResult[0].UserName);
sessionStorage.setItem("UserType",response.data.LoginVerificationResult[0].UserType);
$scope.UserType = sessionStorage.getItem("UserType");
console.log($scope.UserType +"from login controller")
$location.path('/dashboard')
}
});
};
});
My changepassword file
app.controller("ChangePwdController", function($scope, $http, $location,
BaseUrl, changePwdService) {
//$scope.roleId = sessionStorage.getItem("Role ID");
/* $scope.UserType = sessionStorage.getItem("UserType");*/
$scope.username = sessionStorage.getItem("UserName");
$scope.userType = sessionStorage.getItem("UserType");
$scope.EmpName=sessionStorage.getItem("EmpName");
$scope.patientName=sessionStorage.getItem("PatientName")
$scope.changePwd = function() {
$scope.emailAddress = sessionStorage.getItem("EmaiId");
console.log($scope.emailAddress)
var data = {
'emailAddress' : $scope.emailAddress,
'currentPassword' : $scope.opassword,
'newPassword' : $scope.npassword
};
console.log("Hi")
$scope.pwd=changePwdService.postChangePwd(data).success(
function(resp) {
$scope.PostDataResponse = data;
console.log($scope.pwd)
console.log($scope.PostDataResponse);
if (resp.ResetPasswordResult === true) {
alert("Successfully changed");
console.log("success")
$location.path('/dashboard');
} else {
console.log("fail")
alert("Enter valid current password")
}
})
}
})
Is there any alternative way to set and get in one file
There are ways in which you can achieve the same. Please refer this here.

AngularJS show 2 models in one controller

I have 2 models(Project and Task) to show in my home.html, and I want my them to be displayed like this: http://plnkr.co/edit/ItNvBNBIrLxqwRrhye5p, where both scope of data is shown and filtered based on the same color.
I use an Angular controller (projectCtrl.js) to control data on my web page(home.html), and use an Angular service (projectService.js) to grab data from my api file (api.js) written with express framework and mongoose.
But my code doesn't show anything, so I have no idea what's wrong.
home.html:
<div class="row" ng-if="main.loggedIn">
<div ng-controller="ProjectController">
<div class="panel col-md-8">
<!-- Project heading setup -->
<div class="panel-group" ng-repeat="eachProject in project.projects | reverse track by $index">
<div class="panel panel-info">
<div class="panel-heading" data-toggle="collapse" ng-click="true" data-target="#projectDetails{{$index}}" href="#projectDetails{{$index}}">
<h4>{{eachProject.title}}: {{eachProject.short_description}}</h4>
</div>
<div class="panel-collapse collapse out" id="projectDetails{{$index}}">
<p class="panel-body">
<!-- Project detail table, where project data displays-->
<table class="table table-responsive table-bordered table-hover">
<tr>
<th>Description: </th>
<td>{{eachProject.description}}</td>
</tr>
</table>
<!-- Task heading setup -->
<div class="panel-group" ng-repeat="eachTask in task.tasks | filter: { projectID: eachProject.id }">
<div class="panel panel-success">
<div class="panel-heading" data-toggle="collapse" ng-click="true" data-target="#taskDetails{{$index}}" href="#taskDetails{{$index}}">
<h5>{{eachTask.title}}</h5>
</div>
<div class="panel-collapse collapse out" id="taskDetails{{$index}}">
<p class="panel-body">
<!-- Task detail table, where tasks data displays -->
<table class="table table-responsive table-bordered table-hover">
<tr>
<th>Description: </th>
<td>{{eachTask.description}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
ProjectCtrl.js
angular.module('projectCtrl', ['projectService'])
.controller('ProjectController', function(Project, Task, socketio) {
var vm = this;
Project.all()
.success(function(data) {
vm.projects = data;
})
vm.createProject = function() {
// Wrong due date prevention
var start = new Date(vm.projectData.start_date);
var due = new Date(vm.projectData.due_date);
if (start > due) {
alert("Due date can't be earlier than start date, please decide a new due date.");
return;
}
// Create project
vm.message = '';
Project.create(vm.projectData)
.success(function(data) {
// Clear up the project
vm.projectData = '';
vm.message = data.message;
$('#createProject').modal('hide');
})
}
Task.all()
.success(function(data) {
vm.tasks = data;
})
vm.createTask = function() {
// Wrong due date prevention
var start = new Date(vm.taskData.taskStart_date);
var due = new Date(vm.taskData.taskDue_date);
if (start > due) {
alert("Due date can't be earlier than start date, please decide a new due date.");
return;
}
// Create task
vm.message = '';
Task.create(vm.taskData)
.success(function(data) {
// Clear up the task
vm.taskData = '';
vm.message = data.message;
$('#createTask').modal('hide');
})
}
socketio.on('project', 'task', function(data) {
vm.projects.push(data);
vm.tasks.push(data);
})
})
.controller('AllProjectsController', function(projects, socketio) {
var vm = this;
vm.projects = projects.data;
socketio.on('project', function(data) {
vm.projects.push(data);
})
})
projectService.js
angular.module('projectService', [])
.factory('Project', function($http) {
var projectFactory = {};
projectFactory.create = function(projectData) {
return $http.post('/api', projectData);
}
projectFactory.allProjects = function() {
return $http.get('/api/all_projects');
}
projectFactory.all = function() {
return $http.get('/api');
}
projectFactory.deleteProject = function(id) {
return $http.post('/api/deleteProject', {id: id});
}
return projectFactory;
})
.factory('Task', function($http) {
var taskFactory = {};
taskFactory.create = function(taskData) {
return $http.post('/api', taskData);
}
taskFactory.allTasks = function() {
return $http.get('/api/all_tasks');
}
taskFactory.all = function() {
return $http.get('/api');
}
taskFactory.deleteTask = function(id) {
return $http.post('/api/deleteTask', {projectID: id});
}
return taskFactory;
})
.factory('socketio', function($rootScope) {
var socket = io.connect();
return {
on: function(eventName, callback) {
socket.on(eventName, function() {
var args = arguments;
$rootScope.$apply(function() {
callback.apply(socket, args);
})
})
},
emit: function(eventName, data, callback) {
socket.emit(eventName, data, function() {
var args = arguments;
$rootScope.apply(function() {
if(callback) {
callback.apply(socket, args);
}
})
})
}
}
})
api.js
var User = require('../models/user');
var Project = require('../models/project');
var config = require('../../config');
var secretKey = config.secretKey;
var jsonwebtoken = require('jsonwebtoken');
var fields = '...'; // a lot of fields, deleted them just to make it short
// Create tokens for users with jsonwebtoken
function createToken(user) {
var token = jsonwebtoken.sign({
id: user._id,
firstname: user.firstname,
lastname: user.lastname,
username: user.username
}, secretKey, {
expirtesInMinute: 1440
});
return token;
}
module.exports = function(app, express, io) {
var api = express.Router();
api.get('/all_projects', function(req, res) {
Project.find({}, function(err, projects) {
if (err) {
res.send(err);
return;
}
res.json(projects);
})
})
// login api
api.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}).select(fields).exec(function(err, user) {
if(err) {
throw err;
}
if (!user) {
res.send({ message: "User doesn't exist"});
} else if(user){
var validPassword = user.comparePassword(req.body.password);
if (!validPassword) {
res.send({ message: "Invalid Password"});
} else {
var token = createToken(user);
res.json({
success: true,
message: "Login Successfully !",
token: token
});
}
}
});
});
//middleware
api.use(function(req, res, next) {
console.log("Somebody just logged in!");
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
if (token) {
jsonwebtoken.verify(token, secretKey, function(err, decoded) {
if (err) {
res.status(403).send({success: false, message: "Failed to authenticate user."});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).send({ success: false, message: "No Token Provided." });
}
});
//api for projects handling
api.route('/')
.post(function(req, res) {
var project = new Project({
creatorID: req.decoded.id,
creator: req.decoded.firstname + " " + req.decoded.lastname,
creator_dept: req.decoded.department,
title: req.body.title,
short_description: req.body.short_description,
description: req.body.description,
priority: req.body.priority,
status: calcStatus(),
assign_dept: req.body.assign_dept,
estimate_cost: req.body.estimate_cost,
actual_cost: req.body.actual_cost,
last_modified_date: req.body.last_modified_date,
due_date: req.body.due_date,
start_date: req.body.start_date,
complete_date: req.body.complete_date,
});
project.save(function(err, newProject) {
if (err) {
res.send(err);
return;
}
io.emit('project', newProject);
res.json({
message: "New Project Created!"
});
});
})
.get(function(req, res) {
Project.find( {creatorID: req.decoded.id}, function(err, project) {
if (err) {
res.send(err);
return;
}
res.json(project);
});
});
//api for tasks handling
api.route('/')
.post(function(req, res) {
var task = new Task({
creatorID: req.decoded.id,
creator: req.decoded.firstname + " " + req.decoded.lastname,
projectID: req.body.taskProjectID,
title: req.body.taskTitle,
description: req.body.taskDescription,
status: calcStatus(),
assigneeName: req.body.assigneeName,
assigneeID: req.body.assigneeID,
assignee_dept: req.body.assignee_dept,
estimate_cost: req.body.taskEstimate_cost,
actual_cost: req.body.TaskActual_cost,
last_modified_date: req.body.taskLast_modified_date,
due_date: req.body.taskDue_date,
start_date: req.body.taskStart_date,
complete_date: req.body.taskComplete_date,
});
task.save(function(err, newTask) {
if (err) {
res.send(err);
return;
}
io.emit('tasks', newTask);
res.json({
message: "New Task Created!"
});
});
})
.get(function(req, res) {
Task.find( {projectID: req.decoded.id}, function(err, task) {
if (err) {
res.send(err);
return;
}
res.json(task);
});
});
// api for angular
api.get('/me', function(req, res) {
res.json(req.decoded);
});
return api;
}
Thanks for the help.
You reference project.projects here:
<div class="panel-group" ng-repeat="eachProject in project.projects | reverse track by $index">
But project is never in ProjectController... I think you may want to set project to refer to your ProjectController, try this:
<div ng-controller="ProjectController as project">
EDIT
A few other problems. You mentioned you wanted to show all Tasks as well, in ProjectController you populate projects and tasks like so:
Project.all()
.success(function(data) {
vm.projects = data;
})
....
Task.all()
.success(function(data) {
vm.tasks = data;
})
That's good, except the definitions for Project.all and Task.all are referencing the same data source:
projectFactory.all = function() {
return $http.get('/api');
}
taskFactory.all = function() {
return $http.get('/api');
}
They both reference /api. How can they both expect different data from the same route? They should likely be two distinct routes.
In addition, your definition in api.js for this end point requires an ID to be passed to get a single project:
.get(function(req, res) {
Task.find( {projectID: req.decoded.id}, function(err, task) {
if (err) {
res.send(err);
return;
}
res.json(task);
});
});
.get(function(req, res) {
// The request must contain a creatorId, otherwise you're not going to find your project
Project.find( {creatorID: req.decoded.id}, function(err, project) {
if (err) {
res.send(err);
return;
}
res.json(project);
});
});
Perhaps your projectService should be pointing to the /all_projects end point instead for .all. So to sum up:
API end points for Project/Task should be different
Be sure to pass the project ID/task if necessary, otherwise you're not going to get the data you're expecting.
Set breakpoints and log output to trace your code path to see where other mistakes may be.

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

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).

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