Reputation: 1977
I have the following controller:
public JsonResult EquipmentSelect(string term)
{
var equipmentSearchViewModel = new EquipmentSearchViewModel
{
EquipmentList = iEquipmentRepository.FindBy(t => t.equipment_name.Contains(term)
|| t.equipment_id.Contains(term)),
};
var filteredEquipment = equipmentSearchViewModel.EquipmentList.ToList();
var sortableData = filteredEquipment.AsQueryable();
var jsonData = new
{
rows = (
from m in filteredEquipment
select new
{
equipment_id = m.equipment_id,
equipment_name = m.equipment_name
}
).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
and the follow js file:
$(function () {
$('#search').click(function () {
var searchText = $('#txtsearch').val();
getEquipment(searchText);
})
})
// View model declaration
var EquipmentViewModel = {
Profiles: ko.observableArray([])
};
// Bind the equipment
bindData = function (equipment) {
var EquipmentViewModel = {
Profiles: ko.observableArray([])
};
EquipmentViewModel.Profiles(equipment);
ko.applyBindings(EquipmentViewModel);
}
getEquipment = function (searchTerm) {
var url = 'EquipmentSelect/Equipment';
$.ajax({
url: url,
cache: false,
contentType: 'application/json',
data: '{"term": "' + searchTerm + '"}',
type: "POST",
success: function (result) {
bindData(result.rows);
},
error: function (jqXHR) {
$('#message').html(jqXHR.statusText);
}
});
}
and finally my view:
@{
ViewBag.Title = "KnockoutExample";
}
<script src="~/Scripts/knockout-2.2.1.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/koViewModel.js"></script>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<h2>Knockout Example</h2>
<div>
<fieldset>
<legend>Search</legend>
<span>Search For</span>
<input type="text" name="txtsearch" id="txtsearch" />
<input type="button" value="Submit" id="search"/>
</fieldset>
</div>
<table id="myTable" class="table table-striped table-bordered table-condensed">
<tr>
<th>Equipment ID</th>
<th>Equipment Name</th>
</tr>
<tbody data-bind="foreach: Profiles">
<tr">
<td data-bind="text: equipment_id"></td>
<td data-bind="text: equipment_name"></td>
</tr>
</tbody>
</table>
<p id="message"></p>
When I click the search button I get the results I am after. If I click it a second time, I get the same data but duplicated for each of the original count. For example, if the initial call returns 20 items, the second click returns 20 each of the twenty items. I need to clear my viewmodel somehow and repopulate each time the search button is clicked.
Upvotes: 0
Views: 5901
Reputation: 1189
With knockout, I've found it easier to make the top-most view model of the page itself, including all the state and behaviour of the page.
var PageViewModel = {
Profiles: ko.observableArray([]),
SearchTerm: '', // observable not needed, doesn't trigger any changes
Message: ko.observable(''),
GetEquipment: function () {
var self = this; // Retain scope of view model
self.Message('Searching...');
$.ajax({
url: 'EquipmentSelect/Equipment',
cache: false,
contentType: 'application/json',
data: ko.toJSON({ term: self.SearchTerm }),
type: "POST",
success: function (result) {
self.Profiles(result.rows); // Re-set entire observable array
self.Message('');
},
error: function (jqXHR) {
self.Message(jqXHR.statusText);
}
});
}
}
$(function () {
ko.applyBindings(PageViewModel);
})
Then not only do you start to get back to object oriented principles in your JavaScript code, but the view is also bound more simply to the view model. Don't even have to define a single id attribute.
@{
ViewBag.Title = "KnockoutExample";
}
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/koViewModel.js")" type="text/javascript"></script>
<link href="@Url.Content("~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<h2>Knockout Example</h2>
<div>
<fieldset>
<legend>Search</legend>
<span>Search For</span>
<div class="input-append">
<input type="text" data-bind="value: SearchTerm" />
<input type="button" value="Submit" class="btn" data-bind="click: GetEquipment" />
</div>
</fieldset>
</div>
<table class="table table-striped table-bordered table-condensed">
<tr>
<th>Equipment ID</th>
<th>Equipment Name</th>
</tr>
<tbody data-bind="foreach: Profiles">
<tr>
<td data-bind="text: equipment_id"></td>
<td data-bind="text: equipment_name"></td>
</tr>
</tbody>
</table>
<p data-bind="text: Message"></p>
There's no need for removeAll(). There's no need for jQuery style click events and id lookups if you're already using knockout bindings. And there's no need to bind your page-level view model more than once.
Upvotes: 6
Reputation: 1166
Your javascript is almost there, but needs some minor tweaks:
$(function () {
function onSearchClick() {
var searchText = $('#txtsearch').val();
getEquipment(searchText);
}
// View model declaration
var EquipmentViewModel = {
Profiles: ko.observableArray([])
};
function getEquipment (searchTerm) {
var url = 'EquipmentSelect/Equipment';
$.ajax({
url: url,
cache: false,
contentType: 'application/json',
data: '{"term": "' + searchTerm + '"}',
type: "POST",
success: function (result) {
EquipmentViewModel.Profiles(result.rows);
},
error: function (jqXHR) {
$('#message').html(jqXHR.statusText);
}
});
}
$(document).ready(function(){
$('#search').click(onSearchClick);
ko.applyBindings(EquipmentViewModel);
});
})
Upvotes: 2
Reputation: 81
Try this:
bindData.EquipmentViewModel.Profiles.removeAll() like:
getEquipment = function (searchTerm) {
var url = 'EquipmentSelect/Equipment';
bindData.EquipmentViewModel.Profiles.removeAll()
$.ajax({
url: url,
cache: false,
contentType: 'application/json',
data: '{"term": "' + searchTerm + '"}',
type: "POST",
success: function (result) {
bindData(result.rows);
},
error: function (jqXHR) {
$('#message').html(jqXHR.statusText);
}
});
}
Upvotes: 0