Skip to content

Commit 7c14bed

Browse files
committed
feat(uibootstrap-modal): add basic modal service and template when using uibootstrap
Changes: (only if ui-bootstrap is selected) - add `client/components/modal` folder - modal folder contains service, markup template, and stylesheet - modal service is intended to be extended, comes with `Modal.confirm.delete()` method - admin and main page will both use `Modal.confirm.delete()` Todo: - review code for cleanliness and correctness - possibly extend the modal service to include a basic alert class? - write test for `Modal` service?
1 parent c9f80bc commit 7c14bed

16 files changed

+277
-14
lines changed

Diff for: app/templates/client/app/admin(auth)/admin(html).html

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<li class="list-group-item" ng-repeat="user in users">
77
<strong>{{user.name}}</strong><br>
88
<span class="text-muted">{{user.email}}</span>
9-
<a ng-click="delete(user)" class="trash"><span class="glyphicon glyphicon-trash pull-right"></span></a>
9+
<a ng-click="delete(<% if(filters.uibootstrap) { %>user.name + ' (' + user.email + ')', <% } %>user)" class="trash"><span class="glyphicon glyphicon-trash pull-right"></span></a>
1010
</li>
1111
</ul>
1212
</div>

Diff for: app/templates/client/app/admin(auth)/admin(jade).jade

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ div(ng-include='"components/navbar/navbar.html"')
77
strong {{user.name}}
88
br
99
span.text-muted {{user.email}}
10-
a.trash(ng-click='delete(user)')
10+
a.trash(ng-click='delete(<% if(filters.uibootstrap) { %>user.name + " (" + user.email + ")", <% } %>user)')
1111
span.glyphicon.glyphicon-trash.pull-right
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict'
22

33
angular.module '<%= scriptAppName %>'
4-
.controller 'AdminCtrl', ($scope, $http, Auth, User) ->
4+
.controller 'AdminCtrl', ($scope, $http, Auth, User<% if(filters.uibootstrap) { %>, Modal<% } %>) ->
55

66
# Use the User $resource to fetch all users
77
$scope.users = User.query()
88

9-
$scope.delete = (user) ->
9+
$scope.delete = <% if(filters.uibootstrap) { %>Modal.confirm.delete <% } %>(user) ->
1010
User.remove id: user._id
1111
angular.forEach $scope.users, (u, i) ->
1212
$scope.users.splice i, 1 if u is user
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
'use strict';
22

