Skip to content
This repository has been archived by the owner on Oct 2, 2019. It is now read-only.

uiSelect should expose OPEN and other events #432

Closed
ttonyh opened this issue Nov 24, 2014 · 24 comments
Closed

uiSelect should expose OPEN and other events #432

ttonyh opened this issue Nov 24, 2014 · 24 comments

Comments

@ttonyh
Copy link

ttonyh commented Nov 24, 2014

uiSelect should expose open and other events that select2 has: opening, change, close, highlight, selecting, removing, removed, loaded, focus, and blur. I understand several are already available but it would be nice to have them all accessible through a single format, e.g. broadcast/on or via attr/observe.

@syzer
Copy link

syzer commented Dec 3, 2014

+1, btw ng-blur works

@jasedwards
Copy link

+1. I have a form with a bunch of these and I don't want the data to be fetched for them until the person clicks on one. the opening event is the only way I can see to do this unless somebody knows of a different way.

@ttonyh
Copy link
Author

ttonyh commented Dec 3, 2014

I've found a pretty complex workaround using decorators:

Set up a decorator that extends uiSelect directive with a 'compile' function, passing uiSelect's existing 'link' function to the 'post' link. Then a watcher, to watch some internal properties such as $select.open & $select.focus.

    module.config( function( $provide ) {
        $provide.decorator( "uiSelectDirective", function( $delegate ) {
            var directive = $delegate[ 0 ];

            directive.compile = function compile( tElement, tAttrs ) {
                return {
                    pre: function preLink( scope, iElement, iAttrs, controller ) {
                        scope.$watchGroup( [ "$select.open", "$select.focus" ], function( val ) {
                            scope.$parent.$broadcast( "uiSelect:events", val );
                        });
                    },
                    post: directive.link
                }
            };

            return $delegate;
        });
    });

Finally, add a listener and pick out the event type sent:

    scope.$on( "uiSelect:events", function( e, events ) {
        var open = events[ 0 ],
            focus = events[ 1 ];

        if ( open ) {
            // Do something...
        }
    });

Hope this helps until the events are added into uiSelect.

( Tested on Angular v1.3.1 & uiSelect v0.8.3 )

@jasedwards
Copy link

always forget about delegates. thanks for this.

@syzer
Copy link

syzer commented Dec 4, 2014

👍
thanks!

@oslo10e
Copy link

oslo10e commented Dec 5, 2014

@syzer are you sure that ng-blur works ?
I did not succeed to use it, see http://jsfiddle.net/opeaucelle/ca8n37g7/

@syzer
Copy link

syzer commented Dec 5, 2014

$select.search is what i was looking for

@syzer
Copy link

syzer commented Dec 5, 2014

@oslo10e if u read ttonyh answer again u will get it.

@jossemarGT
Copy link

Just correct me if I am wrong, there's no out-of-the-box ng-blur support and the best approach is @ttonyh elegant snippet, isn't it?

@syzer
Copy link

syzer commented Dec 16, 2014

@jossemarGT true

@oslo10e
Copy link

oslo10e commented Dec 16, 2014

@jossemarGT I have implemented a good work around with using jQuery events
see http://jsfiddle.net/opeaucelle/o4njwsgk/

@jossemarGT
Copy link

@oslo10e thanks I'll try it, at this moment I've tried with @ttonyh 's approach watching $select.focus and the second value that it provides. But it's getting my nerve 😠

@cvharris
Copy link

I think the workaround with decorators is too much for the simplicity of reacting to these specific events. Instead, why not add an on-close callback attribute. I've modified my select.js to accept this.

In the ui-select link function, parse the attribute:

$select.onCloseCallback = $parse(attrs.onClose);

then inside the controller's close method call

ctrl.onCloseCallback($scope, {
    $model: ctrl.parserResult.modelMapper($scope, {})
});

Works as expected and is the right fix for the app my team is working on.

@dimirc dimirc added this to the 0.12.x milestone Feb 17, 2015
@captainkovalsky
Copy link

Workaround #432 (comment) doesn't work

@ghost
Copy link

ghost commented Apr 8, 2015

Anyone have any luck getting the blur event (and all the others) to work on the active text field? I'm thinking of using jQuery to bind it after it's live, and take control from there.

The snippet from Tony H worked for me, except that the events are called at points during selection that would not normally be interpreted as a .blur or .focus on a normal select.

What I'm after is using the text the user has entered as a newly created value which gets selected and blurs if the user clicks away from the active ui-select element. The select/dropdown currently remains in an active state until a selection is made.

Also, I want that when the tab key gets pressed during selection, it tabs to the next element. It currently selects the first item in the list, and leaves that item focused. (Edit: never mind, that part seems to be working fine.)

Are any of these things possible out-of-the-box?

@ghost
Copy link

ghost commented Apr 13, 2015

So, I bound .blur to the ".ui-select-search" input box, and selected the first item in the list of options each time that this blurred. The only problem came when I arrowed down to make a selection, then pressed the tab key, it still selected the first option. Any ideas?

@ghost
Copy link

ghost commented Apr 28, 2015

This is still an issue. One solution it's to use the decorators or add some additional attribute to the directive and then do some work in the link function, but really best would be to have these events in the open API layer.

@ghost
Copy link

ghost commented Apr 28, 2015

What I've elected to do is handle validation of the ui-select on form submit, which is less than ideal. Leveraging the jQuery Select2 plugin, and moving away from Angular in this specific case seems like a viable option as well.

