JSFiddle - React, Tailwind, and code Playground

HTML

<ul class="grid list-unstyled" data-bind="sortable: {
      data: goals,
      afterAdd: afterAdd,
      beforeRemove: beforeRemove,
      beforeMove: beforeMove
    }">
    <!-- ko foreach: goals -->
    <li class="row">
        <div class="col-xs-1"> <span class="move">&equiv;</span> 
        </div>
        <div class="col-xs-10">
            <input class="form-control" data-bind="value: goal_secondary,
                        attr: { name: 'goal_secondary_' + $index() }">
            <textarea rows="3" class="form-control" data-bind="value: relation_goal_secondary,
                           attr: { name: 'relation_goal_secondary_' + $index() }"></textarea>
            <div class="col-xs-1">
                <button class="btn text-danger btn-xs remove" data-bind="click: $root.removeGoal">Delete</button>
                 <button class="btn text-danger btn-xs remove" data-bind="click: $root.addGoal">Add</button>
            </div>
    </li>
    <!-- /ko -->
</ul>
        
        <button data-bind="click: toJson">Test toJSON</button>

JavaScript

/**
 * Goals
 * @param {string} goal_secondary          value of input[name=goal_secondary]
 * @param {string} relation_goal_secondary value of textarea[name=relation_goal_secondary]
 */
function Goal(goal_secondary, relation_goal_secondary) {
    var self = this;

    self.goal_secondary = goal_secondary;
    self.relation_goal_secondary = relation_goal_secondary;
}

function GoalsVM() {
    var self = this;

    // Apparently moves trigger afterAdd, but I don't want it to, so let's pretend it doesn't.
    move = false;

    self.goals = ko.observableArray([new Goal('Secondary', 'Relation')]);

    self.addGoal = function () {
        var index = self.goals().length;
        self.goals.push(new Goal('New Goal ' + index, 'Relation ' + index));
    }
    self.removeGoal = function (goal) {
        self.goals.remove(goal);
    }

    self.afterAdd = function (el) {
        // Don't do this stuff on move!
        if (el.nodeType === 1 && !move) {
            $(el).hide().slideDown();
        }
        // Reset move in case the user's next action is to add a new goal.
        move = false;
    }
    self.beforeMove = function (el) {
        // Tell afterAdd this is a move, not really an add.
        move = true;
    }
    self.beforeRemove = function (el) {
        if (el.nodeType === 1) {
            $(el).slideUp(function () {
                $(el).remove();
            })
        }
    }
    
    self.toJson = function(){
        console.log(ko.toJSON(this))
    }
}


ko.applyBindings(new GoalsVM())