How can I force ng-click to take precedence over an ng-blur event?

20,008

Solution 1

Instead of ng-click, use ng-mousedown. Mousedown events get fired before blur events.

However, the handled mousedown might un-focus your field without the blur event being fired. If you then click outside the box, the blur event won't be fired (because the field is already blurred), so after setting focus, you might need to re-focus the field manually - see How to set focus on input field?

Note that by using this approach, the button can also be triggered using right-click (thanks Alexandros Vellis!) as you can see in this official example.

Solution 2

Don't use ng-blur then, bind the $document with click event to stop edit mode. And remember to unbind the event when the scope is destroyed.

Solution 3

If you want to run click event functionality before ng-blur than write your functionality in ng-mousedown event instead ng-click.

Share:
20,008
tidelipop
Author by

tidelipop

I work as a professional webdeveloper, mainly e-commerce sites.

Updated on September 11, 2020

Comments

  • tidelipop
    tidelipop almost 4 years

    When I click the save button it triggers an ng-blur event, how can I make the buttons ng-click event to trigger instead? I still want the ng-blur event to trigger if I click outsida the button or input field.

    http://jsfiddle.net/tidelipop/wyjdT/

    angular.module('MyApp', [])
    
    .filter("placeholder", function(){
        return function (text, length, end) {
            //console.log(typeof text, text, length);
            if(typeof text === 'undefined' || text == ''){
                text = 'Click to edit...';
            }
            return text;
        };
    })
    
    .directive("inlineEdit", function($compile, $q){
        return {
            restrict: "A",
            replace: true,
            template: function(tElement, tAttrs){
                var modelStr = tAttrs.inlineEdit, optionsStr = tAttrs.options, modelXtra = '', editElStr = '';
                if(tAttrs.type === 'selectbox'){
                    modelXtra = '.value';
                    editElStr = '<select ng-show="editMode" ng-blur="endEdit(\''+modelStr+'\')" ng-model="'+modelStr+'" ng-options="a.value for a in '+optionsStr+'"></select>';
                }else if(tAttrs.type === 'textarea'){
                    editElStr = '<textarea ng-show="editMode" ng-blur="endEdit(\''+modelStr+'\')" ng-model="'+modelStr+'"></textarea>';
                }else{
                    editElStr = '<input type="text" ng-show="editMode" ng-blur="endEdit(\''+modelStr+'\')" ng-model="'+modelStr+'" />';
                }
                return '<div class="body">'+
                           '<span ng-hide="editMode" ng-click="startEdit(\''+modelStr+'\', \''+optionsStr+'\')" ng-bind="'+modelStr+modelXtra+' | placeholder"></span>'+
                           editElStr+'<button ng-show="editMode" ng-click="save()">save</button>'+
                       '</div>';
            },
            scope: true,
            controller: function($scope){
                $scope.editMode = false;
                $scope.save = function(){
                    console.log("Saving...");
                    $scope.editMode = false;
                };
                $scope.startEdit = function(modelStr, optionsStr){
                    console.log("Start edit mode...");
                    // Store original value, to be restored if not saving...
                    $scope.origValue = $scope.$eval(modelStr);
                    // If selectbox and no initial value, do init to first option
                    if(typeof $scope.origValue === 'object' && typeof $scope.origValue.value !== 'string'){
                        $scope.$eval(modelStr+'='+optionsStr+'[0]');
                    }
                    // Turn on edit mode
                    $scope.editMode = true;
                };
                $scope.endEdit = function(modelStr){
                    console.log("End edit mode...");
                    // Restore original value
                    $scope.$eval(modelStr+'=origValue');
                    // Turn off edit mode
                    $scope.editMode = false;
                };
            }
        }
    })
    
    
    .controller("UserAdminCtrl", function($scope){
        $scope.options = {};
        $scope.options.cars = [
            { "key": 0, "value": "Audi" },
            { "key": 1, "value": "BMW" },
            { "key": 2, "value": "Volvo" }
        ];
        $scope.data_copy = {
            user: {
                user_id: 'sevaxahe',
                comment: '',
                my_car: {}
            }
        };
    
    });
    
  • tidelipop
    tidelipop almost 11 years
    Doesn't work so well, since it triggers when I click inside the selectbox or when I make a selection. What do you mean "unbind the event when the scope is destroyed". When is the scope destroyed and how do I do that?
  • 6220119
    6220119 almost 11 years
    It can be destroyed by several ways, e.g. on data changed of ng-repeat, switching the ng-view, ...
  • Uli Köhler
    Uli Köhler over 9 years
    @WylieKulicuu Happy to help ;-) It indeed took me half a week to figure out the solution
  • Alexandros V
    Alexandros V almost 9 years
    @UliKöhler I liked this answer. However make a note that when doing this, the <button> elements can also be triggered with a right mouse click, which might not be desired...
  • Uli Köhler
    Uli Köhler almost 9 years
    @AlexandrosVellis Very good point indeed! I have added this to the answer. You can actually see this in the official ng-mousedown example. Are you aware of any method of solving this?
  • Uli Köhler
    Uli Köhler over 8 years
    @TheProgrammer Thanks, I will definitely do ;-)
  • Kimball Robinson
    Kimball Robinson over 8 years
    I was having a problem with a <a ng-click="...."> that wasn't firing until the second time I clicked it. This turned out to be the solution. Thanks!!
  • RaphaMex
    RaphaMex over 6 years
    Great solution! I was considering using $timeout inside ng-blur to let time for my ng-click to trigger... Saved my day :-)