@yvesmh
Copy link

yvesmh commented Jun 6, 2015

@ttonyh 's workaround wasn't working for me on angular 1.3.15 and ui-select 0.12.0, so I had to tweak it a little bit:

  .config(function ($provide) {
    $provide.decorator('uiSelectDirective', function ($delegate) {

      var directive = $delegate[0];
      var directiveCompile = directive.compile;

      directive.compile = function (tElement, tAttrs) {
        var link = directiveCompile.apply(this, arguments);
        return function(scope, elem, attrs) {
          link.apply(this, arguments);
          scope.$watch('$select.open', function(val) {
            scope.$parent.$broadcast('uiSelect:open', val);
          });
        };
      };

      return $delegate;

    });
  });

If you want to know when blur occurs on your controller: (note this event will also fire if user selected something)

    $scope.$on('uiSelect:open', function(evt, opened) {
      if(!opened) {
        console.log('user selected something or blur ocurred');
      }
    });

@ttonyh
Copy link
Author

ttonyh commented Aug 14, 2015

Since my last update, there's been some changes to the way ui-select sets up the directive. They now use the compile function, which is why my above code won't work. Here's an update that I just tried, and it seems to work, if you guys want to give it a shot and let me know your results:

module.config( function( $provide ) {
    $provide.decorator( "uiSelectDirective", function( $delegate ) {
        var directive = $delegate[ 0 ],
            compile = directive.compile;

        directive.compile = function compile( tElement, tAttrs ) {
            var link = compile.apply( this, arguments );

            return function( scope, element, attrs, controller ) {
                link.apply( this, arguments );

                var $select = controller[ 0 ];

                scope.$watchGroup( [ "$select.open", "$select.focus" ], function( val ) {
                    scope.$parent.$broadcast( "uiSelect:events", val );
                });
            };
        };

        return $delegate;
    });
});

This basically extends their compile function instead of the code I posted above, which overrides it.

EDIT: ( Tested on Angular v1.3.17 & uiSelect v0.12.1 )

@RavenHursT
Copy link

Well.. a lot of talk about workarounds w/ decorators. Which is what we ended up doing to achieve onOpen and onClose API functionality. If anyone's interested, here's what we did:

    $provide.decorator('uiSelectDirective', function( $delegate, $parse ) {
            var directive = $delegate[ 0],
                preCompile = directive.compile;

            directive.compile = function compile() {
                var link = preCompile.apply( this, arguments );

                return function( scope, element, attrs, controller ) {
                    link.apply( this, arguments );

                    var $select = controller[ 0 ];

                    if(attrs.uiSelectOnOpen || attrs.uiSelectOnClose) {
                        $select.onOpenCallback = $parse(attrs.uiSelectOnOpen);
                        $select.onCloseCallback = $parse(attrs.uiSelectOnClose);
                        scope.$watch('$select.open', function(newV){
                            if(newV) {
                                $select.onOpenCallback(scope, {
                                    $model: $select.parserResult.modelMapper(scope, {})
                                });
                            } else {
                                $select.onCloseCallback(scope, {
                                    $model: $select.parserResult.modelMapper(scope, {})
                                });
                            }
                        });
                    }
                };
            };

            return $delegate;
        });

Once we had that, I figured why not just make a PR to add it to UI-Select? So here's my PR: #1153

Hopefully if can make it into a 12.x release at some point @dimirc / @ProLoser ?

@kunal999
Copy link

I am trying to make this work for handling the focus event, however it does not work as expected. The $select.focus property is set and reset multiple times when making a selection. Hence, it triggers the focus event multiple times when making a selection in the list of items. I would expect focus property to be set and true until I either tab out of the control or click somewhere else in the document.
Note: I am using single select with bootstrap theme.

aaronroberson added a commit that referenced this issue Mar 15, 2016
feat(skipFocuser): add skip focusser option

Add a skipFocusser option to configure skipping the focusser after selecting an item.

Closes #869 #401 #818 #603 #432
@wesleycho wesleycho removed this from the 0.12.x milestone Mar 27, 2016
@mtucciarone
Copy link

@RavenHursT Thank you so ****ing much for this. I've spent two days trying to get the damn blur event to be available from my calling controller and FINALLY your solution works for me. Gonna +1 your PR

Den-dp added a commit to Den-dp/ui-select that referenced this issue Jul 12, 2016
Den-dp added a commit to Den-dp/ui-select that referenced this issue Jul 12, 2016
Den-dp added a commit to Den-dp/ui-select that referenced this issue Jul 12, 2016
Den-dp added a commit to Den-dp/ui-select that referenced this issue Jul 24, 2016
user378230 pushed a commit that referenced this issue Jul 30, 2016
Adds a new `uisOpenClose` directive which allows a callback to be 
defined that is called whenever the dropdown is opened or closed.

Callback is passed an isOpen parameter which is set to true if the
dropdown has been opened, otherwise false.

Closes #432, closes #1153
kboga pushed a commit to kboga/ui-select that referenced this issue Nov 29, 2016
Adds a new `uisOpenClose` directive which allows a callback to be 
defined that is called whenever the dropdown is opened or closed.

Callback is passed an isOpen parameter which is set to true if the
dropdown has been opened, otherwise false.

Closes angular-ui#432, closes angular-ui#1153
@pehrlich
Copy link

For any hapless googler. This feature is merged and works. You need 0.19 or greater. The documentation is here: https://github.com/angular-ui/ui-select/wiki/uis-open-close

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests