<!doctype html>
<html ng-app="commandDemo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container">
<h1 class="page-header">Command Pattern Demo</h1>
<div ng-controller="ListCtrl">
<h2>List</h2>
<ol>
<li ng-repeat="item in list">{{item.name}}</li>
</ol>
<button ng-click="import()" class="btn btn-default">Import range from list</button>
<button ng-click="export()" class="btn btn-default">Export range from list</button>
</div>
</body>
</html>
angular.module('commandDemo', ['ui.bootstrap']);
angular.module('commandDemo').controller('ListCtrl', function($scope, $modal) {
$scope.list = [
{name: 'Asking Alexandria'},
{name: 'Atreyu'},
{name: 'Audioslave'},
{name: 'Automatic Pilot'},
{name: 'Avenged Sevenfold'},
{name: 'A Wilhelm Scream'},
{name: 'The B-52\'s'},
{name: 'Babymetal'},
{name: 'Bachman–Turner Overdrive'},
{name: 'Bad Religion'},
{name: 'Badfinger'},
{name: 'The Band'},
{name: 'Bauhaus'},
{name: 'The Beatles'},
{name: 'The Beautiful South'},
{name: 'Belle & Sebastian'},
{name: 'Between the Buried and Me'},
{name: 'Biffy Clyro'},
{name: 'Big Drill Car'}
];
$scope.export = function() {
$modal.open({
templateUrl: 'exportModal.html',
controller: 'ModalExportCtrl',
resolve: {
list: function() {
return $scope.list;
}
}
});
}
$scope.import = function() {
$modal.open({
templateUrl: 'importModal.html',
controller: 'ModalImportCtrl',
resolve: {
list: function() {
return $scope.list;
}
}
});
}
});
angular.module('commandDemo').factory('rangeManager', function() {
/**
* Return a request ready object
* @param obj range
* @return obj rangeParams, an object to be parameterized in a request
*/
function getParams(range) {
return {
limit: range.limit,
offset: range.offset
};
}
/**
* Return a default range object from list
* @param array list
* @return obj
*/
function getDefaults(list) {
return {
limit: list.length,
offset: 0,
startingIndex: 1,
endingIndex: list.length,
count: list.length,
limit: null,
isValid: null
}
};
/**
* Validate range selection fields and set errors
* @param obj range
* @return obj validatedRange, an range object object decorated with isValid, errorMessage, offset, limit
* properties derived from starting and ending indices
*/
function decorate(range) {
var startingIndex = range.startingIndex;
var endingIndex = range.endingIndex;
var count = range.count;
var validatedRange = {};
validatedRange.isValid = false;
validatedRange.limit = endingIndex - startingIndex + 1;
validatedRange.offset = startingIndex - 1;
validatedRange.errorMessage = '';
if (angular.isUndefined(startingIndex) || angular.isUndefined(endingIndex)) {
validatedRange.errorMessage = 'Please provide missing fields';
} else if (startingIndex > endingIndex) {
validatedRange.errorMessage = 'Invalid range';
} else if (startingIndex <= 0 || endingIndex <= 0) {
validatedRange.errorMessage = 'Index must be positive';
} else if (endingIndex > count) {
validatedRange.errorMessage = 'Out of range of current list';
} else {
validatedRange.isValid = true;
}
return validatedRange;
}
function processRange(range) {
return angular.extend(range, decorate(range));
}
return {
processRange: processRange,
getParams: getParams,
getDefaults: getDefaults,
}
});
angular.module('commandDemo').controller('ModalExportCtrl', function($scope, $modalInstance, rangeManager, list) {
// set defaults
$scope.range = rangeManager.getDefaults(list);
// decorate range object with validation properties when range values change
$scope.$watch('range.startingIndex', function(newVal) {
var rangeToProcess = angular.extend($scope.range, {
startingIndex: newVal
});
$scope.range = rangeManager.processRange(rangeToProcess);
});
$scope.$watch('range.endingIndex', function(newVal) {
var rangeToProcess = angular.extend($scope.range, {
endingIndex: newVal
});
$scope.range = rangeManager.processRange(rangeToProcess);
});
// grab limit and offset and perform an import operation
$scope.export = function() {
var params = rangeManager.getParams($scope.range);
// would ordinarily make a backend call
alert('Exporting with limit: ' + params.limit + ', offset: ' + params.offset);
// on success, close modal
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
});
angular.module('commandDemo').controller('ModalImportCtrl', function($scope, $modalInstance, rangeManager, list) {
// set defaults
$scope.range = rangeManager.getDefaults(list);
// decorate range object with validation properties when range values change
$scope.$watch('range.startingIndex', function(newVal) {
var rangeToProcess = angular.extend($scope.range, {
startingIndex: newVal
});
$scope.range = rangeManager.processRange(rangeToProcess);
});
$scope.$watch('range.endingIndex', function(newVal) {
var rangeToProcess = angular.extend($scope.range, {
endingIndex: newVal
});
$scope.range = rangeManager.processRange(rangeToProcess);
});
// grab limit and offset and perform an import operation
$scope.import = function() {
var params = rangeManager.getParams($scope.range);
// would ordinarily make a backend call
alert('Importing with limit: ' + params.limit + ', offset: ' + params.offset);
// on success, close modal
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
});
<div ng-class="{'has-error': !range.isValid}" class="text-center">
<div class="row">
<div class="col-md-5">
<input
required
type="number"
integer
inputmode="numeric"
class="form-control"
ng-model="range.startingIndex"
id="startingIndex"
name="startingIndex"
>
</div>
<div class="col-md-2">to</div>
<div class="col-md-5">
<input
required
type="number"
integer
inputmode="numeric"
class="form-control"
ng-model="range.endingIndex"
name="endingIndex"
/>
</div>
</div>
</div>
<div class="col-md-12">
<span ng-show="!range.isValid" class="help-block" style="color: #a94442">{{range.errorMessage}}</span>
</div>
<div class="modal-header">
<h5 class="pull-right" ng-hide="!range.isValid || addMethod === 'addSelected'">
Import
<span class="help-message">{{range.limit}}</span> records
</h5>
<h3 class="modal-title">Import from list</h3>
</div>
<div class="modal-body" ng-include="'rangeSelector.html'"></div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button data-ng-disabled="!range.isValid" ng-click="import()" class="btn btn-primary">Import</button>
</div>
<div class="modal-header">
<h5 class="pull-right" ng-hide="!range.isValid || addMethod === 'addSelected'">
Export
<span class="help-message">{{range.limit}}</span> records
</h5>
<h3 class="modal-title">Export from list</h3>
</div>
<div class="modal-body" ng-include="'rangeSelector.html'"></div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button data-ng-disabled="!range.isValid" ng-click="export()" class="btn btn-primary">Export</button>
</div>