Knockout-Kendo Chart - remove and add series - asp.net-mvc-4

My project is MVC 5, I am using the following to generate a chart with multiple series:
HTML:
<button data-bind="click: addItem">Add</button>
<button data-bind="click: removeItem">Remove</button>
<div data-bind="kendoChart2: { title: { text: 'Graph Sample' },
series: seriesConfig,tooltip: {visible: true,template: '#= series.name #: #= value #'} , seriesDefaults: {
type: 'line',style: 'smooth'}}"> </div>
Javascript
var MainViewModel = function () {
var self = this;
this.Systolic = ko.observableArray([]);
this.Diastolic = ko.observableArray([]);
this.HeartRate= ko.observableArray([]);
$.ajax({
type: "GET",
url: '/Charts/GetChart',
contentType: "application/json; charset=utf-8",
async: false,
cache: false,
dataType: "json",
success: function (result) {
//Diastolic
if (result && result.Systolic.length > 0) {
for (var i = 0; i < result.Systolic.length; i++) {
self.Systolic.push(result.Systolic[i].Systolic);
}
};
....
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}});
this.seriesConfig = ko.observableArray([
{name: "Systolic", data: this.Systolic()},
{name: "Diastolic",data: this.Diastolic()}]);
this.addItem = function() {
this.seriesConfig.push({ name: "Heart Rate", data: this.HeartRate() });
};
this.removeItem = function() {
this.seriesConfig.remove({ name: "Diastolic", data: this.Diastolic() });
};
}.bind(this);
ko.kendo.bindingFactory.createBinding(
{
name: "kendoChart",
bindingName: "kendoChart2",
watch: {
data: function(value) {
ko.kendo.setDataSource(this, value);
},
series: function(value) {
this._sourceSeries = value;
this.refresh();
this.redraw();}
}
});
window.viewModel = new MainViewModel();
ko.applyBindings(window.viewModel);
The chart works great, however can't add or remove series?
Note:
the addItem works, I get the value of the new series:
series: function (value) {
alert(value[2].name);
this.seriesConfig = value;
this.refresh();
this.redraw();
}
I also tried load all series then use the following hide a series:
$("#kendoChart").getKendoChart().options.series[1].visible = false;
$("#kendoChart").getKendoChart().redraw();
Does not work, I think the chart name does not register.

I am not familiar with knockout-kendo, just with knockout in general, so if fixing obvious problem as described below will not work, you might need to refresh bindings. Looking at this example however this is not needed, so most likely you got caught by a simple fact that array's remove performs simple == comparison and it fails to find equal object in the array.
Here is a simplified example (although you might know it already, but just in case):
var a="abc";
var b="abc";
var aa = [1,2,3,"a","b","c"];
var data1 = {name: a, data: aa};
var data2 = {name: b, data: aa};
now, comparison a==b returns true and clearly data slots are the same, however data1==data2 is false. That is because it's a different object.
So in your example in removeItem you create and pass a new object to remove, not the one in the array, so == comparison fails and nothing is removed as that newly created object isn't in your observable array.
I suggest comparing the name only similar to item.age < 18 comparison from knockout.js documentation on observable arrays:
this.seriesConfig.remove( function (item) { return item.name == "Diastolic"; } )
I believe, this should do the trick.

Related

Single dropdown filtering for Datatable

I've been struggling for a few days now, trying to get a single dropdown to filter my table. Upon selection of the eraId, the columns should be refreshed to only show the columns of the selected eraId.
This is how my tables looks like:
I've read a lot of examples on Datatables website or forums but I can't seem to find something working.
I have managed to create a dropdown menu containing the different EraIds as filter (I have simplified the example below with only 3 eraIds) but after selecting an entry in the dropdown, the table gets empty and the column list is not refreshed.
I think the problem is that I first retrieve the columns names, based on the eraId and then draw the table accordingly, displaying only the resources from the specific eraId. I have tried several things but did not manage.
Ideally I should callback getPlayerResourceTable with the selected eraId or update the column list with the resources on the selected eraId.
Javascript:
var columns = [];
function getPlayerResourceTable($selectedEraId) {
$.ajax({
type: "POST",
url: "./graphs.php",
data: { call_function : 'getResourceTableColumns', eraId: $selectedEraId},
success: function (data) {
data = JSON.parse(data);
columnNames = Object.keys(data.data);
for (var i in data.data) {
columns.push({data: data.data[i],
title: data.data[i]});
}
$('#playerResourceTable').DataTable( {
processing: true,
serverSide: false,
filter: true,
columns: columns,
ajax: {
url: './graphs.php',
type: 'POST',
data: { call_function: 'playerResourceTable', column_fields : data.data, eraId: $selectedEraId}
},
initComplete: function () {
this.api().columns( 0 ).every( function () {
var column = this;
var select = $('<select><option value=""></option></select>')
.appendTo( $("#playerResourceTablesWrapper .dataTables_filter"))
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column.search( this.value ).draw();
} );
select.append( '<option value="1">Era 1</option>' )
select.append( '<option value="2">Era 2</option>' )
select.append( '<option value="3">Era 3</option>' )
} );
}
});
}
});
}
$(document).ready(function() {
$selectedEraId = 1;
getPlayerResourceTable($selectedEraId);
} );
PHP:
getResourceTableColumns returns the column list with query similar to SELECT columnName FROM ages WHERE eraId = ?
playerResourceTable returns the resources for each column (type of resource) with query similar to SELECT ".$field_list." FROM user_resources
I also thought of removing WHERE eraId = ? in my MySQL query and filtering the columns on the client side but no luck either.
I eventually ended up separating both functions and destroying/re-creating the table when changing Era.
function getColumns($selectedEraId) {
var columns = [];
$.ajax({
type: "POST",
url: "./graphs.php",
data: { call_function : 'getResourceTableColumns', eraId: $selectedEraId},
success: function (data) {
data = JSON.parse(data);
for (var i in data.data) {
columns.push({data: data.data[i],
title: data.data[i]});
}
if ( $.fn.dataTable.isDataTable( '#playerResourceTable' ) ) { // If the table already exists, detroy it before creating it again
$('#playerResourceTable').DataTable().destroy();
}
getPlayerResourceTable($selectedEraId, columns); // Will recreate the table with the new columns
}
});
}
function getPlayerResourceTable($selectedEraId, columns_to_show) {
$city_id = 91;
$playerResourceTable = $('#playerResourceTable').DataTable( {
processing: true,
serverSide: false,
filter: true,
columns: columns_to_show,
ajax: {
url: './graphs.php',
type: 'POST',
data: { call_function: 'playerResourceTable', column_fields : columns_to_show, city_id : parseInt($city_id)}
},
initComplete: function () {
this.api().columns( 0 ).every( function () {
var column = this;
var select = $('<select id="selectEraId" ><option value=""></option></select>')
.appendTo( $("#playerResourceTablesWrapper .dataTables_filter"))
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column.search( this.value ).draw();
} );
dropdown_string = getEraDropdown($selectedEraId);
} );
$('#selectEraId').on('change', function() {
$selectedEraId = this.value;
columns_to_show = getColumns($selectedEraId);
});
}
});
}

Datatables onchange event for input field

