BeforeRemove Example

http://stackoverflow.com/questions/7426844/knockoutjs-not-applying-beforeremove-and-afteradd-on-nested-templates

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-debug.js"></script>
<select data-bind="options: options.tables, value: table"></select>
<div data-bind="template: { name: 'filterGroupTemplate', data: viewModel.filters }"></div>

<script type="text/html" id="filterGroupTemplate">
    <div class="filterGroup">
        <div class="filterGroupParams">
            Match
            <select data-bind="options: viewModel.options.joins, value: $data.join"></select>
            of the following rules:
            <button data-bind="click: addFilter">+</button>
            <button data-bind="click: addGroup">{...}</button>
            <button class="remove" data-bind="click: function() { $parent.filters.remove($data) }, visible: !meta.isRoot">x</button>
        </div>
        <div data-bind="foreach: { data: filters, beforeRemove: beforeRemove, afterAdd: afterAdd }">
            <div data-bind="template: $data.filters ? 'filterGroupTemplate' : 'filterOptions'"></div>    
        </div>
    </div>
</script> 

<script type="text/html" id="filterOptions">
    <div class="filter">
        <select data-bind="options: viewModel.options.fields, optionsText: 'label', value: field"></select>       
        <select data-bind="options: viewModel.options.modifiers, value: modifier"></select>
        <input type="text" data-bind="value: criteria" />
        <button class="remove" data-bind="click: function() { $parent.filters.remove($data) }">x</button>
    </div>
</script>    
<hr />
<h2>ViewModel JSON</h2>
<pre data-bind="text: ko.toJSON(viewModel, null, 2)"></pre>

CSS

.filter {
    margin: 5px 25px;
}
.filterGroup {
    margin: 20px;
    border: solid 1px #efefef;
    padding: 5px;
}
.remove {
    color: #f00;
    margin-left: 10px;
    text-shadow: 0px 0px 1px #000;
}
.remove:hover {
    text-shadow: 0px 0px 5px #FFF;
}
.filterGroupParams button {
    padding: 2px 5px;
}
.filterGroupParams {
    padding-left: 10px;
}

JavaScript

var beforeRemove = function(elem, x, vm) {
    if (vm.filters)
        $(elem).hide('explode');
    else
        $(elem).hide("drop", { direction: "up" });//.slideUp();
};
var afterAdd = function(elem, x, vm) {
    if (vm.filters)
        $(elem).hide().show('scale');
    else
        $(elem).hide().show("drop", { direction: "up" });
};

// filter class
var Filter = function(parent) {
    this.field = ko.observable();
    this.modifier = ko.observable();
    this.criteria = ko.observable('');
};

// filter group class
var FilterGroup = function(parent) {
    this.join = ko.observable('All');
    // Include a blank filter in every group
    this.filters = ko.observableArray([new Filter(this)]);
    
    this.addFilter = function() {
        this.filters.push(new Filter(this));
    };
    this.addGroup = function() {
        this.filters.push(new FilterGroup(this));
    };
    
    this.meta = function() {};
    this.meta.isRoot = !parent;
};

var viewModel = {
    table: ko.observable('Account'),
    filters: new FilterGroup(),
    options: {
        tables: ['Contact', 'Account'],
        joins: ['All', 'Any'],
        modifiers: [
            'equals',
            'not equal to',
            'less than',
            'greater than',
            'contains',
            'does not contain',
            'starts with'
        ],
        fields: [{"name":"Id","label":"Contact ID"},{"name":"IsDeleted","label":"Deleted"},{"name":"MasterRecordId","label":"Master Record ID"},{"name":"AccountId","label":"Account ID"},{"name":"LastName","label":"Last Name"},{"name":"FirstName","label":"First Name"},{"name":"Salutation","label":"Salutation"},{"name":"Name","label":"Full Name"},{"name":"OtherStreet","label":"Other Street"},{"name":"OtherCity","label":"Other City"},{"name":"OtherState","label":"Other State/Province"},{"name":"OtherPostalCode","label":"Other Zip/Postal Code"},{"name":"OtherCountry","label":"Other Country"},{"name":"MailingStreet","label":"Mailing...