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" /> |