8

I want to ad a new (dialog) type to angular, so I could use it just like I use module.directive, module.filter, module.controller to register directives, filters and controllers.

I want to register my instances of dialog type this way:

module.dialog('prompt',function(dependencies){
    return {
        templateUrl:'prompt.html',
        controller:function($scope){},
        something:'value'
    }
});

I also want to be able to use registered dialogs in controllers (dependency injection)

module.controller('ListCtrl',function($scope,prompt){
    $scope.deleteItem = function(item){
        prompt('Do you want to delete this item?').then(function(result){
            if(result) item.$delete();
        });
    }
});

This boils down to the following questions:

  1. How do I extend angular's module to have module.dialog register my dialog types ?

  2. How do I make my registered dialogs injectable in to controllers etc?

Btw,

  • I know about angular-ui and angular-strap.
  • I would rather not use dialog as a service, but as a separate type (this solution is already implemented in angular-ui).
3
  • why wouldn't you just use directives? Commented May 4, 2013 at 13:46
  • Is there a reason why you'd want to do that as opposed to the bootstrap solution? Commented May 4, 2013 at 21:09
  • @zcaudate, I don't like the way bootstrap registers dialogs - you define your dialog in the controller. I wanted to separate dialogs from the controllers. Commented May 4, 2013 at 22:59

1 Answer 1

7

This is a pretty interesting question. I'll prefix my answer with an opinion: I don't think you should extend angular.module to provide a dialog method. Those methods are shortcuts into built-in Angular providers, and the Angular team adds some from time to time. Since you can get access to the functionality you're looking for without adding the dialog method, I wouldn't. That said, the code below does show you how a very basic version of this might work (it doesn't modify the Angular module prototype, just the single instance of the module).

<div ng-app="myApp">
  <div ng-controller='MainController'>
    <div>
      <button ng-click='askName()'>Ask Name</button>
      <button ng-click='askNameAgain()'>Ask Name Again</button>
      <button ng-click='askAge()'>Ask Age</button>
      <button ng-click='askFood()'>Ask Food</button>
    </div>
    <div>{{lastResponse}}</div>
  </div>
</div>
var app = angular.module('myApp', []);

// Provide some basic injectables for testing
app.constant('nameString', 'NAME');
app.constant('ageString', 'AGE');
app.constant('foodString', 'FAVORITE FOOD');

// Create the dialog provider
app.provider('dialog', function($provide, $injector) {
  var dialogs = {};

  this.register = function(name, configFn) {
    // Create a new service
    $provide.factory(name, function($window, $q) {
      dialogs[name] = function() {
        // Get data based on DI injected version of configFn
        var data = $injector.invoke(configFn);
        // faking async here since prompt is really synchronous
        var deferred = $q.defer();
        var response = $window.prompt(data.text);
        deferred.resolve(response);
        return deferred.promise;
      };
      return dialogs[name];
    });
  };

  // Injecting the service itself gives you a function that
  // allows you to access a dialog by name, much like $filter
  this.$get = function() {
    return function(name) {
      return dialogs[name];
    };
  };
});

// Providing dialog injectables via app.config
app.config(function(dialogProvider) {
  dialogProvider.register('askFood', function(foodString) {
    return { text: 'What is your ' + foodString + '?' }
  });
});

// Alternatively, shortcut to accessing the dialogProvider via app.dialog
app.dialog = function(name, configFn) {
  app.config(function(dialogProvider) {
    dialogProvider.register(name, configFn);
  });
};

app.dialog('askName', function(nameString) {
  return { text: 'What is your ' + nameString + '?' }
});

app.dialog('askAge', function(ageString) {
  return { text: 'What is your ' + ageString + '?' }
});

app.controller('MainController', 
               function($scope, askName, askAge, askFood, dialog) {
  var setLastResponse = function(result) {
    $scope.lastResponse = result;
  };

  $scope.askName = function() {
    askName().then(setLastResponse);
  };

  $scope.askNameAgain = function() {
    // get the dialog through the dialog service
    // much like how $filter works
    var theDialog = dialog('askName');
    theDialog().then(setLastResponse);
  };

  $scope.askAge = function() {
    askAge().then(setLastResponse);
  };

  $scope.askFood = function() {
    askFood().then(setLastResponse);
  };
});

Here is a working example: http://jsfiddle.net/BinaryMuse/zj4Jq/

By leveraging $injector.invoke inside of your dialogProvider.register function, you can provide the ability to use a key like controller in the data your configFn returns. Since directive works a lot like this already, you may gain a lot from checking out the AngularJS source.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.