This describes a provider/service for wrapping the AngularStrap $modal
service. It allows for somewhat easier programatic usage.
First, you define a modal using the provider. Modals are named, and have default configuration for the underlying $modal
service:
angular.module('app', ['app.modals']).config (modalsProvider) ->
#this modal will return true/false when modifying a user succeeds or fails
modalsProvider.defineModal 'modify-user', {
template: 'user/templates/modify-user-modal.html'
backdrop: 'static'
animation: 'am-slide-top'
}
Second, the modal is launched programmatically from other places within the app, and you can specify properties to give to modal's $scope
:
.users(ng-controller="UserListController")
.user(ng-repeat="user in users")
span.name(ng-click="modifyUser(user.id)")
angular.module('app').controller 'UserListController', ($scope, modals, userRepo) ->
#array of user objects keyed by id
$scope.users = userRepo.getAllUsers()
$scope.modifyUser = (userId) ->
# launch the modal, and pass it a user instance
result = modals.launch 'modify-user', {user: $scope.users[userId]}
result.then (res) ->
if res
console.log 'yay :)'
else
console.log 'nay :('
Third, the template for the modal that is launched just uses ng-controller
as normal, the $scope
received by the
controller will have the references passed from where it was launched:
The modal template:
.modal(ng-controller="ModifyUserController")
.modal-dialog
.modal-content
.modal-header
h1 Modify User
.modal-body
p.lead This will modify {{user.name}}
p Imagine stuff here...
.modal-footer
a.btn.btn-default(ng-click="cancel()") Cancel
a.btn.btn-primary(ng-click="confirm()") Confirm
The modal controller:
angular.module('app').controller 'ModifyUserController', ($scope) ->
# $scope.user is already present, passed from wherever the modal was launched
$scope.confirm = -> $scope.$close(true)
$scope.cancel = -> $scope.$close(false)
The provider just lets you define multple instances of modals with default configuration. When you actually launch a modal, you can override some of that config if necessary.
#this wraps the angularstrap modal service to reduce boiler plate
angular.module('app.modals').provider 'modals', ->
modalDefinitions = {}
#allow registering named instances of modals with various config
@defineModal = (name, ops) -> modalDefinitions[name] = ops
@$get = ($modal, $rootScope, $q) ->
#launch a modal requires a template, and optional scope variables
#modal scopes are isolate, and will only contain what is directly passed
#third param is any other options that would be specified via the angularstrap $modals service
launchModalInstance = (name, scopeData = {}, instanceOverrides = {}) ->
modalVars = modalDefinitions[name]
if !modalVars
throw new Error 'Attempted to launch undefined modal ['+name+']'
deferred = $q.defer()
#create isolated modal scope
modalScope = $rootScope.$new(true)
for key, val of scopeData
modalScope[key] = val
#inject custom scope method to close and resolve a promise
#with a return value
modalScope.$close = (result) ->
deferred.resolve result
modalInstance.hide()
modalScope.$destroy()
#assemble final modal vars for the angularstrap $modals service
modalVars.scope = modalScope
modalVars.show = true
for key, val of instanceOverrides
modalVars[key] = val
#instantiate/launch modal instance with the underlying AngularStrap $modal service
modalInstance = $modal modalVars
#launching a modal returns a promise that resolves with whatever data is passed from "$close"
return deferred.promise
return {
launch: launchModalInstance
}
#QUESTION: WAT? Why do I have to return here? The docs suggest that you only define a function for a provider
return @