JSFiddle - React, Tailwind, and code Playground

by blineberry

HTML

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<ul class="list" data-bind="foreach: recipeParts">
    <li class="list-item" data-bind="css: { 'is-editable': isEditable() }">
            <div class="text">
                <span data-bind="text: qty"></span>  
                <span data-bind="text: unit"></span>
                <span data-bind="text: ingredientName"></span>
                <span data-bind="text: modifier"></span>
                <span data-bind="text: isOptional() ? '(optional)' : ''"></span>
                <span data-bind="text: isGarnish() ? '(garnish)' : ''"></span>
            </div>
            <div class="form">
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() + '][\'qty\']' }">Quantity</label>
                <input data-bind="value: qty, attr{ name: 'data[\'RecipePart\'][' + $index() + '][\'qty\']' }, hasfocus: isEditable()" />
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() + '][\'unit\']' }">Unit</label>
                <input data-bind="value: unit, attr{ name: 'data[\'RecipePart\'][' + $index() + '][\'unit\']' }" />
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() + '][\'recipe_part_id\']' }">Ingredient</label>
                <select data-bind="value: ingredientId, options: $root.ingredients, optionsText: 'name', optionsValue: 'id'"></select>
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() + '][\'modifier\']' }">Modifier</label>
                <input data-bind="value: modifier, attr{ name: 'data[\'RecipePart\'][' + $index() + '][\'modifier\']' }" />
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() + '][\'optional\']' }"><input type="checkbox" data-bind="attr{ name: 'data[\'RecipePart\'][' + $index() + '][\'optional\']' }" value="1" /> Optional?</label>
                <label data-bind="attr{ for: 'data[\'RecipePart\'][' + $index() +...

CSS

body {
    font-size: 16px;
    line-height: 1.5;
    
    padding: 1em;
}

.list {
    margin: 0;
    padding: 0;
    
    list-style: none;
}

.list-item .form {
    display: none;
}
.list-item.is-editable .form {
    display: block;
}
.list-item.is-editable .text {
    display: none;
}

JavaScript

function viewModel() {
    var vm = this;
    
    vm.ingredients = [
        {
            id: 0,
            name: 'Absinthe'
        }, 
        {
            id: 1,
            name: 'Bourbon'
        }, 
        {
            id: 2,
            name: 'Gin'
        }, 
        {
            id: 3,
            name: 'Tequila'
        }, 
        {
            id: 4,
            name: 'Vodka'
        }
    ];
    
    vm.recipePart = function (data) {
        var rp = this;
        
        var defaults = data || {};
        
        rp.qty = ko.observable(defaults.qty);
        rp.unit = ko.observable(defaults.unit);
        rp.ingredientId = ko.observable(defaults.ingredientId);
        rp.ingredientName = ko.computed(function() {
            var item = $.grep(vm.ingredients, function(e){ 
                console.log(e.id);
                console.log(rp.ingredientId());
                return e.id == rp.ingredientId();             
            })[0];

            if (typeof item !== 'undefined') {
                return item.name;
            }
            else {
                return null;
            }
        });
        rp.modifier = ko.observable(defaults.modifier);
        rp.isOptional = ko.observable(defaults.isOptional || 0);
        rp.isGarnish = ko.observable(defaults.isGarnish || 0);
        rp.isEditable = ko.observable(defaults.isEditable || true);

        rp.toggleEdit = function () {
            if (rp.isEditable()) {
                rp.isEditable(false);
            } else {
                rp.isEditable(true);
            }
        }

        //self.save = function (recipePart) {
        //    recipePart.isEditable(false);
        //};
    };

    vm.recipeParts = ko.observableArray([]);

    vm.addRecipePart = function (recipePart) {
        vm.recipeParts.push(new vm.recipePart());
    }
    vm.removeRecipePart = function (recipePart) {
        vm.recipeParts.remove(recipePart)
    };
}

ko.applyBindings(new viewModel());