I have updated the code, but am still running into problems... I think I'm lost in spaghetti code at the moment.
My financial periods controller:
public ActionResult GetChartImage(bool checkedCash, bool checkedPrepaidExpenses)
{
var sql = String.Format("select * from FinancialPeriods");
var chartData = db.FinancialPeriods.SqlQuery(sql);
var myChart = new Chart(width: 600, height: 400);
myChart.AddSeries(name: "Prepaid Expenses", chartType: "Line", xValue: chartData, xField: "Year", yValues: chartData, yFields: "PrepaidExpenses");
/*
if (checkedCash)
{
myChart.AddSeries(name: "Cash", chartType: "Line", xValue: chartData, xField: "Year", yValues: chartData, yFields: "Cash").AddLegend("Legend");
}
*/
return File(myChart.ToWebImage().GetBytes(), "image/bytes");
}
//bool checkedCash, bool checkedPrepaidExpenses
public ActionResult Chart(bool checkedCash, bool checkedExpenses)
{
ViewBag.CheckedCash = checkedCash;
ViewBag.CheckedExpenses = checkedExpenses;
var sql = String.Format("select * from FinancialPeriods");
var chartData = db.FinancialPeriods.SqlQuery(sql);
var myChart = new Chart(width: 600, height: 400);
if (checkedCash)
{
myChart.AddSeries(name: "Cash", chartType: "Line", xValue: chartData, xField: "Year", yValues: chartData, yFields: "Cash").AddLegend("Legend");
}
return PartialView("_Chart"); // returns a partial view of the chart based on the model
}
My INDEX View:
<tr><td><input type="submit" class="btn btn-default" id="updatechart" value="Update Chart"></td></tr>
</table>
</p>
</div>
}
<div id="graphRight"> #Html.Action("GetChartImage", new { checkedCash = false, checkedPrepaidExpenses = false })</div>
</div>
<script src ="/assets/plugins/jquery-1.8.3.min.js" type="text/javascript">
var url = '#Url.Action("Chart")';
var imgcontainer = $('graphRight');
$('#updatechart').click(function () {
var isCash = $('#checkedCash').is(':checked');
var isExpenses = $('#checkedPrepaidExpenses').is(':checked');
imgcontainer.load(url, { checkedCash: isCash, checkedPrepaidExpenses: isExpenses });
});
</script>
_Chart.cshtml (shared)
#model IEnumerable<FinancialAnalysisSite.Models.FinancialPeriod>
<img src="#Url.Action("Chart", new { checkedCash = ViewBag.CheckedCash, checkedPrepaidExpenses = ViewBag.checkedPrepaidExpenses })" />
I have updated the code, but am still running into problems... I think I'm lost in spaghetti code at the moment.
You can use ajax to call a controller method that returns a partial view of the updated chart. Assuming you have a controller method
public ActionResult Chart(bool checkedCash, bool checkedExpenses)
{
ViewBag.CheckedCash = checkedCash;
ViewBag.CheckedExpenses = checkedExpenses;
return PartialView("_Chart"); // returns a partial view of the chart based on the model
}
and the _Chart.cshtml view
<img src="#Url.Action("GetChartImage", new { checkedCash = ViewBag.CheckedCash, checkedPrepaidExpenses = checkedExpenses })"/>
which in turn calls your GetChartImage() to generate the chart. Note the return type of your method should be
return File(chart, "image/bytes");
Then in the view, include a button to update the chart
<button type="button" id="updatechart">Update chart</button>
and include a script to handle the buttons .click() event and update the DOM using the jquery .load() method.
var url = '#Url.Action("Chart")';
var imgcontainer = $('graphRight');
$('#updatechart').click(function() {
var isCash = $('#checkedCash').is(':checked');
var isExpenses = $('#checkedExpenses').is(':checked');
imgcontainer.load(url, { checkedCash: isCash, checkedExpenses: isExpenses });
});
Note the code for generating the initial image could then use the same method using #Html.Action() (passing in whatever default values are necessary)
<div id="graphRight">#Html.Action("Chart", new { checkedCash = false, checkedExpenses = false })</div>
Related
I have c# classes that need to be passed into a knockout view model for look up purposes. Show a user status of 1000 or a role of 14038 is useless.
I need to be able to resolve those values to their text representation.
I've got some "reference data" populated in a database. On top of that are some T4 transformations which translate the reference data into C# code, for example:
public static class UserStatus
{
#region Members
public const string ClassName = "UserStatus";
public const int Pending = 1000;
public const int Active = 1001;
public const int Inactive = 1002;
public const int Deleted = 1003;
#endregion
}
This class is then used throughout the code to assign values to a User class, instead of saying user.UserStatus = 1000 it's user.UserStatus = UserStatus.Pending.
Now onto the issue...
CURRENT ISSUE
I've got a page that lists users in the system and one of the columns in the list is the user's status. Well that status passed from in the object is 1000 not "Pending". What I'd like to do is be able to resole 1000 to "Pending" in knockout. Problem is because knockout is executed on the client side it has no knowledge of my C# classes. Ideally I'd like to be able to pre-poplate a list in my controller with the possible values of a UserStatus, pass that into my knockout view model and have it loop through the possible statuses and resolve it based on that specific users status.
HTML Code
<tbody style="display: none;" class="table-body" data-bind="visible: true, foreach: { data: viewData.ClientGroups, as: 'ClientGroup' }">
<tr>
<td><span data-bind="html: ClientGroup.Name()"></span></td>
<td>TODO: Bind Client</td>
<td><span data-bind="html: ClientGroup.StatusText()"></span></td>
<td><span data-bind="html: ClientGroup.CreatedOnText()"></span></td>
</tr>
</tbody>
Creating and binding the knockout view model.
var viewData = {};
require(['main'], function () {
require(['message', 'viewModel/clientGroupViewModel', 'viewModel/clientGroupDetailsViewModel'],
function (message, clientGroupViewModel, clientGroupDetailsViewModel) {
$(document).ready(function () {
// enable ko punches
ko.punches.enableAll();
var json = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model, Newtonsoft.Json.Formatting.None, new IHtmlStringConverter()));
// I'd like to be able to create something here and pass it to my model.
var lookupValues = { blah }
viewData = new clientGroupViewModel(json, lookupValues);
var zdm = new clientGroupDetailsViewModel(json, lookupValues );
ko.applyBindings(viewData, document.getElementById('clientGroupAdmin'));
ko.applyBindings(zdm, document.getElementById('detailsModal'));
});
});
});
In the knockout view model
_self.StatusText = ko.computed(function () {
console.log('user status');
if (ko.utils.arrayFirst(UserStatus, function (value) {
console.log(value);
return value.Id == _self.UserStatus();
}));
return 'false';
});
Please read my extra info questions but this is one way how I'd go about it..
console.clear();
var $fakeAsync = function (api, result) {
var isEmptyCall = _.isNull(api) || api.length === 0;
dfd = $.Deferred(function () {
setTimeout(function () {
dfd.resolve(isEmptyCall ? null : result);
}, isEmptyCall ? 0 : 50);
});
return dfd.promise();
};
var $fakeData = [
{Id: 1, Name: 'foo', UserStatus: 1000},
{Id: 2, Name: 'bar', UserStatus: 1001},
{Id: 3, Name: 'stack', UserStatus: 1000},
{Id: 4, Name: 'overflow', UserStatus: 1002}
]
var $fakeUserStates = {
1000: 'pending',
1001: 'ready',
1002: 'cancelled',
1003: 'banned'
};
function MyViewModel() {
var self = this;
self.userStates = [];
self.loadMapping = function () {
return $fakeAsync('api/fakemappingcall', $fakeUserStates).done(function (result) {
console.log('mapping gathered');
self.userStates = result;
});
};
self.loadData = function () {
return $fakeAsync('api/fakedatacall', $fakeData);
};
self.method = function () {
self.loadData().done(function (result){
_.each(result, function (r) { _.extend(r, { 'UserStatusText': self.userStates[r['UserStatus']] }) });
console.log(result);
});
};
self.loadMapping().done(function () {
self.method();
});
};
ko.applyBindings(new MyViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
I am having an issue with the angular ui bootstrap datepicker popup (https://angular-ui.github.io/bootstrap/) where I cannot get my model to update.
I have in place 2 different calendars - one with the popup and one without (uib-datepicker-popup and uib-datepicker) inside one of my angular component.
This is my code:
function headerCtrl () {
const self = this;
self.$onInit = () => {
self.dateOptions = {
showWeeks: false,
formatYear: 'yyyy',
startingDay: 1
};
self.today = function() {
self.calendar_date2 = new Date();
};
self.today();
self.clear = function() {
self.calendar_date2 = null;
};
self.inlineOptions = {
customClass: getDayClass,
minDate: new Date(),
showWeeks: true
};
self.toggleMin = function() {
self.inlineOptions.minDate = self.inlineOptions.minDate ? null : new Date();
self.dateOptions.minDate = self.inlineOptions.minDate;
};
self.toggleMin();
self.open = function() {
self.popup.opened = true;
};
self.setDate = function(year, month, day) {
self.calendar_date2 = new Date(year, month, day);
};
self.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
self.format = self.formats[0];
self.altInputFormats = ['M!/d!/yyyy'];
self.popup = {
opened: false
};
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
var afterTomorrow = new Date();
afterTomorrow.setDate(tomorrow.getDate() + 1);
self.events = [
{
date: tomorrow,
status: 'full'
},
{
date: afterTomorrow,
status: 'partially'
}
];
function getDayClass(data) {
var date = data.date,
mode = data.mode;
if (mode === 'day') {
var dayToCheck = new Date(date).setHours(0,0,0,0);
for (var i = 0; i < self.events.length; i++) {
var currentDay = new Date(self.events[i].date).setHours(0,0,0,0);
if (dayToCheck === currentDay) {
return self.events[i].status;
}
}
}
}
self.changeCalendarDate = () => {
console.log('changeCalendarDate');
console.log(self.calendar_date);
};
self.changeCalendarDate2 = () => {
console.log('changeCalendarDate2');
console.log(self.calendar_date2);
};
}}
export default {
bindings: {
duration: "<",
zoom: "<",
selection: "<",
selections: "<",
calendar_date: "<",
onDurationChange: "&",
onCalendarDateChange: "&",
onHeatmapSelectionChange: "&"
},
controller: headerCtrl,
template: `
<div class="pp-header-container">
<div uib-datepicker
ng-model="$ctrl.calendar_date"
datepicker-options="$ctrl.dateOptions"
class="heatmap-header pp-sch-header-item"
ng-change="$ctrl.changeCalendarDate()"></div>
<div class="pp-header-calendar">
<input type="text"
uib-datepicker-popup="{{$ctrl.format}}"
ng-model="$ctrl.calendar_date2"
is-open="$ctrl.popup.opened"
datepicker-options="$ctrl.dateOptions"
ng-required="true"
close-text="Close"
alt-input-formats="$ctrl.altInputFormats"
maxlength="10"
size="10"
ng-change="$ctrl.changeCalendarDate2()" />
<button type="button" class="btn btn-default" ng-click="$ctrl.open()"><i class="glyphicon glyphicon-calendar"></i></button>
</div>
</div>`
}
http://prnt.sc/esq9c9
The calendar on the left (uib-datepicker) works, as soon as I pick a date, it triggers changeCalendarDate() and it prints the selected date (console.log(self.calendar_date);)
Now what I am trying to do is change calendar as I would like the one with the popup ( uib-datepicker-popup ) which does trigger changeCalendarDate2() but when I print the value (console.log(self.calendar_date2);) it is undefined.
I am sure I need to grab that value differently but I don't know how.
Can anyone help?
Thanks :)
I have solved this problem :D
The problem was this
uib-datepicker-popup="{{$ctrl.format}}"
as soon as I have removed thee value assigned and left only
uib-datepicker-popup
it worked. I don't know why but I am happier now :)
I have this PartialView, which is loaded from the Layout of an MVC4 application.
On a button click in the main navigation menu the method SearchCustomers is called in an ajax post (shown below). Everything seems to work. Fiddler shows Data is coming back as supposed however the grid is not visible in the Popup. I wonder what am I doing wrong?
The Partial View
#model Invoice.Web.ViewModels.SearchCustomerWindowVM
<h2>Search Results</h2>
<div id="resultsGrid">
#{
if (Model.CustomersList != null)
{
var grid = new WebGrid(Model.CustomersList, rowsPerPage: 6, ajaxUpdateContainerId:"searchResults ");
#grid.GetHtml(
fillEmptyRows: true,
alternatingRowStyle: "alternate-row",
headerStyle: "grid-header",
footerStyle: "grid-footer",
mode: WebGridPagerModes.All,
firstText: "<< First",
previousText: "< Prev",
nextText: "Next >",
lastText: "Last >>",
columns: new [] {
grid.Column("Forename", canSort: false),
grid.Column("Surname"),
grid.Column("PostCode"),
grid.Column("",
header: "Actions",
format: #<text>
#Html.ActionLink("Edit", "Edit", new { id=item.CustomerID} )
|
#Html.ActionLink("Delete", "Delete", new { id=item.CustomerID} )
</text>
)
}
)
}
}
The Ajax Post - I guess the problem is here!!
<script>
$( "#searchCustomers" ).dialog({
autoOpen: false,
height: 350,
width: 700,
modal: true
});
$("#searchButton")
.button()
.click(function() {
$("#searchCustomers").dialog("open");
});
function searchCustomers() {
var forename = $("#Forename").val();
var surname = $("#Surname").val();
var postCode = $("#PostCode").val();
debugger;
var request = {
foreName: forename,
surName: surname,
postCode: postCode
};
$.ajax({
type: "POST",
url: "/Customer/SearchCustomers",
data: JSON.stringify(request),
datatype: "JSONP",
contentType: "application/json; charset=utf-8",
success: function (returndata) {
// if (returndata.ok) {
//$.post(data.Url, function(partial) {
// $('#IdOfDivToUpdate').html(partial);
$("#searchCustomers").dialog("open");
//alert("The File Has Been Downloaded.");
$('#resultsGrid').html(returndata);
//} else {
// window.alert('Error Saving Authorisation.');
//}
}
}
);
}
</script>
The Controller method:
public ActionResult SearchCustomers(string postCode, string surName, string foreName)
{
var model = new SearchCustomerWindowVM();
var modelList = new List<SearchCustomerWindowVM>();
var customersList = _customerRepository.GetAllCustomers().ToList();
foreach (var cust in customersList)
{
model.Forename = cust.FirstName;
model.Surname = cust.Surname;
model.PostCode = cust.ContactDetails.PostCode;
modelList.Add(model);
}
return Json(modelList);
// return Json(new {error = true, message = "all good."});
}
As you see I have tried other approaches in the Ajax post, which I have commented out.
Thanks in advance.
on your controller change the return to partial view
return PartialView("_PartialName", model);
then in the success of your ajax call
$('#searchCustomers').html(result);
$("#searchCustomers").dialog("open");
this way you load the div with the partial view and then open the dialog
I'm attempting to filter my results based on a drop down selected value. All the filtering and everything is working, I'm just struggling to get my view to update with the results. I'm leaving out some brackets and other irrelevant code Here is what I have:
public ViewResult Index()
{
-- this effectively returns all Invoices no matter what date --
var data = new UserBAL().GetInvoice(date);
return View(data);
}
My Jquery and Ajax is :
$(document).ready(function () {
$("[name='DDLItems']").change(function () {
var selection = $("[name='DDLItems']").val();
var dataToSend = {
//variable to hold selection?
idDate: selection
};
$.ajax({
type: "POST",
url: "Invoice/FilterInvoice",
data: dataToSend,
success: function (data) {
$("#Index").html(data);
}
[HttpPost] // Selected DDL value
public ActionResult FilterInvoice(int idDate)
{
switch (idDate)
{
case 0:
date = DateTime.Parse("01-01-1754");
break;
case 3:
date = DateTime.Now.AddMonths(-12);
break;
}
//var data is returning my expected results
var data = new UserBAL().GetInvoice(date);
// I know this isn't right and needs to be changed
return View(data);
My ajax success function isn't doing anything either So it i'm guessing this needs some tweaking. Also Here's how I am displaying the table using table tags. Keep in mind I left some code out but everything important is here, and the only issue is rendering my filtered results back to the view,
#foreach (var item in Model) {
<tr><td>
#Html.DisplayFor(modelItem => item.Invoice_Number)
#Html.DisplayFor(modelItem => item.Amt_Total)
</td>
Instead of passing view you can return the partial view as string then in ajax success using jquery you can update the result :
Controller Logic:
[HttpPost]
public JsonResult FilterInvoice(int idDate)
{
.....
return Json((RenderRazorViewToString("YourViewName", data)), JsonRequestBehavior.AllowGet);
}
[NonAction]
public string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
Ajax call :
$.ajax({
//........
success: function (result) {
$("#Index").replaceWith(result);
}
});
Here's the answer if anyone else comes across this. This is what I ended up doing, the rows are getting filtered passed on the date parameter I'm passing to the URL of the function. Having the Grid populate inside the Ajax call also seemed like it was a problem so I had to take it out.
public JsonResult JqGrid(int idDate)
{
switch (idDate)
#region switch date
--Switch Statement--
#endregion
var invoices = new UserBAL().GetInvoice(date);
return Json(invoices, JsonRequestBehavior.AllowGet);
}
[HttpPost] // pretty much does nothing, used as a middle man for ajax call
public JsonResult JqGridz(int idDate)
{
switch (idDate)
#region switch date
--Switch Statement--
#endregion
var invoices = new UserBAL().GetInvoice(date);
return Json(invoices, JsonRequestBehavior.AllowGet);
}
Yes these two functions seem very redundant and they are. I don't know why my post wouldn't update data, but I needed to reload the grid each time and when I did that it would call the first function. So yea the post jqGridz is kinda of just a middle man.
Here's the jquery code I used
var dropdown
var Url = '/Invoice/JqGrid/?idDate=0'
$(document).ready(function () {
$("#jqgrid").jqGrid({
url: Url,
datatype: 'json',
mtype: 'GET', //insert data from the data object we created above
width: 500,
colNames: ['ID','Invoice #', 'Total Amount', 'Amount Due', 'Amount Paid', 'Due Date'], //define column names
colModel: [
{ name: 'InvoiceID', index: 'Invoice_Number', key: true, hidden: true, width: 50, align: 'left' },
{ name: 'Invoice_Number', index: 'Invoice_Number', width: 50, align: 'left'},
{ name: 'Amt_Total', index: 'Amt_Total', width: 50, align: 'left' },
{ name: 'Amt_Due', index: 'Amt_Due', width: 50, align: 'left' },
{ name: 'Amt_Paid', index: 'Amt_Paid', width: 50, align: 'left' },
{ name: 'Due_Date', index: 'Due_Date', formatter: "date", formatoptions: { "srcformat": "Y-m-d", newformat: "m/d/Y" }, width: 50, align: 'left' },
],
pager: jQuery('#pager'),
sortname: 'Invoice_Number',
viewrecords: false,
editable: true,
sortorder: "asc",
caption: "Invoices",
});
$("[name='DDLItems']").change(function () {
var selection = $(this).val();
dropdown = {
//holds selected value
idDate: selection
};
$.ajax({
type: "POST",
url: "Invoice/JqGridz",
data: dropdown,
async: false,
cache: false,
success: function (data) {
$("#jqgrid").setGridParam({ url: Url + selection})
$("#jqgrid").trigger('reloadGrid');
}
})
})
});
I have a MVC site where I use Kendo UI and knockout.js to display the pages. One scenario is to get the database information from server through $.getJSON and then display this information on a KendoUI grid.
<div data-bind="kendoGrid:{sortable:true, data:users, rowTemplate:'userRowTemplate'}>
<table>
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead> </table>
</div>
<script type="text/html">
<tr>
<td data-bind="text: Username"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: LastName"></td>
<tr>
</script>
and the javascript :
<script type="text/javascript">
var ViewModel = function () {
var self=this;
self.users=ko.mapping.fromJS([]);
$getJSON("/UserManagementController/GetUsers",function(data){
ko.mapping.fromJS(data,{},self.users);
});
};
$(document).ready(function(){
var newViewModel=new ViewModel();
ko.applyBindings(newViewModel);
});
</script>
I want this data to be sortable on specific columns (the ones specified here are for example's sake), but I haven't been able to achieve this successfully. I have tried the solution from this knockout-kendo plugin issue post, which works well on simple objects, but does not work on observables. So my question is : how to map data from database through MVC controller to observables in knockout and displaying them in a Kendo grid, but still be able to sort them?
Thanks,
Alex Barac
You can do this by creating a JS view model to represent the data coming back from the server, and mapping the data into the view model. Then you can have simple objects that get set by subscribing to the matching observable properties to implement the sort.
Here is an example:
http://jsfiddle.net/R4Jys/1/
HTML:
<div data-bind="kendoGrid: gridOptions(myList)"></div>
<script id="rowTmpl" type="text/html">
<tr>
<td>
<span data-bind="text: firstName" />
</td>
<td>
<span data-bind="text: lastName" />
</td>
<td>
<span data-bind="text: userName" />
</td>
</tr>
</script>
JavaScript:
var gridItem = function () {
var self = this;
self.firstName = ko.observable();
self.firstNameSort;
self.firstName.subscribe(function (value) {
self.firstNameSort = value;
});
self.lastName = ko.observable();
self.lastNameSort;
self.lastName.subscribe(function (value) {
self.lastNameSort = value;
});
self.userName = ko.observable();
self.userNameSort;
self.userName.subscribe(function (value) {
self.userNameSort = value;
});
self.other = ko.observable('test');
return self;
};
var vm = function() {
var self = this;
self.myList = ko.observableArray();
self.test = ko.observable();
self.gridOptions = function (data) {
return {
data: data,
rowTemplate: 'rowTmpl',
useKOTemplates: true,
scrollable: true,
sortable: true,
columns: [
{
field: "firstNameSort",
title: "First Name",
width: 130
},
{
field: "lastNameSort",
title: "Last Name",
filterable: true
},
{
field: "userNameSort",
title: "Username"
}
]
}
};
var data = [{'firstName':'Steve', 'lastName':'Jones', 'userName': 'steve.jones'},
{'firstName':'Janet', 'lastName':'Smith', 'userName': 'janet.smith'},
{'firstName':'April', 'lastName':'Baker', 'userName': 'april.baker'},
{'firstName':'Dave', 'lastName':'Lee', 'userName': 'dave.lee'},
{'firstName':'Jack', 'lastName':'Bennet', 'userName': 'jack.bennet'},
{'firstName':'Chris', 'lastName':'Carter', 'userName': 'chris.carter'}];
self.myList(ko.utils.arrayMap(data, function(item) {
var g = new gridItem();
ko.mapping.fromJS(item, {}, g);
return g;
}));
};
var pageVm = new vm();
ko.applyBindings(pageVm);
As an alternative you can modify the kendo.web.js (version 2013.3.1119) on line 6844 & 6844.
Replace
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = selector(a);
b = selector(b);
With
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = ko.utils.unwrapObservable(selector(a));
b = ko.utils.unwrapObservable(selector(b));
By using the knockout utility "ko.utils.unwrapObservable" you can get the value of the observable for use when kendo compares the values of the column
DEFINE CUSTOM COMPARE FUNCTION will serves solution, where you can override the comparing function in field definition.
So you can do something like this:
$("#grid").kendoGrid({
dataSource: dataSource,
sortable: true,
columns: [{
field: "item",
sortable: {
compare: function(a, b) {
var valueA = a["item"];
var valueB = b["item"];
if (typeof valueA === "function") valueA = valueA();
if (typeof valueB === "function") valueB = valueB();
if (this.isNumeric(valueA) && this.isNumeric(valueB))
{
valueA = parseFloat(valueA);
valueB = parseFloat(valueB);
}
if (valueA && valueA.getTime && valueB && valueB.getTime)
{
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB)
{
return a.__position - b.__position;
}
if (valueA == null)
{
return -1;
}
if (valueB == null)
{
return 1;
}
if (valueA.localeCompare)
{
return valueA.localeCompare(valueB);
}
return valueA > valueB ? 1 : -1;
}
}
}]
});
private isNumeric(input)
{
return (input - 0) == input && ('' + input).trim().length > 0;
}
The compare method is stolen from kendo script, but change is where the property is typeof function (what ko.observable is), it unwraps the value. Plus, I added support for numbers.