99Sono
99Sono

Reputation: 3677

Angular ui-bootstrap-tpls-0.12.1 Open modal Dialog Set focus

I'm currently playing with angular, and in particular angular with bootstrap module (who can live without bootstrap nowadays?).

In any case, does anybody have a clean solution for seting focus on an input field within a new modal dialog that is popped open? This basic feature seems not to be supported out of the box. I thought I could hook up in the "opened" future - but it seems to get called to early.

So, when you open a modal instance - e.g.

$modal.open({
                "templateUrl": 'myModalContent.html',
                "controller" : 'modalInstanceCtrl',
                "size": size,
                "resolve" : {
                    "items": function () {
                        return $scope.items;
                    }
                }
            });

You then can use the modal instance with something like broadcast an even:

 modalInstance.opened.then(function(){
                $rootScope.$broadcast(myNewDirectiveBootstrapConfig.events.modalInstanceOpened
                        , modalInstance);
            });

Now the input element that I want to focus upon would be angular enhanced with a directive.

<label for="popupTitle">Title:</label>
<input type="text" class="form-control" id="popupTitle"
       ng-required="true"
       ng-model="popup.title"
       ng-minlength="1"
       newdirective-modal-focus
       >

And directive code would be something none intrusive and independent of the page logic. You would ideally only consume the event API and your non intrusive directive get called and do the focus magic.

angular.module('newDirectivesModule', [])
.constant('myNewDirectiveBootstrapConfig', {
    events: {
        modalInstanceOpened : "modalInstanceOpened"
    }
})
.directive('newdirectiveModalFocus', ['myNewDirectiveBootstrapConfig',    
function (myNewDirectiveBootstrapConfig) {
    return {
      restrict:'A', // process compile directive only on element attributes


       link: function(scope, elm, attr) {             
             scope.$watch(function(){return elm.is(":visible");}, function(value) {
                 if(elm.is(":visible")){
                     elm.focus();                         
                 }                   
             });

             scope.$on(myNewDirectiveBootstrapConfig.events.modalInstanceOpened, function(event, data) {
                 if(elm.is(":visible")){
                     elm.focus();
                 }                   
             });                                  
        }          
    };
}]);

You see in the code that i've played with different watch scenarios. The reality is , the directive code is getting called to son. The opened.then future seems to be publishing the event stating that the model is open before you actually see the modal dialog floating on the screen. The input element is told to be set on focus - but apprently to soon, if I debug it is still in the background when the focus() gets called. IT is visible, in the sense that you can do a find for in the dom of the page and in the sense that if you ask JQuery(":visible") dom element it is indeed visible. But it is still invisible to the eye.

I've seen somewhere else an approach that was totally different from mine, did not rely on events and depend on an attribute value changing from true to false. I did not like the apporach at all because it was too coupled with the business logic of the page, which I dislike and refuse as a solution.

A solution for this should rely solely on the fact that the modal dialog will only have in its body a single input element anootated with the set focus on me please directive and be as decoupled as possible from page logic.

Ideally this should be a supported feature by bootsrap angular itself, sadly it seems not to be, but since we have to - in any case - use APIs to oepn dialogs and close them, we should just be able to extend this step in a cross cuting clean manner to get the focus feature in.

Any suggestions on this?

Upvotes: 0

Views: 2026

Answers (1)

Maxim Shoustin
Maxim Shoustin

Reputation: 77904

I use different way of directive for focus post link to be sure that element is rendered (no matter modal or not):

app.directive('focusMe',
    ['$timeout',
        function ($timeout) {
            return {
                link: {
                    pre: function preLink(scope, element, attr) {
                      // ...
                    },
                    post: function postLink(scope, element, attr) {
                        $timeout(function () {
                            element[0].focus();
                        }, 0);


                    }
                }
            }
        }]);

So the input should look like:

<input type="text" 
       focus-me
       class="form-control" id="popupTitle"
       ng-required="true"
       ng-model="popup.title"
       ng-minlength="1">

Demo in Plunker

Upvotes: 3

Related Questions