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">≡</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())