Giter Club home page Giter Club logo

angular-modal's Introduction

angular-modal Build Status

A modal factory service for AngularJS that makes it easy to add modals to your app.

Install

npm install angular-modal

Usage

  1. Include the modal.js script provided by this component into your app.
  2. Optional: Include the modal.css style provided by this component into your html.
  3. Add btford.modal as a module dependency to your app.

Examples

Plunker demo

Typical Use

app.js

angular.module('myApp', ['btford.modal']).

// let's make a modal called `myModal`
factory('myModal', function (btfModal) {
  return btfModal({
    controller: 'MyModalCtrl',
    controllerAs: 'modal',
    templateUrl: 'my-modal.html'
  });
}).

// typically you'll inject the modal service into its own
// controller so that the modal can close itself
controller('MyModalCtrl', function (myModal) {
  this.closeMe = myModal.deactivate;
}).

controller('MyCtrl', function (myModal) {
  this.showModal = myModal.activate;
});

my-modal.html

<div class="btf-modal">
  <h3>Hello {{name}}</h3>
  <p><a href ng-click="modal.closeMe()">Close Me</a></p>
</div>

index.html

<div ng-app="myApp" ng-controller="MyCtrl as ctrl">
  <a href ng-click="ctrl.showModal()">Show the modal</a>
</div>

Cleaning up

If you add any listeners within the modal's controller that are outside the modal's scope, you should remove them with $scope.$on('$destroy', fn () { ... }) to avoid creating a memory leak.

Building on the example above:

app.js

// ...
controller('MyModalCtrl', function (myModal, $timeout) {

  var ctrl = this,
      timeoutId;

  ctrl.tickCount = 5;

  ctrl.closeMe = function () {
    cancelTick();
    myModal.deactivate();
  };

  function tick() {
    timeoutId = $timeout(function() {
      ctrl.tickCount -= 1;
      if (ctrl.tickCount <= 0) {
        ctrl.closeMe();
      } else {
        tick();
      }
    }, 1000);
  }

  function cancelTick() {
    $timeout.cancel(timeoutId);
  }

  $scope.$on('$destroy', cancelTick);

  tick();
}).
// ...

Inline Options

Note: The best practice is to use a separate file for the template and a separate declaration for the controller, but inlining these options might be more pragmatic for cases where the template or controller is just a couple lines.

angular.module('myApp', []).

// let's make a modal called myModal
factory('myModal', function (btfModal) {
  return btfModal({
    controller: function () {
      this.name = 'World';
    },
    controllerAs: 'ctrl',
    template: '<div class="btf-modal">Hello {{ctrl.name}}</div>'
  });
}).

controller('MyCtrl', function (myModal) {
  this.showModal = myModal.activate;
});
<div ng-app="myApp" ng-controller="MyCtrl">
  <a href ng-click="ctrl.showModal()">Show the modal</a>
</div>

API

btfModal

The modal factory. Takes a configuration object as a parameter:

var modalService = btfModal({
  /* options */
})

And returns a modalService object that you can use to show/hide the modal (described below).

The config object must either have a template or a templateUrl option.

These options work just like the route configuration in Angular's $routeProvider.

config.template

string: HTML string of the template to be used for this modal. Unless the template is very simple, you should probably use config.templateUrl instead.

config.templateUrl

string (recommended): URL to the HTML template to be used for this modal.

config.controller

string|function (optional): The name of a controller or a controller function.

config.controllerAs

string (optional, recommended): Makes the controller available on the scope of the modal as the given name.

config.container

DOM Node (optional): DOM node to prepend . Defaults to document.body.

modalService

A modalService has just two methods: activate and deactivate.

modalService.activate

Takes a hash of objects to add to the scope of the modal as locals. Adds the modal to the DOM by prepending it to the <body>. Returns a promise that resolves once the modal is active.

modalService.deactivate

Removes the modal (DOM and scope) from the DOM. Returns a promise that resolves once the modal is removed.

modalService.active

Returns whether or not the modal is currently activated.

Tests

You can run the tests with karma:

karma start karma.conf.js

License

MIT

angular-modal's People

Contributors

btford avatar francisc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

angular-modal's Issues

0.2.0 not registered with bower?

I tried to install with bower:
bower install --save angular-modal#~0.2.0

I get an error with the following additional details:
Available versions: 0.1.0, 0.0.3, 0.0.2, 0.0.1

Not too familiar with registering packages w/ bower. Is there something that needs to be done in order to register a new version with the bower registry?

reusable directive

probably a noobish question, but how would one approach a reusable directive for modals?

what i'm trying to achieve is to have a "dynamic" content and a modal that i could invoke "externally" - from other controllers


sample use case:

<modal ng:controller="alertModal">
  hi there! I'm {{ alertModal.name }}
</modal>

modal.html

<section class="modal">
  <header>
    <h5 ng:if="title">{{ title }}</h5>
    <button class="close" ng:click="close()">&times;</button>
  </header>
  <div ng:transclude></div>
</section>

directive

app.directive('modal', function () {
  return {
    restrict    : 'E',
    templateUrl : 'modal.html',
    transclude  : true,
    replace     : true,
    // probably the missing part goes here?
  }
});

$compile before $animate

I am having a weird problem where the element doesn't get compiled in time for $animate to properly add the animation classes. I have a directive within the template. If I move the $animate.enter line after $compile, then everything works as intended. I have been unable to reproduce the problem in isolation. Any ideas?

    function attach (html, locals) {
      element = angular.element(html);
      if (element.length === 0) {
        throw new Error('The template contains no elements; you need to wrap text nodes')
      }
      // original $animate
      scope = $rootScope.$new();
      if (locals) {
        for (var prop in locals) {
          scope[prop] = locals[prop];
        }
      }
      var ctrl = $controller(controller, { $scope: scope });
      if (controllerAs) {
        scope[controllerAs] = ctrl;
      }
      $compile(element)(scope);
      $animate.enter(element, container); // new $animate
    }

Not adding variables through modalService.activate

I upgraded my angular to 1.3.15 and the btfmodal to the latest and when opening a modal that has params from the parent scope it doesn't seem to be passing them.

I'm doing something like:
newModalService.activate({ id: itemId });

I believe there is an issue with the new version 0.5.0 on the attach function when doing locals.$scope = scope and $controller(controller, locals) because locals are not being added to the $scope.

Reverted to 0.4.0 and everything is fine.

Has anyone had the same problem?

Modal service "active" value not updated instantly

Hello,
I like the module so far, however I am having a little problem with its' implementation.

  1. I have a main controller, where I am watching the modal service "active" value. This watcher changes a scope value in that controller in order to reflect in the view whether this modal is open or not.

    $scope.$watch( function() { return Modal.active(); },
    function(newValue) {
    $scope.ModalActive = newValue;
    }
    );

  2. In the controller of the modal I have a function:

$scope.closeModal = function() {
Modal.deactivate();
};
3. When this function is activated the modal does close, however the watcher in the main controller does not pick up the "active" value change.

I understand this is an async problem. A quick fix could be something with $timeout, but I am sure there is a more elegant solution. Any ideas?

Plunker demo using v0.0.3

Currently at v0.0.4 but the Plunker demo is using v0.0.3. While I didn't notice any obvious difference in usability of the demo, v0.0.3 doesn't use $animate, does not destroy the scope on deactivate, and lacks the active method.

Thank you for your work on this service!

$scope is not destroyed on close

As far as I can tell, $scope for the controller isn't destroyed when closing the modal.

I might be doing something wrong, but I'm airing the issue anyway.

Attaching content to ctrl

I'm trying to attach content to ctrl before activating and the content doesn't render:

        ctrl.otherContent = 'blah'; // works
        $scope.openModal = function(content) {
            ctrl.content = content; // doesn't work
            modalService.activate();
        };

$animate.leave no longer accepts callbacks as options on angular 1.4

