Skip to content

Instantly share code, notes, and snippets.

@evillemez
Created August 2, 2014 17:18
Show Gist options
  • Save evillemez/ef89eb7be58de098d6a0 to your computer and use it in GitHub Desktop.
Save evillemez/ef89eb7be58de098d6a0 to your computer and use it in GitHub Desktop.

AngularStrap Modal Wrapper

This describes a provider/service for wrapping the AngularStrap $modal service. It allows for somewhat easier programatic usage.

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

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 @
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment