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...