<!DOCTYPE html>
<html>
<head>
<!-- Quick and free Bootstrap stylesheet CDN -->
<link rel="stylesheet" href="//bootswatch.com/darkly/bootstrap.css">
<style>
/*Bootstrap angular ui cursor hotfix*/
a,
td {
cursor: pointer;
}
th {
font-size: 1.2em;
}
#addData {
margin-bottom: 10px;
}
</style>
<!-- Standard CDN Javascript Libraries -->
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.16/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.13/angular-ui-router.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap-tpls.js"></script>
<!-- The library for which this example code is written -->
<script src="anbomofo.min.js"></script>
<!-- Bring all requied modal scripts -->
<script src="myController.js"></script>
<script src="myDataService.js"></script>
</head>
<body ng-app="app" ng-controller="MyController" class="container-fluid">
<h1>Angular Data Driven Modals</h1>
<p> Click on the "Add Data" button to launch the add data modal. Click on a table row to launch an edit data modal.</p>
<p> Further details, source code, and a detailed explanation can be found <a href="https://github.com/mikemeding/Angular-Bootstrap-Modal-Forms" target="_blank"> here in my github repo.</a></p>
<!-- This launches our add modal -->
<button id="addData" class="btn btn-primary" modal ng-service="MyDataService">Add Data</button>
<div class="panel panel-default">
<div class="panel-heading">
<h3>Data Table</h3></div>
<div class="panel-body">
<!--Table-->
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>Customer Name</th>
<th>Street Address</th>
<th>City</th>
<th>State</th>
<th>Active</th>
</tr>
</thead>
<tbody>
<!-- The existance of ng-model here means that the modal is an edit modal using customer as its data-->
<tr modal ng-service="MyDataService" ng-model="customer" ng-repeat="customer in customers">
<td>{{customer.customerName}}</td>
<td>{{customer.street}}</td>
<td>{{customer.city}}</td>
<td>{{customer.usState}}</td>
<td>
<input type="checkbox" ng-model="customer.active">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
(function() {
'use strict';
var app = angular.module('app');
app.service('MyDataService', ['ModalService', function(ModalService) { // bring in ModalService as a dependency (contains compiler)
// submit functions
this.addOnSubmit = function(data) {
console.log(data);
};
this.editOnSubmit = function(data) {
console.log(data);
};
var model = {
fields: [
// making display false preserves the data but does not display it
{
name: "id",
display: false
},
// what a checkbox boolean looks like
{
name: "active",
type: "checkbox",
displayName: "Active?"
},
{
name: "customerName",
displayName: "Customer Name",
placeholder: "Please enter a persons name.",
type: "text",
required: true // constraints can be added as well
},
{
name: "street",
displayName: "Street",
type: "text",
placeholder: "123 Street Rd"
},
{
name: "city",
displayName: "City",
type: "text",
placeholder: "kalamazoo"
},
// a dropdown with many options example
{
name: "usState",
displayName: "State",
type: "dropdown",
dropdownOptions: [
"AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY"
]
},
{
name: "email",
displayName: "Email",
type: "text",
placeholder: "example@gmail.com"
},
{
name: "phone",
displayName: "Phone",
type: "text",
placeholder: "(999)999-9999"
}],
// the callback functions are defined above
addModalSettings: {
title: 'Add New Person',
callback: this.addOnSubmit
},
editModalSettings: {
title: 'Edit Person',
callback: this.editOnSubmit
}
};
// you must compile and update your model
model = ModalService.compileModel(model);
// this method must be implemented to allow external access to your model
this.getModel = function() {
return model;
};
}]);
}());
(function() {
'use strict';
var app = angular.module('app', ['ui.bootstrap', 'autoModals', 'ui.router']);
app.controller('MyController', ['MyDataService', '$scope', '$http', function(ModalService, $scope, $http) {
$scope.customers = [];
// fetch our data from the customers.json
$scope.fetchCustomers = function() {
var request = {
method: 'GET',
url: 'customers.json'
};
$http(request)
.success(function(data, status, headers, config, response) {
$scope.customers = data.list;
})
.error(function(data, status, headers, config, response) {
console.error("Failed getting customers.json file");
});
};
$scope.fetchCustomers(); // on page load
}]);
}());
(function(){var a=angular.module("autoModals",[]);a.directive("modal",function(){return{restrict:"A",scope:{ngService:"@",ngModel:"="},controller:["$scope","$attrs","$injector","$modal",function(d,c,f,e){d.dataServiceName=c.ngService;d.dataService=f.get(d.dataServiceName);d.openModal=function(){function g(){e.open({template:'<div class="modal-header"> <div class="row"> <div class="col-lg-9"> <h3 class="modal-title">{{title}}</h3> </div> <div class="col-lg-3"> <h5 class="pull-right"><strong class="text-info">BLUE</strong> means required</h5> </div> </div> </div> <div class="modal-body"> <form> <!--Loop over all fields in the data model--> <div class="form-group" ng-class="{\'has-error\' : !field.valid}" ng-repeat="field in fields" ng-if="field.display"> <!--for boolean attributes--> <input ng-if="field.type == \'checkbox\'" type="checkbox" ng-model="field.value"> <!--Field Label--> <label ng-class="{\'text-info\' : field.required && field.valid, \'text-default\' : !field.required && field.valid , \'text-danger\': !field.valid}" for="{{field.name}}">{{field.displayName}} </label> <!--Error message--> <div ng-if="field.hasOwnProperty(\'errorMessage\')" class="alert alert-danger alert-dismissible fade in" role="alert"> <strong>Error!</strong> {{field.errorMessage}} </div> <!--Create an input field for text and number attributes--> <input ng-if="field.type == \'text\' || field.type == \'number\'" type="{{field.type}}" class="form-control" placeholder="{{field.placeholder}}" id="{{field.name}}" ng-model="field.value"> <!--Create a dropdown field for dropdown attributes using dropdownOptions attribute--> <select class="form-control" ng-if="field.type == \'dropdown\'" data-ng-model="field.value" data-ng-options="item for item in field.dropdownOptions"> </select> <!--for datetime picker--> <input ng-if="field.type == \'datetime\'" class="form-control" type="datetime-local" ng-model="field.value"> </div> </form> </div> <!--modal footer--> <div class="modal-footer"> <button type="submit" class="btn btn-primary" ng-click="ok(fields)">OK</button> <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button> </div>',controller:"AddModalInstanceController",size:"lg",resolve:{ngModel:function(){return d.ngModel},dataServiceName:function(){return d.dataServiceName}}})}if(typeof d.dataService.preClick==="function"){d.dataService.preClick()}function h(){setTimeout(function(){if(!d.ready){d.ready=d.dataService.modelReady();h()}else{g()}},100)}if(typeof d.dataService.modelReady==="function"){d.ready=d.dataService.modelReady();h()}else{g()}}}],link:function b(d,e,c,f){e.bind("click",function(g){d.openModal()})}}});a.controller("AddModalInstanceController",["$scope","$modalInstance","$http","$state","$injector","dataServiceName","ngModel","ModalService",function(k,f,g,b,j,i,d,c){var h=j.get(i);var e=h.getModel();k.fields=e.fields;if(d===undefined){k.settings=e.addModalSettings}else{k.settings=e.editModalSettings;k.fields=c.addValuesToFields(d,k.fields)}k.title=k.settings.title;k.cancel=function(){f.dismiss("cancel")};k.ok=function(l){var n=c.getValuesFromFields(l);var m=function(){k.fields=c.checkValidRequirements(k.fields);if(!c.checkValidFromFields(k.fields)){k.fields=l}else{if(k.settings.hasOwnProperty("callback")){k.settings.callback(n)}f.close()}};if("validate" in k.settings){k.fields=c.resetValidity(l);k.fields=k.settings.validate(k.fields);m()}else{k.fields=c.resetValidity(l);m()}};f.result.then(function(l){k.fields=c.resetModel(h.getModel())},function(l){k.fields=c.resetModel(h.getModel())})}]);a.service("ModalService",[function(){this.compileModel=function(d){var f=false;for(var c=0;c<d.fields.length;c++){var e=d.fields[c];if(e.type==="dropdown"&&!("dropdownOptions" in e)){console.error("MODAL ERROR: dropdown field ["+e[name]+"] must have dropdownOptions Array");f=true}if(!("name" in e)){console.error("MODAL ERROR: name field must exist in field ["+JSON.stringify(e)+"]");f=true}if(!("type" in e)&&e.display){console.error("MODAL ERROR: type field must exist in field ["+JSON.stringify(e)+"]");f=true}}if(f){console.error("Model compile error. Incorrectly formatted model.");return{}}else{return b(d)}};var b=function(d){var f={};for(var c=0;c<d.fields.length;c++){var e=d.fields[c];if(!("display" in e)){e.display=true}if(!("valid" in e)){e.valid=true}if(!("required" in e)){e.required=false}if(!("displayName" in e)){e.displayName=e.name}if(!("placeholder" in e)){e.placeholder="Please enter a value"}if(e.type==="dropdown"){if(!e.hasOwnProperty("value")){e.value=e.dropdownOptions[0]}}if(e.type==="checkbox"){if(!e.hasOwnProperty("value")){e.value=false}}if(e.type==="datetime"){if(!e.hasOwnProperty("value")){e.value=new Date()}}}return d};this.resetModel=function(d){for(var c=0;c<d.fields.length;c++){var e=d.fields[c];if(e.type==="dropdown"){e.value=e.dropdownOptions[0]}else{delete e.value}e.valid=true;delete e.errorMessage}return d};this.getValuesFromFields=function(d){var f={};for(var c=0;c<d.length;c++){var e=d[c];if("value" in e){f[e.name]=e.value}else{if(e.type==="checkbox"){f[e.name]=false}}}return f};this.checkValidFromFields=function(d){var e=true;for(var c=0;c<d.length;c++){var f=d[c];if(!f.valid){e=false}}return e};this.checkValidRequirements=function(d){for(var c=0;c<d.length;c++){var e=d[c];if("required" in e){if(e.required&&e.valid&&!("value" in e)){e.valid=false;e.errorMessage="This field is required"}}}return d};this.resetValidity=function(d){for(var c=0;c<d.length;c++){var e=d[c];e.valid=true;delete e.errorMessage}return d};this.addValuesToFields=function(e,d){for(var c=0;c<d.length;c++){var g=d[c];for(var f in e){if(e.hasOwnProperty(f)){if(g.name===f){g.value=e[f]}}}}return d}}])}());
{
"status": "OK",
"version": 1.0,
"list": [{
"id": 6,
"customerName": "4 Energy",
"active": true,
"street": "6637 Front Street",
"city": "North Anchorange",
"usState": "AK",
"email": "email@email.com",
"phone": "555-555-5555"
}, {
"id": 9,
"customerName": "Cara Donna",
"active": false,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 14,
"customerName": "Colony Foods",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 4,
"customerName": "Emerson",
"active": false,
"street": "6637 Front Street",
"city": "North Anchorange",
"usState": "AK",
"email": "email@email.com",
"phone": "555-555-5555"
}, {
"id": 8,
"customerName": "Magniture",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 15,
"customerName": "MBTA",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 10,
"customerName": "Ice Cream",
"active": false,
"street": "6637 Front Street",
"city": "North Anchorange",
"usState": "AK",
"email": "email@email.com",
"phone": "555-555-5555"
}, {
"id": 11,
"customerName": "Novartis",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 1,
"customerName": "OutSmart Power Systems",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 7,
"customerName": "Parker Hannifin",
"active": true,
"street": "6637 Front Street",
"city": "North Anchorange",
"usState": "AK",
"email": "email@email.com",
"phone": "555-555-5555"
}, {
"id": 5,
"customerName": "Progress Software",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 13,
"customerName": "Roche Brothers",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 2,
"customerName": "Stop and Shop",
"active": false,
"street": "6637 Front Street",
"city": "North Anchorange",
"usState": "AK",
"email": "email@email.com",
"phone": "555-555-5555"
}, {
"id": 12,
"customerName": "UTC",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}, {
"id": 16,
"customerName": "WCUPA",
"active": true,
"street": "7303 York Street",
"city": "Winchester",
"usState": "OH",
"email": "email@email.com",
"phone": "666-666-6666"
}]
}