Kendo UI datepicker - month change event
I searched for this here & on Telerik forum too but don't have the solution for this.
Here, I want to mark few dates from month and I did it on OPEN event like below-
$.each(dates, function (index, date) {
var reformattedDate = date.getFullYear() + '/' + date.getMonth() + '/' + date.getDate();
$('#datepickerId_dateview a.k-link[data-value="' + reformattedDate + '"]').parent().addClass("date-marking-class");
});
So, I am looping though all my dates and comparing it with data-value of datepicker calendar. On match found, I am applying class to mark that date.
It's working absolutely fine on datepicker OPEN event but whenever I change month, it's not marking the date at all.
So i want an event which will trigger on month change, so that I can execute that 2 lines of code to mark the dates on new month.
There does not appear to be anything documented to do this, but after looking at the DatePicker source code you can accomplish it.
The underlying Calendar widget has a navigate event that does what you want(http://docs.telerik.com/kendo-ui/api/javascript/ui/calendar#events-navigate). The problem is getting a reference to the Calendar used by the DatePicker.
I was able to do it like this:
$(document).ready(function() {
// create DatePicker from input HTML element
var datePicker = $("#datepicker").kendoDatePicker().getKendoDatePicker();
var dateView = datePicker.dateView;
// Force calendar to initialize so we can bind to its events...otherwise, it does not exist until it is opened for the first time.
dateView._calendar();
var calendar = dateView.calendar;
calendar.bind("navigate", function () {
console.log("Do your thing here");
});
});
The DatePicker has a DateView which has a Calendar...but the Calendar doesn't exist until the DateView is opened for the first time. But once that happens, you can attach to its navigate event.
I force the Calendar to exist without an open event by calling the "private" _calendar() method that the DateView internally calls on first open...and now you can handle its navigate.
Demo: http://dojo.telerik.com/#Stephen/ekUwE
You can use the month template of the widget:
$("#date").kendoDatePicker({
month: {
content: $("#date-template").html()
}
});
It renders a template for each day if the widget is set to Month view. There you can wrap the day number with a span with the desired class.
And the template could be something like:
#
var month = data.date.getMonth() + 1;
dates = months[month],
found = false,
result = data.value;
if (dates && dates.length > 0) {
for (var i = 0, len = dates.length; i < len; i++) {
var date = dates[i],
dateSplit = data.dateString.split("/");
if (date.getDate() == dateSplit[2] &&
date.getMonth() == dateSplit[1] &&
date.getFullYear() == dateSplit[0])
{
result = "<span class='date-marking-class'>" + data.value + "</span>";
break;
}
}
}
#
#=result#
Being months an object like this:
// All months are contains an array with date objects(in this case, days 10 and 20 for each one)
var months = {
"1": [new Date(2017, 0, 10), new Date(2017, 0, 20)],
"2": [new Date(2017, 1, 10), new Date(2017, 1, 20)],
"3": [new Date(2017, 2, 10), new Date(2017, 2, 20)],
"4": [new Date(2017, 3, 10), new Date(2017, 3, 20)],
"5": [new Date(2017, 4, 10), new Date(2017, 4, 20)],
"6": [new Date(2017, 5, 10), new Date(2017, 5, 20)],
"7": [new Date(2017, 6, 10), new Date(2017, 6, 20)],
"8": [new Date(2017, 7, 10), new Date(2017, 7, 20)],
"9": [new Date(2017, 8, 10), new Date(2017, 8, 20)],
"10": [new Date(2017, 9, 10), new Date(2017, 9, 20)],
"11": [new Date(2017, 10, 10), new Date(2017, 10, 20)],
"12": [new Date(2017, 11, 10), new Date(2017, 11, 20)]
};
Or anyway you want it - that was just a suggestion - since you change the line dates = months[month] to something that gives you an array of dates.
Demo
Related
Need help for creating dynamic charts in a power point by using Aspose slides.
#Mohini,
I have observed your requirements and like to share that Aspose.Slides offers you to create MSO charts programmatically. I suggest you to please try using following sample code to create a Clustered Column chart.
// Instantiate Presentation class that represents PPTX file
Presentation pres = new Presentation();
// Access first slide
ISlide sld = pres.Slides[0];
// Add chart with default data
IChart chart = sld.Shapes.AddChart(ChartType.ClusteredColumn, 0, 0, 500, 500);
// Setting chart Title
// Chart.ChartTitle.TextFrameForOverriding.Text = "Sample Title";
chart.ChartTitle.AddTextFrameForOverriding("Sample Title");
chart.ChartTitle.TextFrameForOverriding.TextFrameFormat.CenterText = NullableBool.True;
chart.ChartTitle.Height = 20;
chart.HasTitle = true;
// Set first series to Show Values
chart.ChartData.Series[0].Labels.DefaultDataLabelFormat.ShowValue = true;
// Setting the index of chart data sheet
int defaultWorksheetIndex = 0;
// Getting the chart data worksheet
IChartDataWorkbook fact = chart.ChartData.ChartDataWorkbook;
// Delete default generated series and categories
chart.ChartData.Series.Clear();
chart.ChartData.Categories.Clear();
int s = chart.ChartData.Series.Count;
s = chart.ChartData.Categories.Count;
// Adding new series
chart.ChartData.Series.Add(fact.GetCell(defaultWorksheetIndex, 0, 1, "Series 1"), chart.Type);
chart.ChartData.Series.Add(fact.GetCell(defaultWorksheetIndex, 0, 2, "Series 2"), chart.Type);
// Adding new categories
chart.ChartData.Categories.Add(fact.GetCell(defaultWorksheetIndex, 1, 0, "Caetegoty 1"));
chart.ChartData.Categories.Add(fact.GetCell(defaultWorksheetIndex, 2, 0, "Caetegoty 2"));
chart.ChartData.Categories.Add(fact.GetCell(defaultWorksheetIndex, 3, 0, "Caetegoty 3"));
// Take first chart series
IChartSeries series = chart.ChartData.Series[0];
// Now populating series data
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 1, 1, 20));
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 2, 1, 50));
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 3, 1, 30));
// Setting fill color for series
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = Color.Red;
// Take second chart series
series = chart.ChartData.Series[1];
// Now populating series data
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 1, 2, 30));
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 2, 2, 10));
series.DataPoints.AddDataPointForBarSeries(fact.GetCell(defaultWorksheetIndex, 3, 2, 60));
// Setting fill color for series
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = Color.Green;
// First label will be show Category name
IDataLabel lbl = series.DataPoints[0].Label;
lbl.DataLabelFormat.ShowCategoryName = true;
lbl = series.DataPoints[1].Label;
lbl.DataLabelFormat.ShowSeriesName = true;
// Show value for third label
lbl = series.DataPoints[2].Label;
lbl.DataLabelFormat.ShowValue = true;
lbl.DataLabelFormat.ShowSeriesName = true;
lbl.DataLabelFormat.Separator = "/";
// Save presentation with chart
pres.Save(dataDir + "AsposeChart_out.pptx", SaveFormat.Pptx);
I am working as Support developer/ Evangelist at Aspose.
Many Thanks,
I'm looking to add an Attrition Rate % graph to my Dashboard but I'm having difficulty in working out how I would do this using the complex calculation.
Basically the attrition rate % needs to look back at the previous 12 periods - for each period.
Calculation (for each Period) -
Total Starters in Previous 12 Periods (including current) / (Total Heads for Period 13 months ago + Leavers in Previous 12 Periods (including current)).
For example, if the Period is 201710 [YYYYMM], then the calculation would be:
(Total Starters in Periods 201611 to 201710) / Heads in Period 201610 + Total Leavers in Periods 201611 to 201710)
And within the line chart, you'd have the same calculation above for each period.
So, if I have the following data:
{ ... }
{ "Period": 201601, "Heads": 100, "Starters": 10, "Leavers": 8 },
{ "Period": 201602, "Heads": 102, "Starters": 8, "Leavers": 8 },
{ "Period": 201603, "Heads": 102, "Starters": 3, "Leavers": 0 },
{ "Period": 201604, "Heads": 105, "Starters": 8, "Leavers": 12 },
{ "Period": 201605, "Heads": 101, "Starters": 2, "Leavers": 5 },
{ "Period": 201606, "Heads": 98, "Starters": 8, "Leavers": 11 },
{ "Period": 201607, "Heads": 101, "Starters": 6, "Leavers": 5 },
{ "Period": 201608, "Heads": 102, "Starters": 4, "Leavers": 1 },
{ "Period": 201609, "Heads": 105, "Starters": 11, "Leavers": 17 },
{ "Period": 201610, "Heads": 99, "Starters": 8, "Leavers": 11 },
{ "Period": 201611, "Heads": 96, "Starters": 5, "Leavers": 8 },
{ "Period": 201612, "Heads": 95, "Starters": 4, "Leavers": 5 },
{ "Period": 201701, "Heads": 91, "Starters": 1, "Leavers": 5 },
The calculation and attrition rate % for Period 201701 would be:
Starters (Period 201602-201701): 68 / (Heads (Period 201601): 100 + Leavers (Period 201602-201701): 88
Attrition Rate for 201701 is: 36.17%
I would also like to have a number display that shows the attrition rate for the most recent Period.
I have some sample data and a Period chart to work with in a jsfiddle here: https://jsfiddle.net/kevinelphick/nh34aknn/
And a custom reduce function for the group like this:
attritionGroup = dimPeriod.group().reduce(
function (p, d) {
p.heads += d.Heads;
p.starters += d.Starters;
p.leavers += d.Leavers;
return p;
},
function (p, d) {
p.heads -= d.Heads;
p.starters -= d.Starters;
p.leavers -= d.Leavers;
return p;
},
function () {
return {heads: 0, starters: 0, leavers: 0};
});
I appreciate this may be a long shot due to its complex nature and I hope I've described my problem without confusion. I've tried in the past but I can't find any solutions that would work due to my limited knowledge. I can only guess that it would have to loop through the arrays dynamically to sum up starters, leavers that I need for the required Periods? Would I need to get a count of unique Periods to reference the correct periods I need for the calculation?
The reduce sets up the groups for you, then you can use a dummy group that calculates the attrition rates.
(UPDATED code)
function calcAttritionGroup (group) {
return {
all() {
var groupAll = group.all()
groupAll.forEach((p) => {
let elevenMonthsAgo = d3.time.month.offset(p.key, -11)
let twelveMonthsAgo = d3.time.month.offset(p.key, -12)
let twelveMonthsAgoGroup = groupAll.find(function(g){
return g.key.getTime() === twelveMonthsAgo.getTime()
})
let attrHeads = null
if (twelveMonthsAgoGroup) {
attrHeads = twelveMonthsAgoGroup.value.heads;
}
p.attrition = null
if (attrHeads) {
let subgroup = groupAll.filter(function(g) {
return g.key <= p.key && g.key >= elevenMonthsAgo;
})
let attrStarters = subgroup.reduce(function(sum, n) {
return sum + n.value.starters
}, 0)
let attrLeavers = subgroup.reduce(function(sum, n) {
return sum + n.value.leavers
}, 0)
let attrRate = (attrStarters / (attrHeads + attrLeavers))
p.attrition = attrRate || null
})
return groupAll
}
};
}
Here are modifications to your fiddle: https://jsfiddle.net/ga7x1p8m/ (UPDATED)
(Note, the values and formula in your question are different from in the fiddle.)
Some points...
1 - Formatting your period like that is not going to get you far because it won't give you a smooth range for your x scale, and makes it hard to do the comparisons you'll need for getting previous periods. So easiest is probably to cast as date object.
var format = d3.time.format("%Y%m");
data.forEach(function (d) {
d.date = format.parse(d.Period + '')
})
2 - You will have to manage edge cases. What happens if the 12 months previous period can't be found? If the earliest available period is used then this will add some more logic to the calculation.
Am trying to draw a flowchart. I create divs dynamically and have set a unique 'id' property for each div and connect them using Jsplumb connectors.
I get the source and destination id from database(note that 'id' property for div dynamically created is its ID from database) and store in 'connectors' json. Its format is
Eg:
{[from:A,to:B], [from:A,to:C], [from:B,to:C]}
angular.forEach(connectors, function (connect) {
$scope.connection(connect.from, connect.to);
})
The jsplumb code is as follows
$scope.connection = function (s, t) {
var stateMachineConnector1 = {
connector: ["Flowchart", { stub: 25, midpoint: 0.001 }],
maxConnections: -1,
paintStyle: { lineWidth: 3, stroke: "#421111" },
endpoint: "Blank",
anchor: "Continuous",
anchors: [strt, end],
overlays: [["PlainArrow", { location: 1, width: 15, length: 12 }]]
};
var firstInstance = jsPlumb.getInstance();
firstInstance.connect({ source: s.toString(), target: t.toString() }, stateMachineConnector1);
}
THE PROBLEM:
What i have now is
Here the connector B to C overlaps existing A to C connector.
What i need is to separate the two connections like below
I could not find a solution for this anywhere. Any help? Thanks!
Using anchor perimeter calculates the appropriate position for endpoints.
jsfiddle demo for perimeter
jsPlumb.connect({
source:$('#item1'),
target:$("#item2"),
endpoint:"Dot",
connector: ["Flowchart", { stub: 25, midpoint: 0.001 }],
anchors:[
[ "Perimeter", { shape:"Square" } ],
[ "Perimeter", { shape:"Square" } ]
]
});
Jsplumb anchors
What I suggest you to do, to exactly replicate your schema, would be to set 2 endpoints on on box on A, B and C
A Endpoints should be [0.25, 1, 0, 0, 0, 0] and [0.75, 1, 0, 0, 0, 0]
B and C Endpoints should be [0.25, 0, 0, 0, 0, 0] and [0.75, 0, 0, 0, 0, 0]
It basically works like this (I might be wrong for the 4 last one its been a while but you only need to worry about the x and y)
[x,y,offsetx, offsety, angle, angle]
For the x 0 is the extreme left and 1 extreme right
Same goes for y (0 is top and 1 is bottom).
Take care
One iPhone is used to transmit ibeacon using locate app (the proximity uuid is 2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6). The android phone is used to detect the ibeacon.
When I debugļ¼ I can get the following message:
08-30 15:33:57.051 D/BluetoothLeScanner(27939): onScanResult() - ScanResult{mDevice=6D:CC:9D:8D:3A:F3, mScanRecord=ScanRecord [mAdvertiseFlags=26, mServiceUuids=null, mManufacturerSpecificData={76=[2, 21, 47, 35, 68, 84, -49, 109, 74, 15, -83, -14, -12, -111, 27, -87, -1, -90, 0, 0, 0, 0, -59]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-31, mTimestampNanos=58705755631306}
However in function RangingBeaconsInRegion, there is no beacon.
void RangingBeaconsInRegion(object sender, RangeEventArgs e)
{
await ClearData();
var allBeacons = new List<Beacon>();
if (e.Beacons.Count > 0)
{
foreach (var b in e.Beacons)
{
allBeacons.Add(b);
int rssi = b.Rssi;
System.Diagnostics.Debug.WriteLine(rssi.ToString());
}
var orderedBeacons = allBeacons.OrderBy(b => b.Distance).ToList();
await UpdateData(orderedBeacons);
}
else
{
// unknown
await ClearData();
}
}
The following is my implementation:
public class MainActivity : XFormsApplicationDroid, IBeaconConsumer
{
public MainActivity()
{
}
protected override void OnCreate(Bundle bundle)
{
BeaconManager beaconManager = BeaconManager.GetInstanceForApplication(this);
var iBeaconParser = new BeaconParser();
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
var parser = new BeaconParser();
parser.SetBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25");
beaconManager.BeaconParsers.Add(parser);
beaconManager.Bind(this);
}
public void OnBeaconServiceConnect()
{
//obtain the beaconservie object of android
var beaconService = Xamarin.Forms.DependencyService.Get<IbeaconService>();
beaconService.InitializeService();
//beaconService.StartTransmitting();
beaconService.StartMonitoring();
beaconService.StartRanging();
}
public void InitializeService()
{
m_beaconManager = InitializeBeaconManager();
}
private BeaconManager InitializeBeaconManager()
{
BeaconManager bm = BeaconManager.GetInstanceForApplication(Xamarin.Forms.Forms.Context);
//set the scan window
bm.SetForegroundScanPeriod(1100L);
//subscribe to the events;
m_monitorNotifier.EnterRegionComplete += EnteredRegion;
m_monitorNotifier.ExitRegionComplete += ExitedRegion;
m_monitorNotifier.DetermineStateForRegionComplete += DeterminedStateForRegionComplete;
m_rangeNotifier.DidRangeBeaconsInRegionComplete += RangingBeaconsInRegion;
// constructs a new region object to be used for ranging or monitoring
m_tagRegion = new Region("myUniqueBeaconId", Identifier.Parse("E4C8A4FC-F68B-470D-959F-29382AF72CE7"), null, null);
m_tagRegion = new Region("myUniqueBeaconId", Identifier.Parse("B9407F30-F5F8-466E-AFF9-25556B57FE6D"), null, null);
m_tagRegion = new Region("myUniqueBeaconId", Identifier.Parse("2F234454-CF6d-4A0F-ADF2-F4911BA9FFA6"), null, null);
m_emptyRegion = new Region("myEmptyBeaconId", null, null, null);
bm.SetBackgroundMode(false);
//
//bm.Bind((IBeaconConsumer)Xamarin.Forms.Forms.Context);
return bm;
}
public void StartRanging()
{
BeaconManagerInstance.SetForegroundBetweenScanPeriod(0L);
m_beaconManager.AddRangeNotifier(m_rangeNotifier);
m_beaconManager.StartRangingBeaconsInRegion(m_tagRegion);
m_beaconManager.StartRangingBeaconsInRegion(m_emptyRegion);
}
If you look closely at the code that sets up the beaconParsers, you'll see that two are constructed, but only one is added like this:
beaconManager.BeaconParsers.Add(parser);
Adding a second call to add the other beacon parser should solve the problem.
Thanks, that's my mistake! After I add a second call to add the other beacon parser! there is still some strange situation: I did not launch any beacon transmitter, but when I debug, I got the following output: D/BluetoothLeScanner( 6864): onScanResult() - ScanResult{mDevice=58:D6:74:3A:34:C5, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[12, 14, 0, 126, 81, 116, -16, 10, 52, 84, 15, 98, 113, 29, -15, 34]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-89, mTimestampNanos=122142418198514}
08-31 09:11:13.943 D/BluetoothLeScanner( 6864): onScanResult() - ScanResult{mDevice=58:D6:74:3A:34:C5, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[12, 14, 0, 126, 81, 116, -16, 10, 52, 84, 15, 98, 113, 29, -15, 34]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-89, mTimestampNanos=122142560625598}. It seems that one beacon is detected.
Been stumped on this for a little bit.
I found some other help on zoomToIndexes, but I cant get the zoomToDates to work on my page.
Live page is
b2 resource urq sales
Im trying to set the initial view to show from 2000 to current.. I want to slap some original sales data from early 80's in the graph, but dont want the graph to initially show the last 30+ years..
Any help would be MUCH appreciated!
zoomToDates takes real JavaScript Date objects as parameters:
chart.zoomToDates(new Date(2005, 0, 1), new Date(2015, 11, 31));
You can use chart's rendered event to "pre-zoom" on load as well:
var chart = AmCharts.makeChart("chartdiv", {
// your chart config
// ...
});
chart.addListener("rendered", function(event) {
event.chart.zoomToDates(new Date(2005, 0, 1), new Date(2015, 11, 31));
});
Note, that months in Date() constructor parameter (second parameter) are zero-based. Meaning January is 0, February - 1, etc.
You should use valueAxis property of chart object for zoomToValues. I hope this might help you.
var chart= AmCharts.makeChart("chartdiv", {
"type": "gantt",
"theme": "black",
...
});
zoomChart();
chart.addListener("dataUpdated", zoomChart);
function zoomChart(event) {
chart.valueAxis.zoomToValues(new Date(2017, 2, 10), new Date(2017,2,12));
// or ==> event.chart.valueAxis.zoomToValues(new Date(2017, 2, 10), new Date(2017,2,12));
}
This worked for me.:
var chart = AmCharts.makeChart('chartdiv', {
type: 'serial',
...
});
chart.addListener('dataUpdated', zoomChart);
zoomChart();
function zoomChart() {
chart.zoomToDates(new Date(2018, 2, 26), new Date(2018, 2, 28));
}
Note: The months parameter in Date() constructor are zero-based. January is 0, February is 1 and etc.