Angular 1.4 no longer accepts a callback as an option. (angular/angular.js#11826)
So on deactivate:

   function deactivate () {
      var deferred = $q.defer();
      if (element) {
        $animate.leave(element, function () {
          scope.$destroy();
          element = null;
          deferred.resolve();
        });
      } else {
        deferred.resolve();
      }
      return deferred.promise;
    }

should now be:

    function deactivate () {
      var deferred = $q.defer();
      if (element) {
        $animate.leave(element)
          .then(function () {
            scope.$destroy();
            element = null;
            deferred.resolve();
          });
      } else {
        deferred.resolve();
      }
      return deferred.promise;
    }

Disable animation or change scope

Hello. Can anyone show example, how to disable animation when modal window appear or how to change scope variables and re-render window? Thanks.

Not a component

The readme says this is a component eg:

<angular-modal display="$ctrl.display">
  <div>something in modal {{ $ctrl.someProp }} </div>
</angular-modal>

but this is not a component, it's a service.

Access template element from controller

I'm looking forward to access the template element from the controller:

return btfModal({
  controller: 'MyCtrl'
  template: '<div class="btf-modal">Hello {{ctrl.name}}</div>'
});

controller('MyCtrl', function ($element) {
  // $element should be equal to an element pointing to `.btf-modal`.
});

Is this currently possible somehow (from $element or the modal service, etc)?

Not compatible with angular.js v1.3.0-rc.0 sonic-boltification

$animate.leave no longer accepts a callback as a second parameter, but instead returns a promise.

In the code shown below, the anonymous function passed to $animate.leave is never executed. As a result of the element variable not being set to null, the modal is not displayed on subsequent activate calls.

function deactivate () {
  var deferred = $q.defer();
  if (element) {
    $animate.leave(element, function () {
      scope.$destroy();
      element = null;
      deferred.resolve();
    });
  } else {
    deferred.resolve();
  }
  return deferred.promise;
}

This is the commit that made the breaking change.

Deactivating the Modal does not destroy the scope

Hi,

I changed my modal service to also destroy the scope when the modal window is deactivated.

The scope that gets created when calling activate() doesn't get killed and is never garbage collected.

Can I make a pull request to fix this?

Feature: implemented for older version

I made a mod that allows for supplying two templates, the "wrapper" and the modal template. This way you can share certain root nodes/styles between different modal sets without template duplication. Would this be a desired feature that you might pull? If so, I can make it compatible with the Angular 1.3 version. Currently it only works with the older version.

See this Gist: https://gist.github.com/vance/ef8a5714fc116c8ab247

feat: built-in $animate support

Hey.

Would it be possible to provide a built in $animate support for shown / hidden modals?

with a "hard" dependency of $animate, i managed to achieve it by adding

    function attach (html, locals) {
      // ...
      $animate.addClass( element, 'shown' );
    }
    // ...
    function deactivate () {
      if (element) {
        $animate.removeClass( element, 'shown', function () {
          scope.$destroy();
          element.remove();
          element = null;
        });
      }
    }

Ideal solution would use an optional dependency, but I'm not sure how to solve that.
Or should i use a decorator?

Sorry if the request is noobish - still a fresh-comer

How to properly test controller constructed w/ angular-modal?

I was wondering if anyone had a good method of testing the controller that is instantiated by angular-modal's service.activate (when sending locals)? Is it best to grab the controller's scope from it's container? Just wondering what the best practices is around this?

Resolving variables into modal with ui.router

I am trying to use your lib in combination with state resolves of ui-router and my code looks a bit like this:

.state({
    name: 'main',
    url: '/main',
    controller: 'InputViewCtrl',
    templateUrl: 'tpl/overviewInput.html',
    resolve: {
        tasks: function(Shared, TaskService){
            return TaskService.getTasks(Shared.getMonthParameters())
        }
    }
})
.state({
    name: 'main.addtask',
    url: '/addtask',
    onEnter: function(modal, tasks){
        modal.activate(); 
    },
    onExit: function(modal){
        modal.deactivate();
    },
    resolve:{
        modal: function(btfModal){
            return btfModal({
                controller: 'ExternalTaskAddCtrl',
                controllerAs: 'modal',
                templateUrl: 'tpl/partials/externalTaskAdd.html'
            });
        }
    }
})

So as you can see, in my 'main' state I resolve the tasks variable and in my sub-state 'main.addtask' I wish to use this that variable. Now I could easily fix this with:

modal.activate({tasks : tasks});

But that loses the dependency injection in my modal and is directly inserted in the scope. Is there a way I can achieve it that looks similar to what the angular ui-bootstrap modal is doing?[1]

[1] https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state

-edit: link was broken

animate moadal

Please tell me examples of how I can animate modal window

$rootScope versus back buttoning out of modal

Our team encountered this problem:

  • we raise a welcome modal on a given page
  • user hits back button and welcome modal remains

Programmatically closing the modal registers as a dismissal, which
prevents it from opening on user's return to the page.

Our solution was to give the modal a local scope. Is there a good reason to put/have the modal in $rootScope?

Before / After hook for events before/after activate/deactivate

Throwing an idea out there:

I would like to be able to do something along the lines of:

    angular.module('MyApp')
        .factory('libraryModal', function(btfModal) {
            return btfModal({
                controller: '...',
                controllerAs: 'modal',
                templateUrl: '...',
                beforeActivate: function() {
                    ....
                },
                afterDeactivate: function() {
                    ....
                }
            });
        });

The scenario I have is when I go full screen I want to do two things:

  1. Hide scroll bars on the body of the document
  2. Hide thumb videos that display in the UI (QuickTime seems to sit on top of everything)

Because these are mostly UI changes I figured I don't want to do this in the controllers so this would be a good solution.

Thoughts?

ng-annotate required

I find that I must include ng-annotate to use this module. I got around it by requiring the minified version:
require('../../node_modules/angular-modal/modal.min');

It would be great if you could run ng-annotate on the unminified version that you provide.

Having two modals on a page overrides one config.

I have two directives that both encapsulate btfModals, each directive has a module like:

loginModalFactory = (btfModal) ->
  return btfModal(
    controller:   'loginModalController'
    controllerAs: 'modal'
    templateUrl: 'components/login/login.html'
  )

and

logOutModalFactory = (btfModal) ->
  return btfModal(
    controller:   'logOutModalController'
    controllerAs: 'modal'
    templateUrl: 'components/logOut/logOut.html'
  )

Both factories are wrapped in separate directives and modules which are then injected into my app. However, I can only see the stylings for the logOut modal whose encapsulating directive comes after the login directive. I understand that factories are singletons, so what is the recommended way to create multiple directives w/ multiple config objects?

Thank you,

Instantiating multiple times is broken in Angular 1.3.2

I noticed that the modal only shows up, or is created at all, at the first call of activate in Angular 1.3.
By that I don't mean calling activate twice in succession (which doesn't make a lot of sense), but calling it in a way that I'd expect to work:

modal.activate(); // shows up
asyncOperation().then(function() {
  modal.deactivate();
  modal.activate(); // does not show up
  otherAsyncOperation.then(modal.deactivate);
});

I first thought it might be a CSS issue on my end, but putting a test statement console.log('instantiating loading ctrl'); inside the modal's controller showed that the controller won't be instantiated again after the modal is closed the first time.

All this, however, works as expected when I use Angular 1.2.x instead.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.