Attach object to Highcharts click event - javascript

Moving from D3 to Highcharts and this is eluding me. I have a fairly complex object that contains a clickthrough object which needs to be accessed in a function on a point click in the series. I'm creating the series array with the data and name just fine with a small conversion, but I need to attach this object to the data points as well. No idea how.
Quick example. original data:
[
{
"key": "Super Cool Thing",
"format": ".2f",
"values": [
{
"label": "01",
"value": 9.5,
"format": ".2f",
"order": 0,
"tooltip": "numerator = 133, denominator = 14",
"clickthrough": {
"output_format": "json",
"metrics": "",
"options": {
"columns": [
{
"order": 1,
"display_as": "Brand",
"format": "{0}",
"name": "brand",
"data_type": "string"
},
{
"order": 2,
"display_as": "Last Submit Time (Month)",
"format": "%m",
"name": "last-submit-time-month",
"data_type": "datetime"
},
{
"order": 3,
"display_as": "Agent Things",
"format": "{0}",
"name": "agent-thing-values",
"data_type": "string"
}
]
},
"cut_y": "brand",
"type": "",
"filter": { },
"cut_x": "last-submit-time-month"
},
"metrics": [
{
"name": "Agent - Feel Appreciated Mean",
"slug": "qcustomersatr4-mean"
}
]
}
]
}
]
run through a (super quick POC) funct:
for(let i = 0; i < data.length; i++){
var values = [];
var xcuts = [];
data[i].values.forEach(val => {
values.push(val.value);
xcuts.push(val.label);
});
chart.addSeries({
name: data[i].key,
data: values
})
chart.xAxis[0].setCategories(xcuts);
}
and this all works fine. But I need the clickthrough object so I can do something like:
plotOptions: {
series: {
allowPointSelect: true,
cursor: 'pointer',
point: {
events: {
click: function (event) {
console.log('CLICKTHROUGH DATA HERE');
console.log(event.point);
}
}
}
},
},
I'm unsure how to format the series data to include additional data that's accessible in an event function later down the line. I currently do this via d3 and it's fine, but am struggling with the Highcharts method to do the same. It seems I can't just add whatever I want to the series or data, so is this possible?

Have it. I have to set the y value explicitly and then I can add whatever else which is then avail in the event.
example:
data[i].values.forEach(val => {
values.push({link: val.clickthrough, y:val.value});
xcuts.push(val.label);
});
chart.addSeries({
name: data[i].key,
data: values
})

Related

Javascript/React code - control/page won't render in fileReader.onload

I have a react page and one of my inputs is a file upload. When loading, I want to read in the file (it's JSON) and then show the file as a tree to allow my users to select nodes (rules) to run against another dataset. BUT, when I pick the JSON file and the 'onload' event handler actually fires off, the page just stops rendering, I get a blank screen. I'm not sure why, I can't see any errors, but I AM IGNORANT with react and kinda new with javascript as well. So, this is quite likely just a dumb thing I'm doing. Can someone point me at what I'm doing wrong here?
handleRules(event) {
const ruleRdr = new FileReader();
ruleRdr.onload = async (e) => {
const rBuf = (e.target.result);
const rData = JSON.parse(new TextDecoder().decode(rBuf));
// the data is there, but it's not mapping into the tree...!?!?!?
const tree = {
name: "QA/QC Rules",
id: 1,
toggled: true,
children: rData.map((wFlow, index) => ({
name: wFlow.WorkflowName,
id: index,
children: wFlow.Rules.map((rule, idx) => ({
name: rule.RuleName,
id: idx
}))
}))
};
this.setState({ ruleData: rData, hasRules: true, treeData: tree });
}
ruleRdr.readAsArrayBuffer(event.target.files[0]);
}
EDIT #1: I don't think it's the code above now, I think it might be my tree library (react-treebeard) or my ignorance on how I'm using it. The code produces what I think is useable data, but it isn't rendering it out.
{
"name": "QA/QC Rules",
"id": 1,
"toggled": true,
"children": [
{
"name": "COMP",
"id": 0,
"children": [
{
"name": "ParentMustHaveCat",
"id": 0
},
{
"name": "ParentMustHaveMfg",
"id": 1
},
{
"name": "ParentMustHaveFamily",
"id": 2
},
{
"name": "SymbolsMustHaveFamily",
"id": 3
}
]
},
{
"name": "PNLCOMP",
"id": 1,
"children": [
{
"name": "ParentMustHaveCat",
"id": 0
},
{
"name": "ParentMustHaveMfg",
"id": 1
},
{
"name": "ParentMustHaveFamily",
"id": 2
},
{
"name": "SymbolsMustHaveFamily",
"id": 3
}
]
},
{
"name": "PNLTERM",
"id": 2,
"children": [
{
"name": "ParentMustHaveCat",
"id": 0
},
{
"name": "ParentMustHaveMfg",
"id": 1
},
{
"name": "ParentMustHaveFamily",
"id": 2
},
{
"name": "SymbolsMustHaveFamily",
"id": 3
}
]
}
]
}
I figured it out. I switched to MUI since it has more components that I will want to use anyway. I got a similar issue with it as well and realized that I have duplicate IDs between the parent and the children, and was creating a kind of lock when trying to compare parent and child IDs in the MUI library. Totally on me - I'm dumb.

Exact search with ElasticSearch 7.x

I am trying to find an exact search for an url with ElasticSearch ("#elastic/elasticsearch": "^7.5.0").
I have configured my mapping like so:
const schema = {
userId: {
type: "keyword"
},
url: {
type: "keyword",
index: false,
analyzer: 'keyword'
},
pageTitle: {
type: 'text',
},
pageText: {
type: 'text',
}
};
await client.indices.putMapping({
index,
type,
include_type_name: true,
body: {
properties: schema
}
})
I have tried different queries, and they looks like this:
body: {
query: {
bool: {
must: {
match: {
query: 'test stack',
analyzer: 'keyword',
}
}
}
}
}
Or second attempt:
body: {
query: {
constant_score: {
filter: {
bool: {
must: {
term: {
url: 'test stack'
}
}
}
}
},
}
}
None of them work. I want to get only the results where the exact string 'test/stack' is found. Any help would be highly appreciated.
Example of data I'm trying to add:
[
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
},
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
},
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
}
]
Thanks.
I managed to make this work. Steps are:
1. Delete the index.
2. Delete the custom mapping function.
3. Create the index (with client.indices.create)
4. Index the first item (with client.index).
5. At this point, you can check in postman the dynamic mappings created by ElasticSearch (only visible after 1st item is indexed, by what I could tell). You can make a get request at http://localhost:9200/history/_mappings, and the response should look something like this:
{
"history": {
"mappings": {
"properties": {
"fullTitle": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"log": {
"properties": {
"startTime": {
"type": "long"
},
"timeSpent": {
"type": "long"
}
}
},
"protocol": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"totalTimeSpent": {
"type": "long"
},
"totalVisits": {
"type": "long"
},
"url": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"userId": {
"type": "long"
}
}
}
}
}
As you can see, any field indexed as text has attached another field, called keyword, which can be used for exact matches.
6. The query to get the exact matches looks like this:
const result = await esClient.search({
index: 'history',
body: {
query: {
term: {
'url.keyword': {
value: toInsert.url
}
}
}
}
})
At this point you should receive results only in case of exact match for the field "url" in my case. Hope this helps somebody else. Thanks #ibexit for trying to help me.
I see two problems:
The mapping defined for the url field says
url: {
type: "keyword",
index: false,
analyzer: 'keyword'
},
If you define index: false, the field will not be searchable at all. Using the following mapping should work properly:
url: {
type: "keyword"
}
See https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html for more detailed information
The keyword mapped fields will not match using the match query which is designed to query text fields. Please use the term query instead for keyword fields. Please notice the example below using the Elasticseaech Query API:
GET /_search
{
"query": {
"term": {
"url": { <<= the field to search
"value": "test stack" <<= the searched value
}
}
}
}
Here is the according documentation: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html
BTW: keep in mind that you need to reindex the data after a mapping change

setFeatureState not updating a value in Mapbox

I am trying to change the color of a marker which is a circle, that is being painted using the paint property on a layer.
Following this tutorial:
https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/
I have set the circle-color to be dependent of a feature-state:
map.addLayer({
id: "singles",
type: "circle",
source: "users",
filter: ["!has", "point_count"],
paint: {
'circle-radius': {
'base': 10,
'stops': [[5, 20], [15, 500]]
},
'circle-color': ["case",
["boolean", ["feature-state", "hover"], false],
'#ddffc8',
'#ff0000'
],
}
});
Then when somebody hovers over a sidebar, I want to update the feature-state and change the color:
function highlightMarkersOnHoverOnSidebar (markers, map) {
let marks = markers
Array.from(document.querySelectorAll('.sideBarItems')).map( (x, i) => {
x.addEventListener('mouseenter', function(){
map.setFeatureState({source: 'users', id: marks.features[i].properties.id}, { hover: true});
}, false)
x.addEventListener('mouseleave', function(){
map.setFeatureState({source: 'users', id: marks.features[i].properties.id}, { hover: false});
}, false)
})
}
However, nothing happens when i hover the sidebar element.. and it doesnt even throw an error.
Is there anything im missing? thanks.
I also run into this issue.
It seems like you need an id at the feature level in your geojson. Not in the properties:
{
"type": "FeatureCollection",
"features": [
{
"id": 4459,
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
8.64543,
50.163105
]
},
"properties": {
"id": "NOT HERE",
"name": "Test",
"foo": "bar",
}
}
]
}
Moving the id to the feature solved the issue.
Are you using featureCollection in geoJson? That caused some problems for me.

Dropdown in kendo UI grid

I need a drop down for a Kendo-UI grid, and came across this example:
http://codepen.io/jordanilchev/pen/cnkih?editors=001
But in this example, both the key and the display text for the drop down are included in the data source of the grid as well, which seems very redundant. I looked at a similar example on Telerik's site, and it was the same there.
Here is the data source for the Type drop-down:
var types = [
{
"Type": "FB",
"Name": "Facebook"
},
{
"Type": "TW",
"Name": "Twitter"
},
{
"Type": "YT",
"Name": "YouTube"
},
{
"Type": "PI",
"Name": "Pinterest"
}
];
So far so good. But here is the data for the actual grid - notice how it also contains both Type and Name for every record:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}...
What I had expected is that only the Type would have to be in the data source of the grid - like this:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": "FB",
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": "FB",
}...
Is there a way to use a drop-down in the Kendo UI grid without having to include both the key and the display text for every record in the data source of the grid? In other words, the grid would know to reference the datasource of the drop-down to get the display text for the cell.
Update 9/23/2014:
The solution proposed by CodingWithSpike works fine when the datasource for the drop down is a hard-coded / local array, but I am having difficulties getting it to work when loading the data for the drop down from a server. The issue seems to be that the grid gets initialized before the data source for the drop down has been read.
To "simulate" an $http call to populate the data source, I use a setTimeout:
$(document).ready(function () {
var categories = [];
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
$('#grid').data('kendoGrid').dataSource.read(); // Just as a test, but not even this helps
$('#grid').data('kendoGrid').refresh(); // Just as a test, but not even this helps
}, 1000);
When the data is loaded as above (or via $http), the drop down fields now contain the value (id) instead of the text. Here is a plunker that shows this:
http://plnkr.co/edit/DWaaHGVAS6YuDcqTXPL8?p=preview
Note that the real app is an AngularJs app, and I would rather not use some jQuery hack to wait until the drop down data is available and then create the grid element.
How do I get this working with data from a server?
Take a look at the Kendo demo for "Foreign Key" columns. I think it is exactly what you want.
http://demos.telerik.com/kendo-ui/grid/foreignkeycolumn
They use a list of categories:
var categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
},{
"value": 4,
"text": "Dairy Products"
},{
"value": 5,
"text": "Grains/Cereals"
},{
"value": 6,
"text": "Meat/Poultry"
},{
"value": 7,
"text": "Produce"
},{
"value": 8,
"text": "Seafood"
}];
The demo is a little deceiving because their data for the grid contains the entire "Category":
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1,
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false,
Category : {
CategoryID : 1,
CategoryName : "Beverages",
Description : "Soft drinks, coffees, teas, beers, and ales"
}
}
However, if you look at the column definition:
{ field: "CategoryID", width: "200px", values: categories, title: "Category" },
The specified field is CategoryID not Category so the grid data item actually doesn't need to specify a "Category" property at all, and could just be:
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1, // <-- this is the important part!
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false
}
I suspect the "Category" was in there just because this JSON file is shared by a few examples, so a different one may have needed it.
Update
Regarding the issue of the Grid loading before the "Category" (or whatever) FK table:
Use a deferred or the callback on the grid datasource to wait until the FK datasource is done loading before populating the grid data. Alternatively, you can init the grid, but set it to autoBind: false so that it doesn't actually read from its DataSource immediately.
Something like this (sorry for any errors, typing this off the top of my head):
(function () {
// for the foreign keys
var categoriesDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "http://somewhere.com/categories"
}
}
});
// for the actual grid data
var gridDataSource = new kendo.data.DataSource({
...
});
// init the grid widget
var gridWidget = $("#grid").kendoGrid({
dataSource: gridDataSource,
autoBind: false, // <-- don't read the DataSource. We will read it ourselves.
columns: [ ... ]
});
// now we can read the FK table data.
// when that completes, read the grid data.
categoriesDataSource.fetch(function () {
gridDataSource.fetch();
});
});
I asked Telerik about this, and here is the solution they gave.
When the drop-down's data is available, use setOptions on the grid, like this (again, I use setTimeout instead of an Ajax call here, for simplicity):
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
var grid = $('#grid').data('kendoGrid');
var cols = grid.columns;
cols[1].values = categories;
grid.setOptions({columns: cols});
$('#grid').data('kendoGrid').refresh();
}, 200);
Also, autoBind: false is not needed.
Here is an updated plunker:
http://plnkr.co/edit/ZjuK9wk3Zq80yA0LIIWg?p=preview

Using JavaScript calculated property in UpshotJS

I'm new to Upshot and can't find a documentation/group for Upshot so I'm posting it in here.
I want to have calculated property in my model, is it supported and how can I implement that?
For example:
upshot.metadata({
"Cocktail:#Cocktail.Models": {
"key": ["Id"],
"fields": {
"Id": { "type": "Int32:#System" },
"Image": { "type": "String:#System" },
"HtmlId": { "type": "String:#System"},
// Calculated Property
"DetailUrl": { "type": "String:#System", "value": function(){return 'cocktail' + this.Id;} },
}
}
});

Categories