<!DOCTYPE html>
<html ng-app="ngMailChimp">
<head>
<link data-require="bootstrap@*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script data-require="angular.js@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular.js"></script>
<script data-require="angular-messages@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular-messages.js"></script>
<script data-require="angular-animate@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular-animate.js"></script>
<script data-require="angular-aria@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular-aria.js"></script>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="animations.css" />
<script src="script.js"></script>
</head>
<body ng-controller="SignUpController as ctrl">
<div class="signup-wrapper">
<div class="logo">
<img src="http://i.imgur.com/SbhUmwh.png" alt="Logo" />
</div>
<div class="alert alert-success message-animation" role="alert" ng-if="ctrl.showSubmittedPrompt">
Thank you! Your account has been created.
</div>
<form name="ctrl.signupForm" ng-submit="ctrl.signup()" novalidate>
<div class="form-group" ng-class="{'has-error':ctrl.hasErrorClass('email')}">
<label for="email">Email</label>
<input id="email" name="email" class="form-control" type="email" required ng-model="ctrl.newCustomer.email" ng-model-options="{ updateOn : 'default blur' }" ng-focus="ctrl.toggleEmailPrompt(true)" ng-blur="ctrl.toggleEmailPrompt(false)" />
<div class="my-messages">
<div class="prompt message-animation" ng-if="ctrl.showEmailPrompt">
What's your email address?
</div>
</div>
<div class="my-messages" ng-messages="ctrl.signupForm.email.$error" ng-if="ctrl.showMessages('email')">
<div class="message-animation" ng-message="required">
<strong>This field is required.</strong>
</div>
<div class="message-animation" ng-message="email">
<strong>Please format your email correctly.</strong>
</div>
</div>
</div>
<div class="form-group" ng-class="{'has-error':ctrl.hasErrorClass('userName')}">
<label for="userName">Username</label>
<input id="userName" name="userName" class="form-control" type="text" required ng-model="ctrl.newCustomer.userName" ng-model-options="{ updateOn : 'default blur' }" ng-focus="ctrl.toggleUsernamePrompt(true)" ng-blur="ctrl.toggleUsernamePrompt(false)"
/>
<div class="my-messages">
<div class="prompt message-animation" ng-if="ctrl.showUsernamePrompt">
Choose a username that contains only letters and numbers, or use your email address.
</div>
</div>
<div class="my-messages" ng-messages="ctrl.signupForm.userName.$error" ng-if="ctrl.showMessages('userName')">
<div class="message-animation" ng-message="required">
<strong>This field is required.</strong>
</div>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<div class="input-group" ng-class="{'has-error':ctrl.hasErrorClass('password')}">
<input id="password" name="password" class="form-control" required type="{{ctrl.getPasswordType()}}" ng-model-options="{ updateOn : 'default blur' }" ng-model="ctrl.newCustomer.password" validate-password-characters/>
<span class="input-group-addon">
<input type="checkbox" ng-model="ctrl.signupForm.showPassword"> Show
</span>
</div>
<div class="my-messages" ng-messages="ctrl.signupForm.password.$error" ng-if="ctrl.showMessages('password')">
<div class="message-animation" ng-message="required">
<strong>This field is required.</strong>
</div>
</div>
<div class="password-requirements" ng-if="!ctrl.signupForm.password.$valid">
<ul class="float-left">
<li ng-class="{'completed':!ctrl.signupForm.password.$error.lowerCase}">One lowercase character</li>
<li ng-class="{'completed':!ctrl.signupForm.password.$error.upperCase}">One uppercase character</li>
<li ng-class="{'completed':!ctrl.signupForm.password.$error.number}">One number</li>
</ul>
<ul class="selfclear clearfix">
<li ng-class="{'completed':!ctrl.signupForm.password.$error.specialCharacter}">One special character</li>
<li ng-class="{'completed':!ctrl.signupForm.password.$error.eightCharacters}">Eight characters minimum</li>
</ul>
</div>
<div class="alert alert-success message-animation" role="alert" ng-if="ctrl.signupForm.password.$valid">
Your password is secure and you are good to go!
</div>
</div>
<button class="btn btn-primary" type="submit">Create My Account</button>
</form>
</div>
</body>
</html>
angular.module('ngMailChimp', ['ngAria', 'ngMessages', 'ngAnimate'])
.controller('SignUpController', function () {
var ctrl = this,
newCustomer = { email:'', userName:'', password:'' };
var signup = function () {
if( ctrl.signupForm.$valid) {
ctrl.showSubmittedPrompt = true;
clearForm();
}
};
var clearForm = function () {
ctrl.newCustomer = { email:'', userName:'', password:'' }
ctrl.signupForm.$setUntouched();
ctrl.signupForm.$setPristine();
};
var getPasswordType = function () {
return ctrl.signupForm.showPassword ? 'text' : 'password';
};
var toggleEmailPrompt = function (value) {
ctrl.showEmailPrompt = value;
};
var toggleUsernamePrompt = function (value) {
ctrl.showUsernamePrompt = value;
};
var hasErrorClass = function (field) {
return ctrl.signupForm[field].$touched && ctrl.signupForm[field].$invalid;
};
var showMessages = function (field) {
return ctrl.signupForm[field].$touched || ctrl.signupForm.$submitted
};
ctrl.showEmailPrompt = false;
ctrl.showUsernamePrompt = false;
ctrl.showSubmittedPrompt = false;
ctrl.toggleEmailPrompt = toggleEmailPrompt;
ctrl.toggleUsernamePrompt = toggleUsernamePrompt;
ctrl.getPasswordType = getPasswordType;
ctrl.hasErrorClass = hasErrorClass;
ctrl.showMessages = showMessages;
ctrl.newCustomer = newCustomer;
ctrl.signup = signup;
ctrl.clearForm = clearForm;
})
.directive('validatePasswordCharacters', function () {
return {
require: 'ngModel',
link: function ($scope, element, attrs, ngModel) {
ngModel.$validators.lowerCase = function (value) {
var pattern = /[a-z]+/;
return (typeof value !== 'undefined') && pattern.test(value);
};
ngModel.$validators.upperCase = function (value) {
var pattern = /[A-Z]+/;
return (typeof value !== 'undefined') && pattern.test(value);
};
ngModel.$validators.number = function (value) {
var pattern = /\d+/;
return (typeof value !== 'undefined') && pattern.test(value);
};
ngModel.$validators.specialCharacter = function (value) {
var pattern = /\W+/;
return (typeof value !== 'undefined') && pattern.test(value);
};
ngModel.$validators.eightCharacters = function (value) {
return (typeof value !== 'undefined') && value.length >= 8;
};
}
}
})
;
body {
background-color: #f9f9f9;
}
.signup-wrapper {
top: 36px;
max-width: 430px;
min-width: 320px;
margin-left: auto;
margin-right: auto;
}
.signup-wrapper .alert {
margin-top: 10px;
}
.signup-wrapper input[text] {
padding: 0 .4em 0 .4em;
margin-bottom: 2em;
vertical-align: middle;
border-radius: 3px;
min-width: 50px;
max-width: 550px;
width: 100%;
min-height: 36px;
background-color: #fff;
border: 3px solid #c9c9c9 !important;
}
.signup-wrapper label {
color: #595959;
font-weight: 500;
display: block;
max-width: 550px;
padding-bottom: .4em;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
font-size: 15px;
line-height: 18px;
}
.prompt {
margin-top: 10px;
white-space: normal;
word-wrap: break-word;
width: 100%;
max-width: 550px;
position: relative;
color: #959595;
}
.logo {
position: relative;
height: 46px;
width: 320px;
margin-left: auto;
margin-right: auto;
margin-top: 80px;
margin-bottom: 30px;
z-index: 1;
}
.float-left {
float: left;
}
.password-requirements {
margin-top: 10px;
}
.password-requirements .selfclear {
margin-left: 20px;
}
.password-requirements ul {
list-style: none;
padding-left: 0px;
}
.password-requirements ul:first-child {
margin-right: 15px;
}
.password-requirements ul li:before {
position: relative;
top: -1px;
content: "";
display: inline-block;
width: 8px;
height: 8px;
margin-left: 6px;
margin-right: 10px;
border-radius: 8px;
background: #52bad5;
}
.password-requirements ul li.completed {
opacity: .5;
}
.btn-primary {
color: #fff !important;
padding: 10px 30px !important;
background-color: #40A3FD !important;
border-color: #0086FC;
}
.btn-primary:hover {
background-color: #4F94D3 !important;
}
div[ng-messages] {
margin-top: 10px;
white-space: normal;
word-wrap: break-word;
width: 100%;
max-width: 550px;
position: relative;
color: #FF0000;
}
Animations with AngularJS Example One
==============
This is an example of a MailChimp style signup form built with AngularJS.
#Challenge#
1. Create a new CSS file called ```workshop.css``` and add a reference to it in ```index.html```
2. Change the ```message-animation``` class references in ```index.html``` to ```workshop-animation```
3. Using ```animations.css``` as a reference, build out the ```workshop-animation``` classes to fade messages in and out
#Bonus#
Animate an addtional property to make the animation more interesting
Resources
========================
One Hungry Mind
http://onehungrymind.com/
Year of Moo
http://www.yearofmoo.com/
Greensock
http://www.greensock.com/
.my-messages {
position: relative;
}
.message-animation {
transition: 0.2s ease-in-out all;
}
.message-animation.ng-enter.ng-enter-active {
opacity: 1;
margin-top: 0;
}
.message-animation.ng-enter {
opacity: 0;
margin-top: -20px;
}
.message-animation.ng-leave {
opacity: 1;
margin-top: 0;
}
.message-animation.ng-leave-active {
opacity: 0;
margin-top: -20px;
}