JSFiddle - React, Tailwind, and code Playground

by g

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-1.3.0beta.js"></script>
<ul data-bind="foreach: items">
    <li><input type="text" data-bind="value: attemptedValue"/></li>
</ul>

<a data-bind="click: addItem">ADD ITEM</a> 
<a data-bind="click: print">PRINT</a>

JavaScript

var viewModel = { items: function(){ return []; }};

var Item = function(item) {
    var self = this;
    
    this.value = ko.observable();
    
    this.attemptedValue = ko.dependentObservable({
        read: function(){ 
            return self.value(); 
        },
        write: function(newValue) {
            var oldValue = self.value();
            if(oldValue === newValue) { return;}
            var cleanValue = newValue.trim().replace(/_/g, ' ') || oldValue;
            var allValues = ko.utils.arrayMap(viewModel.items(), function(i) { 
                var val = i.value();
                if(val != oldValue)
                    return val;
            });
            if(allValues.indexOf(cleanValue) > -1) {
                var m = cleanValue.match(/\W\d$/);
                if(m){
                    cleanAlias = cleanValue.slice(0,-2);
                }
                var index = 2;
                cleanValue = cleanValue + ' ' + index
                while(allValues.indexOf(cleanValue) > -1){
                    index++;
                    cleanValue = cleanValue.slice(0,-1) + index;
                }
            }
            self.value(cleanValue);
        }
    });

    this.attemptedValue(item.value);
};


viewModel = {
    items: ko.observableArray([
        new Item({ value: 'Name' }),
    ]),
    addItem: function() {
        this.items.push(new Item({ value: 'Name' }))
    },
    print: function(){ alert(ko.toJSON(viewModel));}
};
        
ko.applyBindings(viewModel);