Question

I want to execute an event on a knockout observable bound to an input. This function should be executed when the control lose focus, even without typing anything. I tried to change the event binding but it doesn't fire when the user moves away from the control without typing anything. I tried mouseout event, but that only fires when the user clicks elsewhere in the form, after losing focus - not exactly what I want. I want the even to fire as soon as the focus is moved away from the control, even with tab.

Following is the code I used for mouseout event:

<input
    type="text"
    id="txtFirstName"
    tabindex="1"
    maxlength="25"
    class="txtbox" 
    style="width: 200px;"
    data-bind="value: FirstName, 
               attr: {title: FirstNameErrorMessage },
               css: {validationFailed: !IsValidFirstName() },
               event: {mouseout: ValidateFirstName}" 
/>

this.ValidateFirstName = function () {
    self.IsValidFirstName(true);
    self.FirstNameErrorMessage('');
    if (self.FirstName() == '') {
        self.IsValidFirstName(false);
        self.FirstNameErrorMessage('First Name is required');
    }
}

Can anyone help please?

Was it helpful?

Solution

I think that there are a few approaches that you could use. A nice option would be to use KO's hasfocus binding: http://knockoutjs.com/documentation/hasfocus-binding.html.

You can bind against a boolean observable, and then subscribe to it. In the subscription, you can choose to only react when the value is now false.

Something like:

self.FirstName = ko.observable();
self.FirstName.focused = ko.observable();

self.FirstName.focused.subscribe(function(newValue) {
   if (!newValue) {
       //do validation logic here and set any validation observables as necessary
   }
});

Bind against it like:

data-bind="value: FirstName, hasfocus: FirstName.focused"

I think that this would be a good option if you want it to fire everytime a user leaves the field no matter how they leave it and regardless of whether a change was actually made.

OTHER TIPS

This worked for me:

data-bind="event: { blur: OnBlurEvent }"

I like @RPNiemeyer's answer. However, I just wanted to point out that not everything has to be done via Knockout. It's just a tool, and sometimes it's not the best tool for the job. You an always just use direct event binding like you've always done in JS, i.e.

$('#FirstName').on('blur', function () {
    // do something
});

If you need to actually interact with your view model in there, then you can simply use ko.dataFor as described in the Knockout's documentation on event delegation:

$('#FirstName').on('blur', function () {
    var data = ko.dataFor(this);
    // do something with data, i.e. data.FirstName()
});

I just had the same problem, solved it by creating a custom binding:

ko.bindingHandlers.modifyOnFocusOut = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        $(element).blur(function() {
            //Do your work
        });
    }
}

And then called it like this:

 data-bind="value: FirstName, modifyOnFocusOut: FirstName"

Have you tried event:{blur: ValidateFirstName} this event will be fired if the user click out or 'tab out' of the input.

<input
    type="text"
    id="txtFirstName"
    tabindex="1"
    maxlength="25"
    class="txtbox" 
    style="width: 200px;"
    data-bind="value: FirstName, 
               attr: {title: FirstNameErrorMessage},
               css: {validationFailed: !IsValidFirstName()},
               event: {blur: ValidateFirstName}"

Here's a JSFiddle of a working example.

Using TypeScript I solved it using 2 custom bindings, a SetFocusBinding and a OnBlur Binding... Using the SetFocusBinding I make sure the input field has focus. Using the OnBlur binding a function is called when the blur event is triggered.

module Fx.Ko.Bindings {
    export class SetFocusBinding implements KnockoutBindingHandler {
        public update(element, valueAccessor, allBindingsAccessor) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value);
            if (valueUnwrapped == undefined) {
                return;
            }
            if (valueUnwrapped)
                $(element).focus();
        }
    }
}

and ...

    module Fx.Ko.Bindings {
        export class OnBlurBinding implements KnockoutBindingHandler {
            public init(element, valueAccessor, allBindings, viewModel, bindingContext) {
                var value = valueAccessor();
                $(element).on('blur', function (event) {
                    value();
                });
            }
        }
    }
interface KnockoutBindingHandlers {
    onBlur: KnockoutBindingHandler;
}
ko.bindingHandlers.onBlur = new Fx.Ko.Bindings.OnBlurBinding();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top