Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Change Text For Selected Fields To Uppercase Using KnockoutJS?

My application owners want select text fields to be uppercase as if the caps lock is on. I am using a KnockoutJS viewmodel with observables for these fields. Is there a way I can in effect convert any user entered text to upper case?

I put a input event on the controls I wanted to change but found that although it works, the observables are not updated.

<input type="text" maxlength="80" data-bind="value: colorName, disable: $parent.isReadOnly, event: { 'input': toUpper }" />

toUpper: function (d, e) {
    if (e.target) {
        if (e.target.value) {
            e.target.value = e.target.value.toUpperCase();
        }
    }
}

I have also been thinking of putting a ucase CSS class on the controls I want to be upper case and then either on the client or server, saving those fields in upper case.

.ucase {
    text-transform: uppercase;
}
like image 415
DaveB Avatar asked Jul 21 '14 17:07

DaveB


2 Answers

You could extend your observables -

<input data-bind="value: colorName, valueUpdate:'afterkeydown'" />

ko.extenders.uppercase = function(target, option) {
    target.subscribe(function(newValue) {
       target(newValue.toUpperCase());
    });
    return target;
};

var colorName = ko.observable().extend({ uppercase: true });

fiddle example - http://jsfiddle.net/kbFwK/

Basically whenever the value changes it will convert the value of the observable to upper case.

The disadvantage here is that it actually changes the value and would store it that way as well. You could always tack on a computed property onto the observable just for display purposes as well. You could do that using a ko.computed, a custom binding handler (since it is just for presentation), or something similar. If that is more of what you are looking for let me know with a comment.

Edit

Updated with afterkeydown - http://jsfiddle.net/kbFwK/2/

like image 70
PW Kad Avatar answered Nov 13 '22 03:11

PW Kad


This is a custom binding I wrote that makes sure both the input field and the observable is upper case. It should work just like a textInput binding.

ko.bindingHandlers.textInputUpperCase = {
    init: (element, valueAccessor) => {
        const observable = valueAccessor();
        let preventDoubleUpdate = false;

        function update(newValue) {
            if (preventDoubleUpdate) {
                preventDoubleUpdate = false;
            } else {
                switch(typeof newValue) {
                    //Undefined value will be displayed as empty in the input field.
                    case 'undefined':
                        element.value = '';
                        break;
                    //String value will be converted to upper case.
                    case 'string':
                        const upperCase = newValue.toLocaleUpperCase();
                        //Check if input field matches the observable. If not the change was made directly to the observable.
                        const match = element.value.toLocaleUpperCase() === upperCase;
                        //Remember the cursor position.
                        const selectionStart = element.selectionStart;
                        const selectionEnd = element.selectionEnd;
                        //Update the input text (will move the cursor to the end of the text).
                        element.value = upperCase;
                        //Move the cursor to it's original position if the change came from the input field.
                        if (match) {
                            element.selectionStart = selectionStart;
                            element.selectionEnd = selectionEnd;
                        }
                        //Update the observable if necessary and make sure it won't do a double update.
                        if (newValue !== upperCase) {
                            preventDoubleUpdate = true;
                            observable(upperCase);
                        }
                        break;
                    default:
                        element.value = newValue;
                }
            }
        }

        //Run the update function each time the observable has been changed
        observable.subscribe(update);

        //Initiate the observable and input
        update(observable());

        //Update the observable on changes of the text in the input field
        element.addEventListener('input', event => {
            observable(event.target.value);
        });
    }
};

A small caveat. If you write to the observable it will notify other subscribers twice. First when you write to it and then when it gets upper cased.

like image 1
Conny Olsson Avatar answered Nov 13 '22 01:11

Conny Olsson