33
angular.module('<%= scriptAppName %>')
4-
.controller('AdminCtrl', function ($scope, $http, Auth, User) {
4+
.controller('AdminCtrl', function ($scope, $http, Auth, User<% if(filters.uibootstrap) { %>, Modal<% } %>) {
55

66
// Use the User $resource to fetch all users
77
$scope.users = User.query();
88

9-
$scope.delete = function(user) {
9+
$scope.delete = <% if(filters.uibootstrap) { %>Modal.confirm.delete(<% } %>function(user) {
1010
User.remove({ id: user._id });
1111
angular.forEach($scope.users, function(u, i) {
1212
if (u === user) {
1313
$scope.users.splice(i, 1);
1414
}
1515
});
16-
};
16+
}<% if(filters.uibootstrap) { %>)<% } %>;
1717
});

Diff for: app/templates/client/app/main/main(html).html

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ <h1>'Allo, 'Allo!</h1>
1313
<div class="col-lg-12">
1414
<h1 class="page-header">Features:</h1>
1515
<ul class="nav nav-tabs nav-stacked col-md-4 col-lg-4 col-sm-6" ng-repeat="thing in awesomeThings">
16-
<li><a href="#" tooltip="{{thing.info}}">{{thing.name}}<% if(filters.socketio) { %><button type="button" class="close" ng-click="deleteThing(thing)">&times;</button><% } %></a></li>
16+
<li><a href="#" tooltip="{{thing.info}}">{{thing.name}}<% if(filters.socketio) { %><button type="button" class="close" ng-click="deleteThing(<% if(filters.uibootstrap) { %>thing.name, <% } %>thing)">&times;</button><% } %></a></li>
1717
</ul>
1818
</div>
1919
</div><% if(filters.socketio) { %>

Diff for: app/templates/client/app/main/main(jade).jade

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ header#banner.hero-unit
1414
li
1515
a(href='#', tooltip='{{thing.info}}')
1616
| {{thing.name}}<% if(filters.socketio) { %>
17-
button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if(filters.socketio) { %>
17+
button.close(type='button', ng-click='deleteThing(<% if(filters.uibootstrap) { %>thing.name, <% } %>thing)') ×<% } %><% if(filters.socketio) { %>
1818

1919
form.thing-form
2020
label Syncs in realtime across clients

Diff for: app/templates/client/app/main/main.controller(coffee).coffee

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
angular.module '<%= scriptAppName %>'
4-
.controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) ->
4+
.controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %><% if(filters.uibootstrap && filters.mongoose) { %>, Modal<% } %>) ->
55
$scope.awesomeThings = []
66

77
$http.get('/api/things').success (awesomeThings) ->
@@ -15,7 +15,7 @@ angular.module '<%= scriptAppName %>'
1515

1616
$scope.newThing = ''
1717

18-
$scope.deleteThing = (thing) ->
18+
$scope.deleteThing = <% if(filters.uibootstrap) { %>Modal.confirm.delete <% } %>(thing) ->
1919
$http.delete '/api/things/' + thing._id<% } %><% if(filters.socketio) { %>
2020

2121
$scope.$on '$destroy', ->

Diff for: app/templates/client/app/main/main.controller(js).js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
angular.module('<%= scriptAppName %>')
4-
.controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) {
4+
.controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %><% if(filters.uibootstrap && filters.mongoose) { %>, Modal<% } %>) {
55
$scope.awesomeThings = [];
66

77
$http.get('/api/things').success(function(awesomeThings) {
@@ -17,9 +17,9 @@ angular.module('<%= scriptAppName %>')
1717
$scope.newThing = '';
1818
};
1919

20-
$scope.deleteThing = function(thing) {
20+
$scope.deleteThing = <% if(filters.uibootstrap) { %>Modal.confirm.delete(<% } %>function(thing) {
2121
$http.delete('/api/things/' + thing._id);
22-
};<% } %><% if(filters.socketio) { %>
22+
}<% if(filters.uibootstrap) { %>)<% } %>;<% } %><% if(filters.socketio) { %>
2323

2424
$scope.$on('$destroy', function () {
2525
socket.unsyncUpdates('thing');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.modal-primary .modal-header,
2+
.modal-info .modal-header,
3+
.modal-success .modal-header,
4+
.modal-warning .modal-header,
5+
.modal-danger .modal-header {
6+
color: #fff;
7+
border-radius: 5px 5px 0 0;
8+
}
9+
.modal-primary .modal-header {
10+
background: #428bca;
11+
}
12+
.modal-info .modal-header {
13+
background: #5bc0de;
14+
}
15+
.modal-success .modal-header {
16+
background: #5cb85c;
17+
}
18+
.modal-warning .modal-header {
19+
background: #f0ad4e;
20+
}
21+
.modal-danger .modal-header {
22+
background: #d9534f;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div class="modal-header">
2+
<button ng-if="modal.dismissable" type="button" ng-click="$dismiss()" class="close">&times;</button>
3+
<h4 ng-if="modal.title" ng-bind="modal.title" class="modal-title"></h4>
4+
</div>
5+
<div class="modal-body">
6+
<p ng-if="modal.text" ng-bind="modal.text"></p>
7+
<div ng-if="modal.html" ng-bind-html="modal.html"></div>
8+
</div>
9+
<div class="modal-footer">
10+
<button ng-repeat="button in modal.buttons" ng-class="button.classes" ng-click="button.click($event)" ng-bind="button.text" class="btn"></button>
11+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.modal-header
2+
button.close(ng-if='modal.dismissable', type='button', ng-click='$dismiss()') &times;
3+
h4.modal-title(ng-if='modal.title', ng-bind='modal.title')
4+
.modal-body
5+
p(ng-if='modal.text', ng-bind='modal.text')
6+
div(ng-if='modal.html', ng-bind-html='modal.html')
7+
.modal-footer
8+
button.btn(ng-repeat='button in modal.buttons', ng-class='button.classes', ng-click='button.click($event)', ng-bind='button.text')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.modal-primary,
2+
.modal-info,
3+
.modal-success,
4+
.modal-warning,
5+
.modal-danger {
6+
.modal-header {
7+
color: #fff;
8+
border-radius: 5px 5px 0 0;
9+
}
10+
}
11+
.modal-primary .modal-header {
12+
background: @brand-primary;
13+
}
14+
.modal-info .modal-header {
15+
background: @brand-info;
16+
}
17+
.modal-success .modal-header {
18+
background: @brand-success;
19+
}
20+
.modal-warning .modal-header {
21+
background: @brand-warning;
22+
}
23+
.modal-danger .modal-header {
24+
background: @brand-danger;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.modal-primary,
2+
.modal-info,
3+
.modal-success,
4+
.modal-warning,
5+
.modal-danger {
6+
.modal-header {
7+
color: #fff;
8+
border-radius: 5px 5px 0 0;
9+
}
10+
}
11+
.modal-primary .modal-header {
12+
background: $brand-primary;
13+
}
14+
.modal-info .modal-header {
15+
background: $brand-info;
16+
}
17+
.modal-success .modal-header {
18+
background: $brand-success;
19+
}
20+
.modal-warning .modal-header {
21+
background: $brand-warning;
22+
}
23+
.modal-danger .modal-header {
24+
background: $brand-danger;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.modal-primary
2+
.modal-info
3+
.modal-success
4+
.modal-warning
5+
.modal-danger
6+
.modal-header
7+
color #fff
8+
border-radius 5px 5px 0 0
9+
10+
.modal-primary .modal-header
11+
background #428bca
12+
13+
.modal-info .modal-header
14+
background #5bc0de
15+
16+
.modal-success .modal-header
17+
background #5cb85c
18+
19+
.modal-warning .modal-header
20+
background #f0ad4e
21+
22+
.modal-danger .modal-header
23+
background #d9534f
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict'
2+
3+
angular.module '<%= scriptAppName %>'
4+
.factory 'Modal', ($rootScope, $modal) ->
5+
6+
###
7+
Opens a modal
8+
@param {Object} scope - an object to be merged with modal's scope
9+
@param {String} modalClass - (optional) class(es) to be applied to the modal
10+
@return {Object} - the instance $modal.open() returns
11+
###
12+
openModal = (scope, modalClass) ->
13+
modalScope = $rootScope.$new()
14+
scope = scope or {}
15+
modalClass = modalClass or 'modal-default'
16+
angular.extend modalScope, scope
17+
$modal.open
18+
templateUrl: 'components/modal/modal.html'
19+
windowClass: modalClass
20+
scope: modalScope
21+
22+
23+
# Public API here
24+
25+
# Confirmation modals
26+
confirm:
27+
28+
###
29+
Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
30+
@param {Function} del - callback, ran when delete is confirmed
31+
@return {Function} - the function to open the modal (ex. myModalFn)
32+
###
33+
delete: (del) ->
34+
del = del or angular.noop
35+
36+
###
37+
Open a delete confirmation modal
38+
@param {String} name - name or info to show on modal
39+
@param {All} - any additional args are passed staight to del callback
40+
###
41+
->
42+
args = Array::slice.call arguments
43+
name = args.shift()
44+
deleteModal = undefined
45+
deleteModal = openModal(
46+
modal:
47+
dismissable: true
48+
title: 'Confirm Delete'
49+
html: '<p>Are you sure you want to delete <strong>' + name + '</strong> ?</p>'
50+
buttons: [
51+
{
52+
classes: 'btn-danger'
53+
text: 'Delete'
54+
click: (e) ->
55+
deleteModal.close e
56+
return
57+
}
58+
{
59+
classes: 'btn-default'
60+
text: 'Cancel'
61+
click: (e) ->
62+
deleteModal.dismiss e
63+
return
64+
}
65+
]
66+
, 'modal-danger')
67+
deleteModal.result.then (event) ->
68+
del.apply event, args
69+
return
70+
71+
return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
angular.module('<%= scriptAppName %>')
4+
.factory('Modal', function ($rootScope, $modal) {
5+
/**
6+
* Opens a modal
7+
* @param {Object} scope - an object to be merged with modal's scope
8+
* @param {String} modalClass - (optional) class(es) to be applied to the modal
9+
* @return {Object} - the instance $modal.open() returns
10+
*/
11+
function openModal(scope, modalClass) {
12+
var modalScope = $rootScope.$new();
13+
scope = scope || {};
14+
modalClass = modalClass || 'modal-default';
15+
16+
angular.extend(modalScope, scope);
17+
18+
return $modal.open({
19+
templateUrl: 'components/modal/modal.html',
20+
windowClass: modalClass,
21+
scope: modalScope
22+
});
23+
}
24+
25+
// Public API here
26+
return {
27+
28+
/* Confirmation modals */
29+
confirm: {
30+
31+
/**
32+
* Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
33+
* @param {Function} del - callback, ran when delete is confirmed
34+
* @return {Function} - the function to open the modal (ex. myModalFn)
35+
*/
36+
delete: function(del) {
37+
del = del || angular.noop;
38+
39+
/**
40+
* Open a delete confirmation modal
41+
* @param {String} name - name or info to show on modal
42+
* @param {All} - any additional args are passed staight to del callback
43+
*/
44+
return function() {
45+
var args = Array.prototype.slice.call(arguments),
46+
name = args.shift(),
47+
deleteModal;
48+
49+
deleteModal = openModal({
50+
modal: {
51+
dismissable: true,
52+
title: 'Confirm Delete',
53+
html: '<p>Are you sure you want to delete <strong>' + name + '</strong> ?</p>',
54+
buttons: [{
55+
classes: 'btn-danger',
56+
text: 'Delete',
57+
click: function(e) {
58+
deleteModal.close(e);
59+
}
60+
}, {
61+
classes: 'btn-default',
62+
text: 'Cancel',
63+
click: function(e) {
64+
deleteModal.dismiss(e);
65+
}
66+
}]
67+
}
68+
}, 'modal-danger');
69+
70+
deleteModal.result.then(function(event) {
71+
del.apply(event, args);
72+
});
73+
};
74+
}
75+
}
76+
};
77+
});

0 commit comments

Comments
 (0)