Basic form with validation using AngularJS and Form Material Design Lite ( MDL )
A Pen by Álister Lopes Ferreira on CodePen.
Basic form with validation using AngularJS and Form Material Design Lite ( MDL )
A Pen by Álister Lopes Ferreira on CodePen.
<div ng-app="validationApp" ng-controller="mainController" class="layout mdl-layout mdl-js-layout mdl-layout--fixed-header"> | |
<main class="mdl-layout__content" data-ng-view="" ng-class="render.mdlColor? render.mdlColor : 'mdl-color--grey-100'"> | |
<div class="mdl-grid"> | |
<div class="mdl-cell mdl-cell--12-col"> | |
<h4>Registration Form!</h4> | |
<h5 ng-if="formStatus">{{formStatus}}</h5> | |
<form name="form" ng-submit="submit()" novalidate> | |
<fieldset> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="text" id="companyName" name="companyName" ng-model="data.companyName" ng-required="true" /> | |
<label class="mdl-textfield__label" for="companyName">Company name*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="companyName" ng-show="form.companyName.$invalid && form.companyName.$touched"> | |
<span ng-show="form.companyName.$error.required">Required.</span> | |
</span> | |
</div> | |
</fieldset> | |
<fieldset> | |
<ul class="list-unstyled"> | |
<li> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="text" id="name" name="name" pattern="[A-Za-z\s]*" ng-model="data.name" ng-required="true" /> | |
<label class="mdl-textfield__label" for="name">Full name*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="name" ng-show="form.name.$invalid && form.name.$touched"> | |
<span ng-show="form.name.$error.required">Required.</span> | |
<span ng-show="form.name.$error.pattern">Invalid pattern.</span> | |
</span> | |
</div> | |
</li> | |
<li> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="email" id="email" name="email" ng-model="data.email" ng-required="true" /> | |
<label class="mdl-textfield__label" for="email">Email*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="email" ng-show="form.email.$invalid && form.email.$touched"> | |
<span ng-show="form.email.$error.required">Required.</span> | |
<span ng-show="form.email.$error.email">Invalid email.</span> | |
</span> | |
</div> | |
</li> | |
<li> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="text" id="login" name="login" pattern="^[a-zA-Z][a-zA-Z0-9-_\.]*" minlength="3" ng-model="data.login" ng-required="true" /> | |
<label class="mdl-textfield__label" for="login">Login*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="login" ng-show="form.login.$invalid && form.login.$touched"> | |
<span ng-show="form.login.$error.required">Required.</span> | |
<span ng-show="form.login.$error.pattern">Invalid pattern.</span> | |
<span ng-show="form.login.$error.minlength">Minimum of three characters.</span> | |
</span> | |
</div> | |
</li> | |
<li> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="password" id="password" name="password" ng-model="data.password" ng-required="true" /> | |
<label class="mdl-textfield__label" for="password">Password*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="password" ng-show="form.password.$invalid && form.password.$touched"> | |
<span ng-show="form.password.$error.required">Required.</span> | |
</span> | |
</div> | |
</li> | |
<li> | |
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"> | |
<input class="mdl-textfield__input" type="password" id="confirmPassword" name="confirmPassword" ng-model="data.confirmPassword" ng-required="true" field-match="data.password" /> | |
<label class="mdl-textfield__label" for="confirmPassword">Confirm Password*</label> | |
<span class="mdl-tooltip mdl-tooltip--validation" for="confirmPassword" ng-show="form.confirmPassword.$invalid && form.confirmPassword.$touched"> | |
<span ng-show="form.confirmPassword.$error.required">Required.</span> | |
<span ng-show="form.confirmPassword.$error.fieldmatch">Passwords do not match.</span> | |
</span> | |
</div> | |
</li> | |
</ul> | |
</fieldset> | |
<button type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-js-ripple-effect">Register</button> | |
</form> | |
</div> | |
</div> | |
</main> | |
</div> |
// create angular app | |
var validationApp = angular.module('validationApp', ['fieldMatch']); | |
//Field Match directive | |
angular.module('fieldMatch', []) | |
.directive('fieldMatch', ["$parse", function($parse) { | |
return { | |
require: 'ngModel', | |
link: function(scope, elem, attrs, ctrl) { | |
var me = $parse(attrs.ngModel); | |
var matchTo = $parse(attrs.fieldMatch); | |
scope.$watchGroup([me, matchTo], function(newValues, oldValues) { | |
ctrl.$setValidity('fieldmatch', me(scope) === matchTo(scope)); | |
}, true); | |
} | |
} | |
}]); | |
//Run material design lite | |
validationApp.run(function($rootScope, $timeout) { | |
$rootScope.$on('$viewContentLoaded', function(event) { | |
$timeout(function() { | |
componentHandler.upgradeAllRegistered(); | |
}, 0); | |
}); | |
$rootScope.render = { | |
header: true, | |
aside: true | |
} | |
}); | |
// create angular controller | |
validationApp.controller('mainController', function($scope) { | |
$scope.formStatus = ''; | |
// function to submit the form after all validation has occurred | |
$scope.submit = function() { | |
// check to make sure the form is completely valid | |
if ($scope.form.$invalid) { | |
angular.forEach($scope.form.$error, function(field) { | |
angular.forEach(field, function(errorField) { | |
errorField.$setTouched(); | |
}) | |
}); | |
$scope.formStatus = "Form is invalid."; | |
console.log("Form is invalid."); | |
} else { | |
$scope.formStatus = "Form is valid."; | |
console.log("Form is valid."); | |
console.log($scope.data); | |
} | |
}; | |
}); |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script> | |
<script src="https://storage.googleapis.com/code.getmdl.io/1.0.4/material.min.js"></script> |
/*Disable MDL validation styles*/ | |
.mdl-textfield.is-invalid .mdl-textfield__input { | |
border-bottom: 1px solid rgba(0, 0, 0, .12); | |
} | |
.mdl-textfield--floating-label.is-invalid .mdl-textfield__label { | |
color: rgba(0, 0, 0, .26); | |
} | |
.mdl-textfield--floating-label.is-focused .mdl-textfield__label, | |
.mdl-textfield--floating-label.is-dirty .mdl-textfield__label { | |
color: rgb(63, 81, 181); | |
} | |
.mdl-textfield.is-invalid .mdl-textfield__label:after { | |
background-color: rgb(63, 81, 181); | |
} | |
/*Validation styles based on MDL*/ | |
.ng-invalid.ng-invalid.ng-touched { | |
border-color: rgb(222, 50, 38); | |
box-shadow: none; | |
} | |
.ng-invalid.ng-invalid.ng-touched + label { | |
color: rgb(222, 50, 38); | |
font-size: 12px; | |
} | |
.ng-invalid.ng-invalid.ng-touched + label:after { | |
background-color: rgb(222, 50, 38); | |
} | |
.ng-invalid.ng-invalid.ng-touched ~ .mdl-tooltip--validation { | |
background-color: rgb(222, 50, 38); | |
} | |
.ng-invalid.ng-invalid.ng-touched.ng-dirty:focus { | |
border-color: rgb(255, 193, 7); | |
box-shadow: none; | |
} | |
.ng-invalid.ng-invalid.ng-touched.ng-dirty:focus + label { | |
color: rgb(255, 193, 7); | |
font-size: 12px; | |
} | |
.ng-invalid.ng-invalid.ng-touched.ng-dirty:focus + label:after { | |
background-color: rgb(255, 193, 7); | |
} | |
.ng-invalid.ng-invalid.ng-touched.ng-dirty:focus ~ .mdl-tooltip--validation { | |
background-color: rgb(255, 193, 7); | |
} | |
.ng-invalid.ng-touched:not(:focus) + label::before { | |
font-family: 'Material Icons'; | |
font-weight: normal; | |
font-style: normal; | |
font-size: 24px; | |
/* Preferred icon size */ | |
display: inline-block; | |
width: 1em; | |
height: 1em; | |
line-height: 1; | |
text-transform: none; | |
letter-spacing: normal; | |
word-wrap: normal; | |
/* Support for all WebKit browsers. */ | |
-webkit-font-smoothing: antialiased; | |
/* Support for Safari and Chrome. */ | |
text-rendering: optimizeLegibility; | |
/* Support for Firefox. */ | |
-moz-osx-font-smoothing: grayscale; | |
/* Support for IE. */ | |
font-feature-settings: 'liga'; | |
content: "warning"; | |
position: absolute; | |
right: 0; | |
bottom: 20px; | |
} | |
.ng-valid.ng-touched:not(:focus) + label::before { | |
font-family: 'Material Icons'; | |
font-weight: normal; | |
font-style: normal; | |
font-size: 24px; | |
/* Preferred icon size */ | |
display: inline-block; | |
width: 1em; | |
height: 1em; | |
line-height: 1; | |
text-transform: none; | |
letter-spacing: normal; | |
word-wrap: normal; | |
/* Support for all WebKit browsers. */ | |
-webkit-font-smoothing: antialiased; | |
/* Support for Safari and Chrome. */ | |
text-rendering: optimizeLegibility; | |
/* Support for Firefox. */ | |
-moz-osx-font-smoothing: grayscale; | |
/* Support for IE. */ | |
font-feature-settings: 'liga'; | |
content: "done"; | |
position: absolute; | |
right: 0; | |
bottom: 20px; | |
} | |
/*Basic styles*/ | |
.list-unstyled { | |
padding-left: 0; | |
list-style: none; | |
} |
<link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" rel="stylesheet" /> | |
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> | |
<link href="https://storage.googleapis.com/code.getmdl.io/1.0.4/material.indigo-pink.min.css" rel="stylesheet" /> |