I have input fields in my project with a special class (date_input_field). On change these fields format the data that user has entered. The problem is that these fields work fine absolutely everywhere EXCEPT DataTables. I tried rowCallback in my datatable itself, but it didn't help. Why? And how can I fix that?
$(".date_input_field").change(function () {
...format the data
});
var table2 = $("#stopsTable").DataTable({
ajax: {
url: "/api/stops/" + confirmationId,
dataSrc: ""
},
columns: [
{
data: "date1",
render: function (data) {
var arr = data.split("T")[0].split("-");
return "<input class='form-control date_input_field' type='text' value='" + arr[2] + "-" + arr[1] + "-" + arr[0]+"'/>" ;}}
],
order: [[0, "asc"]],
scrollY: '50vh',
scrollCollapse: true,
paging: false,
rowCallback: function (row, data) {
$(row).css("cursor", "pointer");
$(".date_input_field").change(function () {
$(this).attr("value", "01");
});
}
});
Sorry for stupid question.
I tried the code below and it works.
rowCallback: function (row, data) {
$(row).css("cursor", "pointer");
$(row).on("change", ".date_input_field", function () {
var input = $(this);
input.val("1111111111");
});
}
when the user edits the input on the cell this will console log the edited field with its input id rowCallback is the function which can trigger / perform on any cell inside the datatable
var datatable = $('#datatable').DataTable({
rowCallback: function (row, data) {
$(row).on("change", ".edit_qty_class", function () {
var input = $(this);
input.attr('value', input.val())
console.log()
console.log($(this).attr('id'), input.val())
});
},
columns: [{
data: "id",
render: function(data, type, full, meta) {
return meta.row + 1
}
},
{
data: "id",
title:"QTY SEND",
render: function(data, type, full, meta) {
var dataID = 'edit_qty_' + data.id
return `<input type="number" id="`+dataID+`" name="`+dataID+`" class="edit_qty_class" value="0">`
}
},
})

How to chain get and map the result with lodash?

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!
I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

x-editable price format issue

i have an issue in x-editable plugin, how to display prices in correct format in x-editable ? i would like to get the values as in price format like xx,xxx.xx ?
My code is
var editVehiclePrice = function (el, options) {
var options = $.extend(true, {
url: Utils.siteUrl() + 'dashboard/sell_vehicle/inline_vehicle_edit/',
ajaxOptions: {
dataType: 'json'
},
mode: 'inline',
}, options || {
params: function(params) {
params.veh_id = $(this).data('vehid');
return params;
},
success: function(response, newValue) {
if(response.status == 0) return response.msg;
console.log(Utils.numberFormat(newValue,2));
$(response.to_update).text(newValue);
},
validate: function(value) {
if($.trim(value) == '') {
return 'This field is required';
}
}
});
$(el).editable(options);
}
That console.log will display correct format value..but when i update it displays number in normal format.. Please help me
if we want to display any changes after editing, we should add
display: function(value, response) {
var k = Utils.numberFormat(value,2);
$(this).text(k);
},
The price will display in the number format.
Just to add to Anju's reply, Utils.numberFormat(value,2); is not defined. you can replace that with number.toFixed(2).replace(/(\d)(?=(\d{3})+.)/g, '$1,'); or wrap it into a utility function

Rally - array does not contain all pushed elements

I have an app that I am trying to use but it seems that while iterating through arrays and pushing into another array.i.e combining the arrays into one is not working for me. Example - I see all 213 pushes to this array but when I check its contents they are less.
Here is the code that shows me incomplete array push list.
For 213 test cases test set only 67 are pushed and present in the array
that = this;
that._testSetTestList = [];
console.log('testsetdata',testsetdata);
Ext.Array.each(testsetdata, function(record) {
console.log('record.get(TestCases)',record.get('TestCases'));
Ext.Array.each(record.get('TestCases'), function(name, index) {
that._testSetTestList.push({
resID: name.FormattedID,
resName: name.Name,
resObject: name.ObjectID,
resSetID: record.get('FormattedID'),
resSet: record.get('Name'),
resSetObject: record.get('ObjectID'),
resSetProject: name.Project.ObjectID
});
console.log('_testSetTestList.push',{
resID: name.FormattedID
});
});
});
Can anyone guide me to what I am doing wrong if anything.
Try using this code instead:
this._testSetTestList = Ext.Array.flatten(Ext.Array.map(testsetdata, function(record) {
return Ext.Array.map(record.get('TestCases'), function(name, index) {
return {
resID: name.FormattedID,
resName: name.Name,
resObject: name.ObjectID,
resSetID: record.get('FormattedID'),
resSet: record.get('Name'),
resSetObject: record.get('ObjectID'),
resSetProject: name.Project.ObjectID
};
});
}))
The issue in my case was not the code but the scope..I was trying to get the test case results for test cases that were not directly reachable in the project tree. We have the test cases residing in several projects but then we use them in test sets under different projects. If the test cases that are part of the queried test sets are directly reachable for the project for which we view this page, then test case results were accounted for BUT if the test cases were in projects that were siblings to the one that view the app from then the query could not find them and take their test case results. The solution was to consolidate all test cases under the correct project so that the app can access them from any required project.
Based on the example of using promises from this github repo, here is a code that builds a grid of test sets with their collection of test cases, where elements of array of test sets setsWithCases is pushed in to another array testsets, and the second array is used to populate the grid. The second array contains all elements of the first array. I am using LowDash _.each, included with AppSDK2.
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentsCls: 'app',
launch: function(){
var aStore = Ext.create('Rally.data.wsapi.Store', {
model: 'TestSet',
fetch: ['ObjectID', 'FormattedID', 'Name', 'TestCases'],
autoLoad: true,
context:{
projectScopeUp: false,
projectScopeDown: false
},
listeners:{
scope: this,
load: this._onStoreLoaded
}
});
},
_onStoreLoaded: function(store, records){
var setsWithCases = [];
var testsets = [];
var that = this;
var promises = [];
_.each(records, function(testset){
promises.push(that._getTestCases(testset, that));
});
Deft.Promise.all(promises).then({
success: function(results) {
_.each(results, function(result) {
if (result.TestCases.length > 0) {
setsWithCases.push(result);
}
});
_.each(setsWithCases, function(testset){
testsets.push(testset);
});
that._makeGrid(testsets);
}
});
},
_getTestCases:function(testset, scope){
var deferred = Ext.create('Deft.Deferred');
var that = scope;
var testcases = [];
var result = {};
var testsetRef = testset.get('_ref');
var testsetObjectID = testset.get('ObjectID');
var testsetFormattedID = testset.get('FormattedID');
var testsetName = testset.get('Name');
var testcaseCollection = testset.getCollection("TestCases", {fetch: ['Name', 'FormattedID']});
var testcaseCount = testcaseCollection.getCount();
testcaseCollection.load({
callback: function(records, operation, success){
_.each(records, function(testcase){
testcases.push(testcase);
});
result = {
"_ref": testsetRef,
"ObjectID": testsetObjectID,
"FormattedID": testsetFormattedID,
"Name": testsetName,
"TestCases": testcases
};
deferred.resolve(result);
}
});
return deferred;
},
_makeGrid:function(testsets){
var that = this;
var gridStore = Ext.create('Rally.data.custom.Store', {
data: testsets,
pageSize: 1000,
remoteSort: false
});
var aGrid = Ext.create('Rally.ui.grid.Grid', {
itemId: 'testsetGrid',
store: gridStore,
columnCfgs:[
{
text: 'Formatted ID', dataIndex: 'FormattedID', xtype: 'templatecolumn',
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate')
},
{
text: 'Name', dataIndex: 'Name', flex: 1
},
{
text: 'TestCases', dataIndex: 'TestCases', flex:1,
renderer: function(value) {
var html = [];
_.each(value, function(testcase){
html.push('' + testcase.get('FormattedID') + '' + ' ' + testcase.get('Name'));
});
return html.join(', ');
}
}
]
});
that.add(aGrid);
}
});