<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Sign-in Register Service</title>
<link href="//code.ionicframework.com/1.1.0/css/ionic.css" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
<script src="//code.ionicframework.com/1.1.0/js/ionic.bundle.js"></script>
<script data-require="lodash.js@3.10.0" data-semver="3.10.0" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.js"></script>
<script src="script.js"></script>
<script src="modals.service.js"></script>
<script src="sign-in-register.service.js"></script>
</head>
<body ng-controller="AppCtrl as vm">
<ion-header-bar class="bar-positive">
<h1 class="title">Sign-in/Register Service</h1>
</ion-header-bar>
<ion-content class="has-header">
<div class="row responsive-sm">
<div class="col col-offset-25 col-50">
<div class="list card">
<div class="item item-divider item-positive">Sign-in or Register</div>
<div class="item">
<div class="button-bar">
<button ng-click="vm.showSignInRegister( 'signin' )" class="button button-balanced">
Sign in
</button>
<button ng-click="vm.showSignInRegister( 'signup' )" class="button button-balanced">
Register
</button>
</div>
</div>
<div class="item">
<label class="item item-input">
<span class="input-label">Username</span>
<input type="text" ng-model="vm.user.username" placeholder="username" readonly="readonly" />
</label>
</div>
<div class="item">
<label class="item item-input">
<span class="input-label">Email Address</span>
<input type="text" ng-model="vm.user.email" readonly="readonly" />
</label>
</div>
</div>
<div class="card">
<div class="item item-divider item-assertive">console</div>
<div class="item">
<pre ng-repeat="msg in vm.console">{{msg}}</pre>
</div>
</div>
</div></div>
</ion-content>
</body>
</html>
// condensed John Papa style
(function() {
'use strict';
angular.module('app', ['ionic', 'app.core']);
angular.module('app.core', ['app.components']);
angular.module('app.components', []);
var appRun, ionicConfig, toastrConfig;
appRun = function($rootScope, $ionicPlatform, $ionicHistory, $location, $state) {
$ionicPlatform.ready(function() {
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
return StatusBar.styleLightContent();
}
});
};
ionicConfig = function($ionicConfigProvider) {
$ionicConfigProvider.backButton.text('').icon('ion-ios-arrow-back').previousTitleText(false);
};
appRun.$inject = ['$rootScope', '$ionicPlatform', '$ionicHistory', '$location', '$state'];
ionicConfig.$inject = ['$ionicConfigProvider'];
angular.module('app.core')
.config(ionicConfig)
.run(appRun);
/*
* for testing REST API calls
*/
var UsersResource = function($q){
this.data = [
{
username: 'bob',
email: 'bob@mail.com'
},
{
username: 'art',
email: 'art@mail.com'
},
{
username: 'jane',
email: 'jane@mail.com'
}
];
this.query = function(filter){
var found = _.filter( this.data, filter);
return $q.when(found);
};
this.post = function(data){
var exists = _.filter( this.data, {username:data.username})
if (exists.length)
return $q.reject( "DUPLICATE KEY");
this.data.push(data);
return $q.when(data)
}
}
UsersResource.$inject = ['$q']
var AppCtrl;
AppCtrl = function($log, $rootScope, $q, signInRegisterSvc, UsersResource) {
var vm;
vm = this;
vm.isBrowser = ionic.Platform.isWebView() === false
vm.user = {};
vm.signIn = function(data) {
vm.user = {};
return $q.when( data
).then(function(data) {
return UsersResource.query({
username: data.username
});
}).then(function(user) {
$rootScope.$emit('user:sign-in', $rootScope['user']);
return user;
})["catch"](function(err) {
$rootScope.$emit('user:sign-out');
});
}
vm.register = function(data) {
vm.user = {};
return $q.when( data
).then(function(data) {
return UsersResource.post(data);
})["catch"](function(err) {
$rootScope.$emit('user:sign-out');
if (_.isString(err)) {
err = {
message: err
};
}
err['isError'] = true;
return err;
});
}
vm.showSignInRegister = function(initialSlide) {
var vm;
vm = this;
return signInRegisterSvc.showSignInRegister(initialSlide, {
signIn: vm.signIn,
checkIfUserExists: function(data) {
return UsersResource.query({
username: data.username
});
},
register: vm.register,
notImplemented: null
}).then(function(results) {
console.log(['SignInRegisterSvc', results]);
vm.user = results;
return results;
});
};
window.console.log = function(msg) {
vm.console.unshift(vm.console.length + ":" + JSON.stringify(msg, null, 2));
}
vm.loading = false;
vm.console = [];
console.log("ready")
return vm
};
AppCtrl.$inject = ['$log', '$rootScope', '$q', 'signInRegisterSvc', 'UsersResource'];
angular.module('app')
.service('UsersResource', UsersResource)
.controller('AppCtrl', AppCtrl);
}).call(this);
/* Styles go here */
.pull-right {
float: right;
}
#sign-in-register-modal-view .item, #sign-in-register-modal-view .item-input {
border-color:transparent;
}
#sign-in-register-modal-view input {
width: 100%;
}
#sign-in-register-modal-view .item a {
cursor: pointer;
}
# Sign-in/Register Service for ionic Framework
This service uses `$ionicModal` to open a modal content pane containing
an `ion-slide` with both a sign-up and register form.
add `signInRegisterSvc` as a dependency to your controller,
then launch the service with the following call:
```javascript
signInRegisterSvc.showSignInRegister( 'signup', {
signIn: function(data){
// your server sign-in code
},
checkIfUserExists: function(data) {
// check if username already exists
},
register: function(data){
// your server new user registration code
}
}).then(function(results) {
console.log(['SignInRegisterSvc', results]);
$rootScope.user = results;
return results;
});
```
`bower.json` dependencies
```json
{
"devDependencies": {
"ionic": "driftyco/ionic-bower#1.1.0",
"lodash": "~3.10.1",
}
}
```
(function() {
'use strict';
/*
* @description: reusable $ionicModal service
* see: http://forum.ionicframework.com/t/ionic-modal-service-with-extras/15357
* also: http://codepen.io/anon/pen/KdzawK?editors=101
* usage: appModalSvc.show( <templateUrl>, "controller as vm", params )
*/
var ReusableModal;
ReusableModal = function($ionicModal, $rootScope, $q, $injector, $controller) {
var _cleanup, _evalController, show;
show = function(templateUrl, controller, parameters, options) {
var ctrlInstance, defaultOptions, dfd, modalScope, thisScopeId;
dfd = $q.defer();
modalScope = $rootScope.$new();
thisScopeId = modalScope.$id;
ctrlInstance = null;
defaultOptions = {
animation: 'slide-in-up',
focusFirstInput: false,
backdropClickToClose: true,
hardwareBackButtonClose: true,
modalCallback: null
};
options = angular.extend(defaultOptions, options, {
scope: modalScope
});
$ionicModal.fromTemplateUrl(templateUrl, options).then(function(modal) {
var ctrlEval, locals, same;
modalScope.modal = modal;
modalScope.openModal = function() {
return modalScope.modal.show();
};
modalScope.closeModal = function(result) {
dfd.resolve(result);
return modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function(thisModal) {
var modalScopeId;
if (thisModal.currentScope) {
modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === thisModal.currentScope.$id) {
dfd.resolve(null);
return _cleanup(thisModal.currentScope);
}
}
});
if (angular.isObject(controller)) {
modalScope.vm = ctrlInstance = controller;
same = modalScope.vm === modalScope.modal.scope.vm;
ctrlInstance['openModal'] = modalScope.openModal;
ctrlInstance['closeModal'] = modalScope.closeModal;
} else {
locals = {
'$scope': modalScope,
'parameters': parameters
};
ctrlEval = _evalController(controller);
if (ctrlEval.controllerName) {
ctrlInstance = $controller(controller, locals);
}
if (ctrlEval.isControllerAs && ctrlInstance) {
ctrlInstance['openModal'] = modalScope.openModal;
ctrlInstance['closeModal'] = modalScope.closeModal;
}
}
if (parameters != null) {
angular.extend(modalScope, parameters);
}
return modalScope.modal.show().then(function() {
modalScope.$broadcast('modal.afterShow', modalScope.modal);
return typeof options.modalCallback === "function" ? options.modalCallback(modal) : void 0;
});
}, function(err) {
return dfd.reject(err);
});
return dfd.promise;
};
_cleanup = function(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
};
_evalController = function(ctrlName) {
var fragments, result;
if (ctrlName == null) {
ctrlName = '';
}
result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
fragments = ctrlName.trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = ctrlName;
} else {
result.controllerName = ctrlName;
}
return result;
};
return {
show: show
};
};
ReusableModal.$inject = ['$ionicModal', '$rootScope', '$q', '$injector', '$controller'];
angular.module('app.components')
.factory('appModalSvc', ReusableModal);
}).call(this);
<style id="sign-in-register-style">
@media (min-width: 680px) {
#sign-in-register-modal-view.modal { top: 20%; height:424px; bottom: 20%;}
}
</style>
<ion-modal-view id="sign-in-register-modal-view">{{vm.slideCtrl.setSlide();''}}
<ion-slide-box delegate-handle="sign-in-register" active-slide="vm.slideCtrl.index" auto-play="false" show-pager="false" slide-interval="" pager-click="" on-slide-changed="">
<ion-slide class="sign-up">
<ion-scroll>
<div class="list">
<div class="item">
<button ng-click="vm.on.notImplemented('Facebook Connect')" class="button button-block button-positive light icon ion-social-facebook"> Sign-up with Facebook</button>
</div>
<div class="item">
<div class="text-center larger">— or —</div>
</div>
<div class="padding-horizontal">
<form name="registerForm" novalidate="" ng-submit="vm.on.register(person)">
<label class="item item-input"><i class="icon ion-person padding-horizontal larger"></i>
<input type="text" name="Username" placeholder="Username" ng-model="person.username" required="required"/>
</label>
<div ng-show="vm['error']['username']" class="error"><span class="assertive">{{vm['error']['username']}}</span></div>
<label class="item item-input"><i class="icon ion-email padding-horizontal larger"></i>
<input type="text" name="Email" placeholder="Email" ng-model="person.email" required="required"/>
</label>
<label class="item item-input"><i class="icon ion-locked padding-horizontal larger"></i>
<input type="password" name="Password" placeholder="Password" ng-model="person.password" required="required"/>
</label>
<label class="item item-input hide"><i class="icon ion-locked padding-horizontal larger"></i>
<input type="password" name="PasswordRepeat" placeholder="Again" ng-model="person.passwordRepeat"/>
</label>
<div ng-show="vm['error']['passwordRepeat']" class="error"><span class="assertive">{{vm['error']['passwordRepeat']}} </span></div>
<div class="item">
<div class="button-bar">
<button ng-click="closeModal('CANCELED')" class="button button-balanced button-outline">Cancel</button>
<button type="submit" class="button button-balanced">Sign up</button>
</div>
</div>
<div class="item positive">
<div class="text-right"> <a ng-click="vm.slideCtrl.setSlide('signin')">Have an account? Sign in <i class="icon ion-android-arrow-forward padding-right"></i></a></div>
</div>
</form>
</div>
</div>
</ion-scroll>
</ion-slide>
<ion-slide class="sign-in">
<ion-scroll>
<div class="list">
<div class="item">
<button ng-click="vm.on.notImplemented('Facebook Connect')" class="button button-block button-positive light icon ion-social-facebook"> Sign-in with Facebook</button>
</div>
<div class="item">
<div class="text-center larger">— or —</div>
</div>
<div class="padding-horizontal">
<form name="signInForm" novalidate="" ng-submit="vm.on.signIn(person);">
<label class="item item-input"><i class="icon ion-person padding-horizontal larger"></i>
<input type="text" name="Username" placeholder="Username" ng-model="person.username" required="required"/>
</label>
<div ng-show="vm['error']['username']" class="error"><span class="assertive">{{vm['error']['username']}}</span></div>
<label class="item item-input"><i class="icon ion-locked padding-horizontal larger"></i>
<input type="password" name="Password" placeholder="Password" ng-model="person.password" required="required"/>
</label>
<div class="item">
<div class="button-bar">
<button ng-click="closeModal('CANCELED')" class="button button-balanced button-outline">Cancel</button>
<button type="submit" class="button button-balanced">Sign in</button>
</div>
</div>
<div class="item positive text-left">
<div class="pull-right"><a ng-click="vm.on.notImplemented('password recovery')">Forgot Password?</a></div><a ng-click="vm.slideCtrl.setSlide('signup')"><i class="icon ion-android-arrow-back padding-left"></i> Sign up</a>
</div>
</form>
</div>
</div>
</ion-scroll>
</ion-slide>
</ion-slide-box>
</ion-modal-view>
(function() {
'use strict';
var CALLBACKS, MODAL_VIEW, SignInRegister, SignInRegisterCtrl, signInRegisterSvcConfig;
CALLBACKS = {
signIn: function(data) {
return [data];
},
checkIfUserExists: function(username) {
return [];
},
register: function(data) {
return data;
},
notImplemented: function(label) {
return console.log(["Sorry,", label, "is not available yet."].join(' '));
}
};
MODAL_VIEW = {
TEMPLATE: 'sign-in-register.template.html',
CONTENT_HEIGHT: 424,
GRID_RESPONSIVE_SM_BREAK: 680,
MESSAGE: {},
ERROR: {
REQURIED: 'Please enter a value.',
USERNAME_EXISTS: 'Sorry, that username is already taken.',
PASSWORD_NO_MATCH: 'Sorry, your passwords do not match.',
USER_NOT_FOUND: 'Sorry, the username and password combination was not found.'
},
DEFAULT_SLIDE: 'signin'
};
/*
* @description Sign-in or Register modal service
* shows $ionicModal for allowing user sign-in or register
*/
SignInRegister = function($q, appModalSvc) {
var init, self, setModalHeight;
init = function() {
var stop;
stop = $scope.$on('modal.afterShow', function(ev) {
var h;
h = setModalHeight();
if (typeof stop === "function") {
stop();
}
});
};
setModalHeight = function() {
var contentH, marginH, modalH, styleH;
contentH = $window.innerWidth <= MODAL_VIEW.GRID_RESPONSIVE_SM_BREAK ? $window.innerHeight : Math.max(MODAL_VIEW.CONTENT_HEIGHT, $window.innerHeight);
marginH = ($window.innerHeight - contentH) / 2;
modalH = Math.max(MODAL_VIEW.MAP_MIN_HEIGHT, modalH);
styleH = "#sign-in-register-modal-view.modal {top:%marginH%px; bottom:%marginH%px; height:%modalH%px}";
styleH = styleH.replace(/%marginH%/g, marginH).replace(/%modalH%/g, modalH);
angular.element(document.getElementById('address-lookup-style')).append(styleH);
return modalH;
};
self = {
showSignInRegister: function(initialSlide, callbacks) {
if (_.isFunction(callbacks.signIn)) {
CALLBACKS.signIn = callbacks.signIn;
}
if (_.isFunction(callbacks.checkIfUserExists)) {
CALLBACKS.checkIfUserExists = callbacks.checkIfUserExists;
}
if (_.isFunction(callbacks.register)) {
CALLBACKS.register = callbacks.register;
}
if (_.isFunction(callbacks.notImplemented)) {
CALLBACKS.notImplemented = callbacks.notImplemented;
}
return appModalSvc.show( MODAL_VIEW.TEMPLATE, 'SignInRegisterCtrl as vm', {
initialSlide: initialSlide,
person: {}
}).then(function(result) {
console.log(['showSignInRegister', result]);
if (result == null) {
result = 'CANCELED';
}
if (result === 'CANCELED') {
return $q.reject('CANCELED');
}
if (result != null ? result['isError'] : void 0) {
return $q.reject(result);
}
return result;
});
}
};
return self;
};
SignInRegister.$inject = ['$q', 'appModalSvc'];
/*
* @description controller for appModalSvc $ionicModal
*
*/
SignInRegisterCtrl = function($scope, parameters, $q, $timeout, $window) {
var vm;
vm = this;
vm.isBrowser = !ionic.Platform.isWebView();
vm.MESSAGE = MODAL_VIEW.MESSAGE;
vm['slideCtrl'] = {
index: null,
slideLabels: ['signup', 'signin'],
initialSlide: parameters.initialSlide || MODAL_VIEW.DEFAULT_SLIDE,
setSlide: function(label) {
var i, next;
if (vm['slideCtrl'].index === null) {
$timeout(function() {
if (!label) {
label = vm['slideCtrl'].initialSlide;
}
return vm['slideCtrl'].setSlide(label);
});
return vm['slideCtrl'].index = 0;
}
if (label==null) {
return vm['slideCtrl'].index;
}
if (label === 'initial') {
label = vm['slideCtrl'].initialSlide;
}
i = vm['slideCtrl'].slideLabels.indexOf(label);
next = i >= 0 ? i : vm['slideCtrl'].index;
vm['error'] = {};
return vm['slideCtrl'].index = next;
}
};
vm['error'] = {};
vm['on'] = {
signIn: function(data, fnComplete) {
if (data == null) {
data = {};
}
vm['error'] = {};
return $q.when().then(function() {
if (!data.username) {
return $q.reject('NOT FOUND');
}
data.username = data.username.toLowerCase().trim();
return CALLBACKS.signIn(data);
}).then(function(results) {
var person;
person = _.isArray(results) ? results.shift() : results;
if (_.isEmpty(person)) {
return $q.reject('NOT FOUND');
}
return person;
}).then(function(result) {
vm.closeModal(result);
return result;
})["catch"](function(err) {
if (err === 'NOT FOUND') {
vm['error']['username'] = MODAL_VIEW.ERROR.USER_NOT_FOUND;
}
return $q.reject(err);
});
},
register: function(data, fnComplete) {
if (data == null) {
data = {};
}
vm['error'] = {};
return $q.when().then(function() {
if (!data.username) {
return $q.reject('REQUIRED VALUE');
}
data.username = data.username.toLowerCase().trim();
return CALLBACKS.checkIfUserExists(data);
}).then(function(results) {
var person;
person = _.isArray(results) ? results.shift() : results;
if (!_.isEmpty(person)) {
return $q.reject('DUPLICATE USERNAME');
}
return CALLBACKS.register(data);
}).then(function(result) {
vm.closeModal(result);
return result;
})["catch"](function(err) {
if (err === 'REQUIRED VALUE') {
vm['error']['username'] = MODAL_VIEW.ERROR.REQUIRED;
}
if (err === 'DUPLICATE USERNAME') {
vm['error']['username'] = MODAL_VIEW.ERROR.USERNAME_EXISTS;
}
return $q.reject(err);
});
},
notImplemented: function(value) {
return CALLBACKS.notImplemented(value);
}
};
return vm;
};
SignInRegisterCtrl.$inject = ['$scope', 'parameters', '$q', '$timeout', '$window'];
angular.module('app.components')
.factory('signInRegisterSvc', SignInRegister)
.controller('SignInRegisterCtrl', SignInRegisterCtrl);
}).call(this);