"angular"
Bootstrap 3.0.0 Snippet by evarevirus

<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!DOCTYPE html><html class=''> <head><script src='//production-assets.codepen.io/assets/editor/live/console_runner-079c09a0e3b9ff743e39ee2d5637b9216b3545af0de366d4b9aad9dc87e26bfd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/events_runner-73716630c22bbc8cff4bd0f07b135f00a0bdc5d14629260c3ec49e5606f98fdd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/css_live_reload_init-2c0dc5167d60a5af3ee189d570b1835129687ea2a61bee3513dee3a50c115a77.js'></script><meta charset='UTF-8'><meta name="robots" content="noindex"><link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" /><link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" /><link rel="canonical" href="https://codepen.io/StuffieStephie/pen/BzmxXd?depth=everything&limit=all&order=popularity&page=21&q=material+&show_forks=false" /> <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/css/materialize.css'> <style class="cp-pen-styles"> .width-30-pct{ width:30%; } .text-align-center{ text-align:center; } .margin-bottom-1em{ margin-bottom:1em; } .breakEmail, .breakEmail a{ /* These are technically the same, but use both */ overflow-wrap: break-word; word-wrap: break-word; -ms-word-break: break-all; /* This is the dangerous one in WebKit, as it breaks things wherever */ word-break: break-all; /* Instead use this non-standard one: */ word-break: break-word; /* Adds a hyphen where the word breaks, if supported (No Blink) */ -ms-hyphens: auto; -moz-hyphens: auto; -webkit-hyphens: auto; hyphens: auto; } .activetab { background-color: #ee6e73 !important; color: #fff!important; min-width: 50px; } .activetab i.right { margin-left: 0px; } tr th {cursor: pointer;} .divider, .sorter {display: none;} .sorter .btn { margin-bottom: 2px; font-size: .75em; padding: 0 2px;} .customAction {margin-bottom: 2px;} .ng-invalid.ng-dirty, input[type=tel].ng-invalid.ng-dirty:focus:not([readonly]){border-bottom: 1px solid #F44336; box-shadow: 0 1px 0 0 #F44336;} @media screen and (max-width: 1300px) { .container { width:90%;} } /* Max width before this PARTICULAR table gets nasty This query will take effect for any screen smaller than 760px and also iPads specifically. */ @media only screen and (max-width: 760px), (min-device-width: 768px) and (max-device-width: 1024px) { tr {margin-bottom: 10px;} .divider {display: block; width:100%;} .sorter {display: inline-block; margin-top: 15px;} .nopad {padding: 0!important; margin: 0;} /* Force table to not be like tables anymore */ table, thead, tbody, th, td, tr { display: block; } /* Hide table headers (but not display: none;, for accessibility) */ thead tr { position: absolute; top: -9999px; left: -9999px; } tr { border: 1px solid #ccc; } td { /* Behave like a "row" */ border: none; border-bottom: 1px solid #eee; position: relative; padding-left: 50%; } td:before { /* Now like a table header */ position: absolute; /* Top/left values mimic padding */ /* top: 6px; */ left: 6px; width: 45%; padding-right: 10px; white-space: nowrap; margin-left: 20px; } /* Label the data */ td:nth-of-type(1):before { content: "ID"; } td:nth-of-type(2):before { content: "Name"; } td:nth-of-type(3):before { content: "Phone"; } td:nth-of-type(4):before { content: "Email"; } td:nth-of-type(5):before { content: "Status"; } td:nth-of-type(6):before { content: "Submitted"; } td:nth-of-type(7):before { content: "Modify"; } } @media screen and (max-width: 500px) { td, th {padding: 7px 2px;} .customAction {padding: 0 5px;} .waves-effect { font-size: .75em;} h4 { font-size: 1.5em;} .modal { width: 95%;} .row .col.nopad {padding: 0;} td { padding-left: 115px} td:before {width: 90px; margin-left:0; } } @media screen and (max-width: 400px) { .sorter {width: 100%; margin-top: 0px; display: block; clear: both;} .container .row {margin: 0;} .container {width: 100%;} }</style></head><body> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Read volunteers</title> <!-- include material design icons --> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> </head> <body> <!-- page content and controls will be here --> <div class="container" ng-app="myApp" ng-controller="volunteersCtrl"> <div class="row"> <div class="col s12"> <h4>Volunteers</h4> <!-- used for searching the current list --> <div class="row"> <div style="min-width: 150px" class="input-field col s2 nopad"> <select ng-model="selected" ng-change="hasChanged()" ng-options="item.title for item in fieldTable"> </select> </div> <div class="input-field col s8 nopad"> <input type="text" ng-model="search" class="form-control" placeholder="Search volunteers..." /> </div> </div> <div id="email-list" class="modal"> <div class="modal-content"> <div class="row"> <b ng-repeat="d in names | filter:filters">{{d.email}}{{$last ? '' : ', '}}</b></div> </div></div> <a ng-href="mailto:{{d.email}}{{$last ? '' : ', '}}" class="waves-effect waves-light btn margin-bottom-1em"><i class="material-icons left">email</i>Email All {{selected.field}} Volunteers ({{(names | filter:filters).length}})</a> <a href="#email-list" class="modal-trigger waves-effect waves-light btn margin-bottom-1em">View List of Email Addresses ({{(names | filter:filters).length}})</a> <br> <!-- table that shows volunteer record list --> <div style="width: 130px;" class="input-field col s2"><label for="resultlimit"> Results per page: </label></div> <div style="min-width: 50px;" class="input-field col s1 "> <select name="resultlimit" ng-model="resultlimit" ng-init="resultlimit=5"> <option value="1">1</option> <option value="3">3</option> <option value="5">5</option> <option value="10">10</option> <option value="20">20</option> <option value="100">100</option> </select> </div> <div class="sorter"><h6 style="display: inline-block;">Order by: </h6> <a ng-class="{'activetab waves-effect waves-light' : predicate == 'name', 'disabled': predicate != 'name'}" ng-click="predicate = 'name'; reverse=!reverse" class="btn">Name <i ng-show="predicate == 'name' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'name' && reverse" class="material-icons right">expand_less</i></a> <a ng-class="{'activetab waves-effect waves-light' : predicate == 'phone', 'disabled': predicate != 'phone'}" ng-click="predicate = 'phone'; reverse=!reverse" class="btn">Phone <i ng-show="predicate == 'phone' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'phone' && reverse" class="material-icons right">expand_less</i></a> <a ng-class="{'activetab waves-effect waves-light' : predicate == 'email', 'disabled': predicate != 'email'}" ng-click="predicate = 'email'; reverse=!reverse" class="btn">Email <i ng-show="predicate == 'email' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'email' && reverse" class="material-icons right">expand_less</i></a> <a ng-class="{'activetab waves-effect waves-light' : predicate == 'status', 'disabled': predicate != 'status'}" ng-click="predicate = 'status'; reverse=!reverse" class="btn">Status<i ng-show="predicate == 'status' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'status' && reverse" class="material-icons right">expand_less</i></a> <a ng-class="{'activetab waves-effect waves-light' : predicate == 'unix', 'disabled': predicate != 'unix'}" ng-click="predicate = 'unix'; reverse=!reverse" class="btn">Submited <i ng-show="predicate == 'unix' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'unix' && reverse" class="material-icons right">expand_less</i></a> </div> <div class="divider"> <hr /></div> <dir-pagination-controls boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)"></dir-pagination-controls> <table class="table hoverable bordered"> <thead> <tr> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'id'}" ng-click="predicate = 'id'; reverse=!reverse">ID <i ng-show="predicate == 'id' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'id' && reverse" class="material-icons right">expand_less</i> </th> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'name'}" ng-click="predicate = 'name'; reverse=!reverse">Name <i ng-show="predicate == 'name' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'name' && reverse" class="material-icons right">expand_less</i> </th> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'phone'}" ng-click="predicate = 'phone'; reverse=!reverse">Phone <i ng-show="predicate == 'phone' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'phone' && reverse" class="material-icons right">expand_less</i></th> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'email'}" ng-click="predicate = 'email'; reverse=!reverse" >Email <i ng-show="predicate == 'email' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'email' && reverse" class="material-icons right">expand_less</i></th> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'status'}" ng-click="predicate = 'status'; reverse=!reverse">Status <i ng-show="predicate == 'status' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'status' && reverse" class="material-icons right">expand_less</i></th> <th ng-class="{'activetab waves-effect waves-light' : predicate == 'unix'}" ng-click="predicate = 'unix'; reverse=!reverse">Submitted <i ng-show="predicate == 'unix' && !reverse" class="material-icons right">expand_more</i> <i ng-show="predicate == 'unix' && reverse" class="material-icons right">expand_less</i></th> <th class="text-align-center">Action</th> </tr> </thead> <tbody> <tr dir-paginate="d in names | filter:search | filter:filters | orderBy:predicate:reverse | itemsPerPage: resultlimit "> <td>{{ d.id }}</td> <td>{{ d.name }}</td> <td>{{ d.phone | tel }}</td> <td class="breakEmail"><a href="mailto:{{ d.email }}?Subject=Volunteering%20at%20our%20event" target="_blank">{{ d.email }}</a></td> <td>{{ d.status }}</td> <td>{{ d.unix | date:'MM/dd/yy h:mma' }}</td> <td> <a ng-click="readOne(d.id)" class="waves-effect waves-light btn customAction"><i class="material-icons left">edit</i>Edit</a> <a ng-click="deleteVolunteer($index)" class="waves-effect waves-light btn customAction"><i class="material-icons left">delete</i>Delete</a> </td> </tr> </tbody> </table> <dir-pagination-controls boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)"></dir-pagination-controls> <!-- modal for for creating new volunteer --> <form name="volunteerForm" id="modal-volunteer-form" class="modal"> <div class="modal-content"> <h4 id="modal-volunteer-title">Create New Volunteer Record</h4> <div class="row"> <div class="input-field col s12"> <input name="name" ng-model="name" type="text" ng-minlength="5" ng-pattern="/^[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]+$/u" id="form-name" placeholder="Type name here..." required /> <label for="name">Name</label> </div> <div class="input-field col s12"> <input ng-model="email" type="email" placeholder="Type email here..." required> <label for="email">Email Address</label> </div> <div class="input-field col s12"> <input ng-model="phone" required pattern="^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$" ng-minlength="10" phone-input type="tel" id="form-phone" placeholder="Type phone number here..." /> <label for="phone">Phone Number</label> </div> <div style=" margin-top: -10px; min-width: 150px" class="input-field col s2"> <label for="status"> Status: </label><br> <select name="status" ng-model="status" ng-init="status='APPLIED'"> <option value="APPLIED">APPLIED</option> <option value="APPROVED">APPROVED</option> <option value="WITHDRAWN">WITHDRAWN</option> <option value="DISMISSED">DISMISSED</option> <option value="BANNED">BANNED</option> </select> </div> <div class="input-field col nopad s12"> <a ng-class="{'disabled': volunteerForm.$invalid}" id="btn-create-volunteer" class="waves-effect waves-light btn customAction" ng-click="volunteerForm.$valid && createVolunteer()"><i class="material-icons left">add</i>Create</a> <a ng-class="{'disabled': volunteerForm.$invalid}" id="btn-update-volunteer" class="waves-effect waves-light btn customAction" ng-click="volunteerForm.$valid && updateVolunteer()"><i class="material-icons left">edit</i>Save Changes</a> <a class="modal-action modal-close waves-effect waves-light btn customAction"><i class="material-icons left">close</i>Close</a> </div> </div> </div> </form> <!-- END MODAL --> <!-- floating button for creating volunteer --> <div class="fixed-action-btn" style="bottom:45px; right:24px;"> <a class="waves-effect waves-light btn modal-trigger btn-floating btn-large red" href="#modal-volunteer-form" ng-click="showCreateForm()"><i class="large material-icons">add</i></a </div> <!-- END BUTTON --> </div> <!-- end col s12 --> </div> <!-- end row --> </div> <!-- end container --> <!-- include jquery --> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <!-- material design js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/js/materialize.min.js"></script> <!-- include angular js --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script> </body> </html> <script src='//production-assets.codepen.io/assets/common/stopExecutionOnTimeout-b2a7b3fe212eaa732349046d8416e00a9dec26eb7fd347590fbced3ab38af52e.js'></script> <script > $(document).ready(function() { $('select').material_select(); }); // angular js codes will be here var app = angular.module('myApp', ['angularUtils.directives.dirPagination']); app.controller('volunteersCtrl', function($scope, $http) { // more angular JS codes will be here $scope.currencyVal; $scope.filters = { }; $scope.predicate = "unix"; $scope.reverse = true; $scope.fieldTable = [{ field: "", title: "ALL" }, { field: "APPROVED", title: "APPROVED" }, { field: "APPLIED", title: "APPLIED" }, { field: "DISMISSED", title: "DISMISSED" }, { field: "WITHDRAWN", title: "WITHDRAWN" }, { field: "BANNED", title: "BANNED" }]; $scope.selected = $scope.fieldTable[0]; $scope.hasChanged = function() { $scope.filters = $scope.selected.field; } $scope.showCreateForm = function(){ // clear form $scope.clearForm(); //Setting default $scope.status = "APPLIED"; $scope.unix = Math.floor(Date.now()); // change modal title $('#modal-volunteer-title').text("Create New Volunteer"); // hide update volunteer button $('#btn-update-volunteer').hide(); // show create volunteer button $('#btn-create-volunteer').show(); } // END SHOW CREATE FORM // clear variable / form values $scope.clearForm = function(){ $scope.id = ""; $scope.name = ""; $scope.email = ""; $scope.phone = ""; } // END CLEAR FORM // create new volunteer $scope.createVolunteer = function(){ // fields in key-value pairs $http.post('create_volunteer.php', { 'name' : $scope.name, 'email' : $scope.email, 'phone' : $scope.phone, 'status' : $scope.status, 'unix' : $scope.unix } ).success(function (data, status, headers, config) { console.log(data); // tell the user new volunteer was created Materialize.toast(data, 4000); // close modal $('#modal-volunteer-form').closeModal(); // clear modal content $scope.clearForm(); // refresh the list $scope.getAll(); }); } //END CREATE VOLUNTEER // read volunteers $scope.names = [ { "id": "1", "name": "Alexander Hamilton", "email": "ahamil@example.com", "phone": "5555555555", "status": "APPLIED", "created": "2016-07-07 09:16:32", "unix": "1467640600000" }, { "id": "2", "name": "Jon Snow", "email": "jsnow@example.com", "phone": "1111111111", "status": "APPROVED", "created": "2016-05-02 13:12:50", "unix": "1467641611111" }, { "id": "3", "name": "Peter Pan", "email": "ppan@example.com", "phone": "2222222222", "status": "DISMISSED", "created": "2016-05-31 14:55:32", "unix": "1467642676122" }, { "id": "4", "name": "Mary Poppins", "email": "rickytikytaffy@example.com", "phone": "4444444444", "status": "WITHDRAWN", "created": "2016-05-05 22:44:01", "unix": "1467643699333" }, { "id": "5", "name": "Peter Griffin", "email": "roadhouse@rosebud.com", "phone": "7777777777", "status": "BANNED", "created": "2016-06-21 16:33:11", "unix": "1467648699444" }, { "id": "6", "name": "Anders Anderson", "email": "ders@example.com", "phone": "1112223333", "status": "APPLIED", "created": "2016-06-22 19:22:22", "unix": "1467648699555" }, { "id": "7", "name": "Natalie Portman", "email": "nport@example.com", "phone": "1111111111", "status": "APPROVED", "created": "2016-03-02 12:11:33", "unix": "1467648699666" }, { "id": "8", "name": "Meryl Streep", "email": "academy@example.com", "phone": "2222222222", "status": "DISMISSED", "created": "2016-04-17 11:12:44", "unix": "1467648699777" }, { "id": "9", "name": "David Bowie", "email": "majortom@example.com", "phone": "4444444444", "status": "WITHDRAWN", "created": "2016-05-15 10:12:55", "unix": "1467648699888" }, { "id": "10", "name": "Katniss Everdeen", "email": "prim4ever@example.com", "phone": "7777777777", "status": "DISMISSED", "created": "2016-02-15 08:17:46", "unix": "1467648699999" }, { "id": "11", "name": "Robb Stark", "email": "robb@winterfell.com", "phone": "1113334444", "status": "WITHDRAWN", "created": "2016-02-15 08:17:46", "unix": "1467648699999" }, { "id": "12", "name": "Jeff Guy", "email": "jeff@hotmail.com", "phone": "0123456789", "status": "APPROVED", "created": "2016-07-10 02:16:10", "unix": "1467648761111" }, { "id": "13", "name": "Arya Stark", "email": "arry@kingslanding.com", "phone": "4445556666", "status": "DISMISSED", "created": "2016-02-15 08:17:46", "unix": "1467648699999" }, { "id": "14", "name": "Cassandra Styles", "email": "cassie@gmail.com", "phone": "7779992222", "status": "WITHDRAWN", "created": "2016-07-10 20:30:46", "unix": "1467648762111" }, { "id": "15", "name": "Stephanie Meyers", "email": "SmyersMQ@gmail.com", "phone": "4445556666", "status": "APPLIED", "created": "2016-07-10 20:35:36", "unix": "1467648763111" }, { "id": "16", "name": "Oprah Winfrey", "email": "asdds@fgfg.com", "phone": "1234567890", "status": "APPLIED", "created": "2016-07-10 20:44:03", "unix": "1467648764111" }, { "id": "17", "name": "Arin Hanson", "email": "egoraptor@gmail.com", "phone": "1234567890", "status": "DISMISSED", "created": "2016-07-10 20:47:06", "unix": "1467648765111" }, { "id": "18", "name": "Dan Avidan", "email": "danny@sexbang.com", "phone": "2567001234", "status": "APPROVED", "created": "2016-07-10 20:50:30", "unix": "1467648766111" }, { "id": "19", "name": "Eddard Stark", "email": "Ned@stark.com", "phone": "6667774444", "status": "BANNED", "created": "2016-07-10 20:52:58", "unix": "1468183939000" }, { "id": "20", "name": "Theon Greyjoy", "email": "dicks@betray.com", "phone": "3334445555", "status": "BANNED", "created": "2016-07-10 22:57:17", "unix": "1468191384105" }, { "id": "21", "name": "Stephen King", "email": "steve@shining.com", "phone": "2225551111", "status": "WITHDRAWN", "created": "2016-07-12 03:42:49", "unix": "1468294848273" }, { "id": "22", "name": "Stephen Colbert", "email": "steve@america.com", "phone": "9996667777", "status": "WITHDRAWN", "created": "2016-07-12 03:42:50", "unix": "1468294848273" }, { "id": "23", "name": "Thaddius Gibralter", "email": "thad@gmail.com", "phone": "2653144660", "status": "DISMISSED", "created": "2016-07-12 03:42:50", "unix": "1468294848275" }, { "id": "24", "name": "Mei-Ling Zhou", "email": "mei@overwatch.com", "phone": "3006667777", "status": "DISMISSED", "created": "2016-07-12 03:42:50", "unix": "1468294848280" }, { "id": "25", "name": "Angela Zeigler", "email": "mercy@overwatch.com", "phone": "8008675309", "status": "APPROVED", "created": "2016-07-12 17:32:16", "unix": "1468344699228" }, { "id": "26", "name": "Lena Oxton", "email": "tracer@overwatch.com", "phone": "8003005000", "status": "BANNED", "created": "2016-07-12 03:42:50", "unix": "1468294848273" }, { "id": "27", "name": "Hana Song", "email": "D.va@overwatch.com", "phone": "7001325468", "status": "APPROVED", "created": "2016-07-12 19:16:14", "unix": "1468350535504" }, { "id": "28", "name": "Fareeha Amari", "email": "justicerains@overwatch.com", "phone": "3347008000", "status": "APPLIED", "created": "2016-07-12 21:57:18", "unix": "1468360549116" }, { "id": "29", "name": "Ana Amari", "email": "sniper@support.net", "phone": "1112223333", "status": "APPROVED", "created": "2016-07-13 01:50:13", "unix": "1468374569046" } ] // retrieve record to fill out the form $scope.readOne = function(id){ // change modal title $('#modal-volunteer-title').text("Edit Volunteer Record"); // show udpate volunteer button $('#btn-update-volunteer').show(); // show create volunteer button $('#btn-create-volunteer').hide(); // post id of volunteer to be edited $http.post('read_one.php', { 'id' : id }) .success(function(data, status, headers, config){ // put the values in form $scope.id = data[0]["id"]; $scope.name = data[0]["name"]; $scope.email = data[0]["email"]; $scope.phone = data[0]["phone"]; $scope.status = data[0]["status"]; // show modal $('#modal-volunteer-form').openModal(); }) .error(function(data, status, headers, config){ Materialize.toast('Unable to retrieve record.', 4000); }); } // END READ ONE // update volunteer record / save changes $scope.updateVolunteer = function(){ $http.post('update_volunteer.php', { 'id' : $scope.id, 'name' : $scope.name, 'email' : $scope.email, 'phone' : $scope.phone, 'status' : $scope.status }) .success(function (data, status, headers, config){ // tell the user volunteer record was updated Materialize.toast(data, 4000); // close modal $('#modal-volunteer-form').closeModal(); // clear modal content $scope.clearForm(); // refresh the volunteer list $scope.getAll(); }); } //END UPDATE // delete volunteer record $scope.deleteVolunteer = function(index){ if(confirm("Are you sure?")){ $scope.names.splice(index, 1); Materialize.toast('Volunteer record deleted', 4000); } } //END DELETE }); // END ANGULAR app.directive('phoneInput', function($filter, $browser) { return { require: 'ngModel', link: function($scope, $element, $attrs, ngModelCtrl) { var listener = function() { var value = $element.val().replace(/[^0-9]/g, ''); $element.val($filter('tel')(value, false)); }; // This runs when we update the text field ngModelCtrl.$parsers.push(function(viewValue) { return viewValue.replace(/[^0-9]/g, '').slice(0,10); }); // This runs when the model gets updated on the scope directly and keeps our view in sync ngModelCtrl.$render = function() { $element.val($filter('tel')(ngModelCtrl.$viewValue, false)); }; $element.bind('change', listener); $element.bind('keydown', function(event) { var key = event.keyCode; // If the keys include the CTRL, SHIFT, ALT, or META keys, or the arrow keys, do nothing. // This lets us support copy and paste too if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)){ return; } $browser.defer(listener); // Have to do this or changes don't get picked up properly }); $element.bind('paste cut', function() { $browser.defer(listener); }); } }; }); app.filter('tel', function () { return function (tel) { console.log(tel); if (!tel) { return ''; } var value = tel.toString().trim().replace(/^\+/, ''); if (value.match(/[^0-9]/)) { return tel; } var country, city, number; switch (value.length) { case 1: case 2: case 3: city = value; break; default: city = value.slice(0, 3); number = value.slice(3); } if(number){ if(number.length>3){ number = number.slice(0, 3) + '-' + number.slice(3,7); } else{ number = number; } return ("(" + city + ") " + number).trim(); } else{ return "(" + city; } }; }); app.config(function(paginationTemplateProvider) { paginationTemplateProvider.setString('<ul class="pagination " ng-if="1 < pages.length || !autoHide"> <li class="nopad" ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"> <a href="" ng-click="setCurrent(1)"><i class="material-icons">first_page</i></a> </li> <li class="nopad" ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"> <a href="" ng-click="setCurrent(pagination.current - 1)"><i class="material-icons">chevron_left</i></a> </li> <li ng-click="setCurrent(pageNumber)"class="waves-effect" ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' }"> <a href="" >{{ pageNumber }}</a> </li> <li class="nopad" ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"> <a href="" ng-click="setCurrent(pagination.current + 1)"><i class="material-icons">chevron_right</i></a> </li> <li class="nopad" ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)"><i class="material-icons">last_page</i></a></li></ul>'); }); // jquery codes will be here $(document).ready(function(){ // initialize modal $('.modal-trigger').leanModal(); }); /** * dirPagination - AngularJS module for paginating (almost) anything. * * * Credits * ======= * * Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ * for the idea on how to dynamically invoke the ng-repeat directive. * * I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project: * https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js * * Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk> */ (function() { /** * Config */ var moduleName = 'angularUtils.directives.dirPagination'; var DEFAULT_ID = '__default'; /** * Module */ angular.module(moduleName, []) .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective]) .directive('dirPaginateNoCompile', noCompileDirective) .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective]) .filter('itemsPerPage', ['paginationService', itemsPerPageFilter]) .service('paginationService', paginationService) .provider('paginationTemplate', paginationTemplateProvider) .run(['$templateCache',dirPaginationControlsTemplateInstaller]); function dirPaginateDirective($compile, $parse, paginationService) { return { terminal: true, multiElement: true, priority: 100, compile: dirPaginationCompileFn }; function dirPaginationCompileFn(tElement, tAttrs){ var expression = tAttrs.dirPaginate; // regex taken directly from https://github.com/angular/angular.js/blob/v1.4.x/src/ng/directive/ngRepeat.js#L339 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); var filterPattern = /\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/; if (match[2].match(filterPattern) === null) { throw 'pagination directive: the \'itemsPerPage\' filter must be set.'; } var itemsPerPageFilterRemoved = match[2].replace(filterPattern, ''); var collectionGetter = $parse(itemsPerPageFilterRemoved); addNoCompileAttributes(tElement); // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any // dir-pagination-controls directives that may be looking for this ID. var rawId = tAttrs.paginationId || DEFAULT_ID; paginationService.registerInstance(rawId); return function dirPaginationLinkFn(scope, element, attrs){ // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and // potentially register a new ID if it evaluates to a different value than the rawId. var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID; // (TODO: this seems sound, but I'm reverting as many bug reports followed it's introduction in 0.11.0. // Needs more investigation.) // In case rawId != paginationId we deregister using rawId for the sake of general cleanliness // before registering using paginationId // paginationService.deregisterInstance(rawId); paginationService.registerInstance(paginationId); var repeatExpression = getRepeatExpression(expression, paginationId); addNgRepeatToElement(element, attrs, repeatExpression); removeTemporaryAttributes(element); var compiled = $compile(element); var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId); paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope); if (typeof attrs.totalItems !== 'undefined') { paginationService.setAsyncModeTrue(paginationId); scope.$watch(function() { return $parse(attrs.totalItems)(scope); }, function (result) { if (0 <= result) { paginationService.setCollectionLength(paginationId, result); } }); } else { paginationService.setAsyncModeFalse(paginationId); scope.$watchCollection(function() { return collectionGetter(scope); }, function(collection) { if (collection) { var collectionLength = (collection instanceof Array) ? collection.length : Object.keys(collection).length; paginationService.setCollectionLength(paginationId, collectionLength); } }); } // Delegate to the link function returned by the new compilation of the ng-repeat compiled(scope); // (TODO: Reverting this due to many bug reports in v 0.11.0. Needs investigation as the // principle is sound) // When the scope is destroyed, we make sure to remove the reference to it in paginationService // so that it can be properly garbage collected // scope.$on('$destroy', function destroyDirPagination() { // paginationService.deregisterInstance(paginationId); // }); }; } /** * If a pagination id has been specified, we need to check that it is present as the second argument passed to * the itemsPerPage filter. If it is not there, we add it and return the modified expression. * * @param expression * @param paginationId * @returns {*} */ function getRepeatExpression(expression, paginationId) { var repeatExpression, idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/); if (paginationId !== DEFAULT_ID && !idDefinedInFilter) { repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/, "$1 : '" + paginationId + "'"); } else { repeatExpression = expression; } return repeatExpression; } /** * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the * appropriate multi-element ng-repeat to the first and last element in the range. * @param element * @param attrs * @param repeatExpression */ function addNgRepeatToElement(element, attrs, repeatExpression) { if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) { // using multiElement mode (dir-paginate-start, dir-paginate-end) attrs.$set('ngRepeatStart', repeatExpression); element.eq(element.length - 1).attr('ng-repeat-end', true); } else { attrs.$set('ngRepeat', repeatExpression); } } /** * Adds the dir-paginate-no-compile directive to each element in the tElement range. * @param tElement */ function addNoCompileAttributes(tElement) { angular.forEach(tElement, function(el) { if (el.nodeType === 1) { angular.element(el).attr('dir-paginate-no-compile', true); } }); } /** * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives. * @param element */ function removeTemporaryAttributes(element) { angular.forEach(element, function(el) { if (el.nodeType === 1) { angular.element(el).removeAttr('dir-paginate-no-compile'); } }); element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate'); element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end'); } /** * Creates a getter function for the current-page attribute, using the expression provided or a default value if * no current-page expression was specified. * * @param scope * @param attrs * @param paginationId * @returns {*} */ function makeCurrentPageGetterFn(scope, attrs, paginationId) { var currentPageGetter; if (attrs.currentPage) { currentPageGetter = $parse(attrs.currentPage); } else { // If the current-page attribute was not set, we'll make our own. // Replace any non-alphanumeric characters which might confuse // the $parse service and give unexpected results. // See https://github.com/michaelbromley/angularUtils/issues/233 var defaultCurrentPage = (paginationId + '__currentPage').replace(/\W/g, '_'); scope[defaultCurrentPage] = 1; currentPageGetter = $parse(defaultCurrentPage); } return currentPageGetter; } } /** * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end). * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled. */ function noCompileDirective() { return { priority: 5000, terminal: true }; } function dirPaginationControlsTemplateInstaller($templateCache) { $templateCache.put('angularUtils.directives.dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length || !autoHide"><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(1)">«</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)">‹</a></li><li ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' || ( ! autoHide && pages.length === 1 ) }"><a href="" ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.current + 1)">›</a></li><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">»</a></li></ul>'); } function dirPaginationControlsDirective(paginationService, paginationTemplate) { var numberRegex = /^\d+$/; var DDO = { restrict: 'AE', scope: { maxSize: '=?', onPageChange: '&?', paginationId: '=?', autoHide: '=?' }, link: dirPaginationControlsLinkFn }; // We need to check the paginationTemplate service to see whether a template path or // string has been specified, and add the `template` or `templateUrl` property to // the DDO as appropriate. The order of priority to decide which template to use is // (highest priority first): // 1. paginationTemplate.getString() // 2. attrs.templateUrl // 3. paginationTemplate.getPath() var templateString = paginationTemplate.getString(); if (templateString !== undefined) { DDO.template = templateString; } else { DDO.templateUrl = function(elem, attrs) { return attrs.templateUrl || paginationTemplate.getPath(); }; } return DDO; function dirPaginationControlsLinkFn(scope, element, attrs) { // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has // not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is // no corresponding dir-paginate directive and wrongly throwing an exception. var rawId = attrs.paginationId || DEFAULT_ID; var paginationId = scope.paginationId || attrs.paginationId || DEFAULT_ID; if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) { var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' '; if (window.console) { console.warn('Pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive, which was not found at link time.'); } } if (!scope.maxSize) { scope.maxSize = 9; } scope.autoHide = scope.autoHide === undefined ? true : scope.autoHide; scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true; scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false; var paginationRange = Math.max(scope.maxSize, 5); scope.pages = []; scope.pagination = { last: 1, current: 1 }; scope.range = { lower: 1, upper: 1, total: 1 }; scope.$watch('maxSize', function(val) { if (val) { paginationRange = Math.max(scope.maxSize, 5); generatePagination(); } }); scope.$watch(function() { if (paginationService.isRegistered(paginationId)) { return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId); } }, function(length) { if (0 < length) { generatePagination(); } }); scope.$watch(function() { if (paginationService.isRegistered(paginationId)) { return (paginationService.getItemsPerPage(paginationId)); } }, function(current, previous) { if (current != previous && typeof previous !== 'undefined') { goToPage(scope.pagination.current); } }); scope.$watch(function() { if (paginationService.isRegistered(paginationId)) { return paginationService.getCurrentPage(paginationId); } }, function(currentPage, previousPage) { if (currentPage != previousPage) { goToPage(currentPage); } }); scope.setCurrent = function(num) { if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) { num = parseInt(num, 10); paginationService.setCurrentPage(paginationId, num); } }; /** * Custom "track by" function which allows for duplicate "..." entries on long lists, * yet fixes the problem of wrongly-highlighted links which happens when using * "track by $index" - see https://github.com/michaelbromley/angularUtils/issues/153 * @param id * @param index * @returns {string} */ scope.tracker = function(id, index) { return id + '_' + index; }; function goToPage(num) { if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) { var oldPageNumber = scope.pagination.current; scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange); scope.pagination.current = num; updateRangeValues(); // if a callback has been set, then call it with the page number as the first argument // and the previous page number as a second argument if (scope.onPageChange) { scope.onPageChange({ newPageNumber : num, oldPageNumber : oldPageNumber }); } } } function generatePagination() { if (paginationService.isRegistered(paginationId)) { var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1; scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange); scope.pagination.current = page; scope.pagination.last = scope.pages[scope.pages.length - 1]; if (scope.pagination.last < scope.pagination.current) { scope.setCurrent(scope.pagination.last); } else { updateRangeValues(); } } } /** * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination * template to display the current page range, e.g. "showing 21 - 40 of 144 results"; */ function updateRangeValues() { if (paginationService.isRegistered(paginationId)) { var currentPage = paginationService.getCurrentPage(paginationId), itemsPerPage = paginationService.getItemsPerPage(paginationId), totalItems = paginationService.getCollectionLength(paginationId); scope.range.lower = (currentPage - 1) * itemsPerPage + 1; scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems); scope.range.total = totalItems; } } function isValidPageNumber(num) { return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last)); } } /** * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the * links used in pagination * * @param currentPage * @param rowsPerPage * @param paginationRange * @param collectionLength * @returns {Array} */ function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) { var pages = []; var totalPages = Math.ceil(collectionLength / rowsPerPage); var halfWay = Math.ceil(paginationRange / 2); var position; if (currentPage <= halfWay) { position = 'start'; } else if (totalPages - halfWay < currentPage) { position = 'end'; } else { position = 'middle'; } var ellipsesNeeded = paginationRange < totalPages; var i = 1; while (i <= totalPages && i <= paginationRange) {if (window.CP.shouldStopExecution(1)){break;} var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages); var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end')); var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start')); if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) { pages.push('...'); } else { pages.push(pageNumber); } i ++; } window.CP.exitedLoop(1); return pages; } /** * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position. * * @param i * @param currentPage * @param paginationRange * @param totalPages * @returns {*} */ function calculatePageNumber(i, currentPage, paginationRange, totalPages) { var halfWay = Math.ceil(paginationRange/2); if (i === paginationRange) { return totalPages; } else if (i === 1) { return i; } else if (paginationRange < totalPages) { if (totalPages - halfWay < currentPage) { return totalPages - paginationRange + i; } else if (halfWay < currentPage) { return currentPage - halfWay + i; } else { return i; } } else { return i; } } } /** * This filter slices the collection into pages based on the current page number and number of items per page. * @param paginationService * @returns {Function} */ function itemsPerPageFilter(paginationService) { return function(collection, itemsPerPage, paginationId) { if (typeof (paginationId) === 'undefined') { paginationId = DEFAULT_ID; } if (!paginationService.isRegistered(paginationId)) { throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.'; } var end; var start; if (angular.isObject(collection)) { itemsPerPage = parseInt(itemsPerPage) || 9999999999; if (paginationService.isAsyncMode(paginationId)) { start = 0; } else { start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage; } end = start + itemsPerPage; paginationService.setItemsPerPage(paginationId, itemsPerPage); if (collection instanceof Array) { // the array just needs to be sliced return collection.slice(start, end); } else { // in the case of an object, we need to get an array of keys, slice that, then map back to // the original object. var slicedObject = {}; angular.forEach(keys(collection).slice(start, end), function(key) { slicedObject[key] = collection[key]; }); return slicedObject; } } else { return collection; } }; } /** * Shim for the Object.keys() method which does not exist in IE < 9 * @param obj * @returns {Array} */ function keys(obj) { if (!Object.keys) { var objKeys = []; for (var i in obj) {if (window.CP.shouldStopExecution(2)){break;} if (obj.hasOwnProperty(i)) { objKeys.push(i); } } window.CP.exitedLoop(2); return objKeys; } else { return Object.keys(obj); } } /** * This service allows the various parts of the module to communicate and stay in sync. */ function paginationService() { var instances = {}; var lastRegisteredInstance; this.registerInstance = function(instanceId) { if (typeof instances[instanceId] === 'undefined') { instances[instanceId] = { asyncMode: false }; lastRegisteredInstance = instanceId; } }; this.deregisterInstance = function(instanceId) { delete instances[instanceId]; }; this.isRegistered = function(instanceId) { return (typeof instances[instanceId] !== 'undefined'); }; this.getLastInstanceId = function() { return lastRegisteredInstance; }; this.setCurrentPageParser = function(instanceId, val, scope) { instances[instanceId].currentPageParser = val; instances[instanceId].context = scope; }; this.setCurrentPage = function(instanceId, val) { instances[instanceId].currentPageParser.assign(instances[instanceId].context, val); }; this.getCurrentPage = function(instanceId) { var parser = instances[instanceId].currentPageParser; return parser ? parser(instances[instanceId].context) : 1; }; this.setItemsPerPage = function(instanceId, val) { instances[instanceId].itemsPerPage = val; }; this.getItemsPerPage = function(instanceId) { return instances[instanceId].itemsPerPage; }; this.setCollectionLength = function(instanceId, val) { instances[instanceId].collectionLength = val; }; this.getCollectionLength = function(instanceId) { return instances[instanceId].collectionLength; }; this.setAsyncModeTrue = function(instanceId) { instances[instanceId].asyncMode = true; }; this.setAsyncModeFalse = function(instanceId) { instances[instanceId].asyncMode = false; }; this.isAsyncMode = function(instanceId) { return instances[instanceId].asyncMode; }; } /** * This provider allows global configuration of the template path used by the dir-pagination-controls directive. */ function paginationTemplateProvider() { var templatePath = 'angularUtils.directives.dirPagination.template'; var templateString; /** * Set a templateUrl to be used by all instances of <dir-pagination-controls> * @param {String} path */ this.setPath = function(path) { templatePath = path; }; /** * Set a string of HTML to be used as a template by all instances * of <dir-pagination-controls>. If both a path *and* a string have been set, * the string takes precedence. * @param {String} str */ this.setString = function(str) { templateString = str; }; this.$get = function() { return { getPath: function() { return templatePath; }, getString: function() { return templateString; } }; }; } })(); //# sourceURL=pen.js </script> </body></html>

Related: See More


Questions / Comments: