Related
I am using gauge chart of highchart. I want to change color of tick in intervals. If I use tickColor and minorTickColor then it changes in whole range.
I used minorTickColor and tickColor but don't know how to give different color in different ranges.
yAxis:{
min: 0,
max: 200,
minorTickInterval: 'auto',
minorTickWidth: 1,
minorTickLength: 10,
minorTickPosition: 'inside',
minorTickColor:'blue',
tickPixelInterval: 30,
tickWidth: 2,
tickPosition: 'inside',
tickLength: 12,
tickColor: 'red',
labels: {
step: 2,
rotation: 'auto'
},
title: {
text: 'km/h'
},
plotBands: [{
from: 0,
to: 120,
className: 'green-band',
// green
}, {
from: 120,
to: 160,
className: 'yellow-band' // yellow
}, {
from: 160,
to: 200,
className: 'red-band' // red
}]
},
There aren't ranges for axis ticks in Highcharts. However, you can achieve it by looping through ticks collection and changing each tick attribute when it position meets the requirements. Check demo and code posted below.
Code:
chart: {
...
events: {
load: function() {
var chart = this,
yAxis = chart.yAxis[0],
key, tick, minorTick;
for (key in yAxis.ticks) {
tick = yAxis.ticks[key];
if (tick.pos < 120) {
tick.mark.attr({
stroke: 'red'
});
} else if (tick.pos >= 120 && tick.pos < 160) {
tick.mark.attr({
stroke: 'green'
});
} else if (tick.pos >= 160) {
tick.mark.attr({
stroke: 'yellow'
});
}
}
for (key in yAxis.minorTicks) {
minorTick = yAxis.minorTicks[key];
if (minorTick.pos < 120) {
minorTick.mark.attr({
stroke: 'orange'
});
} else if (minorTick.pos >= 120 && minorTick.pos < 160) {
minorTick.mark.attr({
stroke: 'tomato'
});
} else if (minorTick.pos >= 160) {
minorTick.mark.attr({
stroke: 'blue'
});
}
}
}
}
}
Demo:
https://jsfiddle.net/BlackLabel/kbe6joac/
API reference:
https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr
I have a model with a hex color field. The user can edit it as rgb via 3 separate numberfields. I'm trying to bind the field to these components but I'm not sure how to do it. I tried putting them in a container and binding the container to the field. However my setValue isn't called when a numberfield is changed.
I guess I could add listeners to the numberfields but I was hoping there was a better way to go about it.
https://fiddle.sencha.com/#view/editor&fiddle/23t2
setValue is only called on a form field. Form fields are denoted by the config isFormField: true. Note however that setValue is not the only function expected on a form field; there are dozens others (e.g. getValue, getModelValue, isValid, ...).
Because of this, I always use a hiddenfield to aggregate other form fields, not a container, and then I use change listeners to keep hiddenfield and other fields synchronized (bind should work as well, but our app is still MVC). I also annotate the other fields with
excludeForm: true,
submitValue: false,
to make sure that the values are not submitted and do not affect the "dirty" state of the form.
I have made some changes. It will work for both scenario,
if user change from color name(textfield) then RGB will change.
If user will change from number field than color name(textfield) will be changed.
Try this code in your sencha fiddle
Ext.create({
xtype: 'viewport',
renderTo: Ext.getBody(),
viewModel: {
data: {
theColor: {
name: 'Blue',
hex: '3366CC'
}
},
formulas: {
containerValue: {
bind: '{theColor.hex}',
get: function (value) {
return {
hex: value
};
},
set: function (value) {
this.set('theColor.hex', value.hex);
}
}
}
},
items: [{
region: 'center',
xtype: 'form',
bodyPadding: 10,
height: 300,
fieldDefaults: {
labelWidth: 90,
},
items: [{
xtype: 'component',
width: 30,
height: 30,
bind: {
style: {
background: '#{theColor.hex}'
}
}
}, {
xtype: 'textfield',
fieldLabel: 'Name',
bind: '{theColor.name}',
listeners:{
blur:function(textfield,e,eOpts){
var viewModel = textfield.up('viewport').getViewModel();
colorName = textfield.getValue(),
hex = colorToHex(colorName);
viewModel.set('theColor',{
name: colorName,
hex: hex
});
function colorToRGBA(color) {
// Returns the color as an array of [r, g, b, a] -- all range from 0 - 255
// color must be a valid canvas fillStyle. This will cover most anything
// you'd want to use.
// Examples:
// colorToRGBA('red') # [255, 0, 0, 255]
// colorToRGBA('#f00') # [255, 0, 0, 255]
var cvs, ctx;
cvs = document.createElement('canvas');
cvs.height = 1;
cvs.width = 1;
ctx = cvs.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
return ctx.getImageData(0, 0, 1, 1).data;
}
function byteToHex(num) {
// Turns a number (0-255) into a 2-character hex number (00-ff)
return ('0'+num.toString(16)).slice(-2);
}
function colorToHex(color) {
// Convert any CSS color to a hex representation
// Examples:
// colorToHex('red') # '#ff0000'
// colorToHex('rgb(255, 0, 0)') # '#ff0000'
var rgba, hex;
rgba = colorToRGBA(color);
hex = [0,1,2].map(
function(idx) { return byteToHex(rgba[idx]); }
).join('');
return hex;
}
}
}
}, {
xtype: 'container',
setValue: function (value) {
const hex = value.hex || '000000';
const red = parseInt(hex.substr(0, 2), 16);
const green = parseInt(hex.substr(2, 2), 16);
const blue = parseInt(hex.substr(4, 2), 16);
const items = this.query('');
items[0].setValue(red);
items[1].setValue(green);
items[2].setValue(blue);
},
bind: {
value: '{containerValue}',
},
defaults: {
xtype: 'numberfield',
maxValue: 255,
minValue: 0,
allowBlank: false,
width: 175,
listeners:{
change:function(numberfield){
if(numberfield.hasFocus){
var viewModel = numberfield.up('viewport').getViewModel(),
items = this.up().query(''),
red = items[0].getValue() || 0,
green = items[1].getValue() || 0,
blue = items[2].getValue() || 0,
hex = rgbToHex(red,green,blue);
viewModel.set('theColor',{
name: hex,//For hex to color name you have to search for that, here I am giving hax color.
hex: hex
});
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return componentToHex(r) + componentToHex(g) + componentToHex(b);
}
}
}
}
},
items: [{
fieldLabel: 'R',
}, {
fieldLabel: 'G',
}, {
fieldLabel: 'B',
}]
}]
}]
});
I have have a web app set up to display charts using the highcharts-ng directive.
I have the same type of chart with the same series data displaying on multiple views. The only difference between the charts is the height value which I would like to be able to set when I initialise my chart in my html markup.
This is how I have set things up:
The highcharts-ng directive is installed through bower.
I initialise my chart as follows:
<div ng-controller="AgeChartController" >
<highchart diagramHeight="500" id="{{link + '_chart'}}" config="ageChartConfig">
</highchart></div>
In my AgeChartController I do the following:
Pull data from JSON file
Get chart height attribute from diagramHeight
Construct Highcharts options object and send diagramHeight to this options object
angular.module('socialDashboard')
.controller('AgeChartController', function ($scope, $http, $attrs) {
var seventeenCount = 0;
var eighteenCount = 0;
var twentyFiveCount = 0;
var thirtyFiveCount = 0;
var chartHeight = 0;
var posts = [];
$http.get('dummy_content.json')
.then(function(res){
posts = res.data;
for (var i = 0; i < posts.length; i++) {
if (posts[i].age <= 17) {
seventeenCount++;
}
else if ((posts[i].age >= 18) && (posts[i].age <= 24)) {
eighteenCount++;
}
else if ((posts[i].age >= 25) && (posts[i].age <= 34)) {
twentyFiveCount++;
}
else if (posts[i].age >= 35) {
thirtyFiveCount++;
}
}
chartHeight = $attrs.diagramHeight;
$scope.ageChartConfig = {
options: {
chart: {
type: 'bar',
height: chartHeight,
backgroundColor: false
}
},
title: {
text: false
},
xAxis: {
categories: ['< 17', '18 - 24', '25 - 34', '> 35']
},
yAxis: {
gridLineWidth: 0,
title: {
text: 'Post count'
},
labels:
{
enabled: false
}
},
plotOptions: {
series: {
dataLabels: {
enabled: true
}
}
},
legend: {
layout: 'vertical',
floating: true,
backgroundColor: '#FFFFFF',
align: 'right',
verticalAlign: 'top',
y: 60,
x: -60
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
this.x + ': ' + this.y;
}
},
navigation: {
buttonOptions: {
enabled: false
}
},
series: [{
name: 'Post count',
showInLegend: false,
data: [{
color: '#9365b8',
y: seventeenCount
}, {
color: '#2c82c9',
y: eighteenCount
}, {
color: '#41a85f',
y: twentyFiveCount
}, {
color: '#fac51c',
y: thirtyFiveCount
}]
}],
loading: false
};
});
});
However when I declare my chart elsewhere with diagramHeight set to 200:
<div ng-controller="AgeChartController">
<highchart diagramHeight="200" id="{{link + '_chart'}}" config="ageChartConfig">
</highchart></div>
Only one of the two values get pulled and my chart is set the same across all charts (height 500). Why is this? Does this have something to do with my scope? I'm pretty new to angular and still getting my head around scope.
You should use angular factory to configure chart
app.factory('chartname', function () {
var agechart={ // your all chart option here
.
.
}
return agechart;
});
You can use above factory in any controller now just like
app.controller("controllername",function(chartname){
$scope.ageChartConfig=chartname;
});
Tell me, please, how you can use two types of separator digital input field? You can use only one standard methods, but at a different keyboard layout, there is a need to use another, keeping the data in one format, that is the '.'
Input: 10,789 or 10.789
Save: 10.789
I use Ext.form.NumberField for editing integral field.
Part of my code:
var editor = new Ext.ux.grid.RowEditor({
saveText: LANG['update'],
listeners: {
afteredit: function(object, changes, r, rowIndex) {
Ext.MessageBox.alert(LANG['alert_info'], LANG['memory']); }
}
}
});
var userGrid = new Ext.grid.GridPanel({
id: 'status-form',
region:'center',
margins: '5 5 5 5',
store: Gstore,
iconCls: 'icon-grid',
plugins: [editor, summary],
cm: new Ext.grid.ColumnModel([
{header: "ID", width: 30, sortable: true, dataIndex: 'idb', renderer: formatID},
{xtype: 'datecolumn', header: LANG['date'], width: 70, sortable: true, dataIndex: 'date',
groupRenderer: Ext.util.Format.dateRenderer('M Y'),
format: 'd/m/Y',
editor: new Ext.form.DateField({
value: (new Date()).format('d/m/Y'),
//format: 'd/m/Y',
minValue: '01/01/2010',
//minText: 'Please Check Correct Data',
maxValue: (new Date()).format('d/m/Y'),
editable: false
})
},
{header: LANG['title'], width: 150, sortable: true, dataIndex: 'title',
editor: new Ext.form.TextField({}),
summaryType: 'count',
summaryRenderer: function(v, params, data){
return ((v === 0 || v > 1) ? LANG['Tasks']+ ': '+ v: '1 '+LANG['Task']);
}
},
{header: LANG['lenght'], width: 60, sortable: true, dataIndex: 'lenght', renderer: formatKM, align: 'center',
summaryType: 'sum',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
//decimalSeparator: ',',
maxValue: 1000
//allowBlank: false
})
},
{header: LANG['time'], width: 30, sortable: true, dataIndex: 'time', align: 'center',
renderer: formatTimeStr,
summaryType: 'sum22',
editor: new Ext.form.NumberField({
//format: 'H:i',
allowNegative: false,
decimalPrecision: 2,
decimalSeparator: ':'
})
},
{header: LANG['vsr'], width: 50, sortable: true, dataIndex: 'vsr', renderer: formatKM, align: 'center',
summaryType: 'average',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
maxValue: 100
})
},
{header: LANG['vmax'], width: 50, sortable: true, dataIndex: 'vmax', renderer: formatKM, align: 'center',
summaryType: 'max',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
maxValue: 100
})
}, ............
I implemented this with easy way. My method is enable key events of number field and check if pressed key is ',' than add a '.' to value. That is the implementation;
Ext.create('Ext.form.Panel', {
title: 'On The Wall',
width: 300,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [{
xtype: 'numberfield',
anchor: '100%',
name: 'bottles',
fieldLabel: 'Bottles of Beer',
enableKeyEvents : true,
value: 99,
maxValue: 99,
minValue: 0,
listeners : {
keypress : function(view, e){
if(e.getKey() == 44)
view.setRawValue(view.getRawValue() +'.');
}
}
}]
});
I used this in my project but I don't know it's fit your request.
First, checking the value where it's number and has "." then put proper value based on this condition. If it's not has "." in the value then calling the replace function.
value.match(/^[0-9]+\.?[0-9]*$/) ? Number(value) : Number((value.replace(',', '.')));
The solution is found here: http://habrahabr.ru/post/137966/ (russian lang)
Since there is seen a solution for version> 4.0, and I have worth less had to change the code, replacing the standard JS functions.Vykladyvayu it here, maybe someone will need:
To begin at the header of the page to connect сursor.js
// Author: Diego Perini <dperini#nwbox.com>
var sb = new Array(
'leftbox', 'rightbox', 'scrollLeft', 'scrollRight',
'caretPos', 'maxLength', 'textLength', 'availLength',
'beforeCaret', 'afterCaret', 'selectedText'
)
var leftbox = {};
var rightbox = {};
var scrollLeft = {};
var scrollRight = {};
var caretPos = {};
var maxLength = {};
var textLength = {};
var availLength = {};
var beforeCaret = {};
var afterCaret = {};
var selectedText = {};
/*
for (var i in sb) {
var v = '"var '+sb[i]+' = {}"';
eval(v);
}
*/
var os = 0
var oe = 0
function update(o) {
var t = o.value, s = getSelectionStart(o), e = getSelectionEnd(o)
if (s == os && e == oe) return
caretPos.firstChild.nodeValue = s
maxLength.firstChild.nodeValue = o.getAttribute('maxLength')
textLength.firstChild.nodeValue = t.length
availLength.firstChild.nodeValue = o.getAttribute('maxLength') - t.length
afterCaret.firstChild.nodeValue = t.substring(s).replace(/ /g, '\xa0') || '\xa0'
beforeCaret.firstChild.nodeValue = t.substring(0, s).replace(/ /g, '\xa0') || '\xa0'
selectedText.firstChild.nodeValue = t.substring(s, e).replace(/ /g, '\xa0') || '\xa0'
rightbox.value = scrollRight.firstChild.nodeValue = t.substring(s).replace(/ /g, '\xa0') || '\xa0'
leftbox.value = scrollLeft.firstChild.nodeValue = t.substring(0, s).replace(/ /g, '\xa0') || '\xa0'
os = s
oe = e
return true
}
function setup() {
for (var i in sb) eval(sb[i] + ' = document.getElementById(sb[i])')
update(document.getElementById('textbox'))
}
function getSelectionStart(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
r.moveEnd('character', o.value.length)
if (r.text == '') return o.value.length
return o.value.lastIndexOf(r.text)
} else return o.selectionStart
}
function getSelectionEnd(o) {
if (o.createTextRange) {
var r = document.selection.createRange().duplicate()
r.moveStart('character', -o.value.length)
return r.text.length
} else return o.selectionEnd
}
and connect the plugin:
Ext.ns('Ext.ux.form');
/**
* #class Ext.ux.form.NumberInputFilter
* #extends Ext.form.NumberField
* Plugin (ptype = 'numberinputfilter')
* #param allowedDecimalSeparators: ',.:-' and other
* #ptype numberinputfilter
*/
Ext.ux.form.NumberInputFilter = Ext.extend(Ext.form.NumberField, {
initComponent: function(){
Ext.ux.form.NumberInputFilter.superclass.initComponent.call(this);
},
init : function(field) {
if (!(field && field.isXType('numberfield'))) {
return;
}
Ext.apply(field, {
allowedDecimalSeparators : this.allowedDecimalSeparators,
checkValue : function(newChar) {
var raw = this.getRawValue();
var el = Ext.get(this.id).dom;
// functions taken from here http://javascript.nwbox.com/cursor_position/
// and connected to a separate file cursor.js
var start = getSelectionStart(el);
var end = getSelectionEnd(el);
if (start != end) {
// delete the selected text from the expected values
raw = raw.substring(0, start) + raw.substring(end);
}
if (Ext.isEmpty(raw)) {
return (newChar == this.decimalSeparator || (this.minValue < 0) && newChar == '-') || newChar.search('/^\d$/');
}
if (raw.length == this.maxLength) {
return false;
}
if (newChar == this.decimalSeparator && (!this.allowDecimals || raw.indexOf(this.decimalSeparator) != -1)) {
return false;
}
// form the intended meaning
raw = raw.substring(0, start) + newChar + raw.substring(start);
raw = raw.split(new RegExp(this.decimalSeparator.replace("/([-.*+?^${}()|[\]\/\\])/g", "\\$1")));
return (!raw[0] || this.intRe.search(raw[0])) && (!raw[1] || this.decRe.search(raw[1]));
},
filterKeys : function(e){
if (e.ctrlKey && !e.altKey) {
return;
}
var key = e.getKey(),
charCode = String.fromCharCode(e.getCharCode());
if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
return;
}
if(!Ext.isGecko && e.isSpecialKey() && !charCode){
return;
}
// begin hack
if (charCode != this.decimalSeparator && this.allowedDecimalSeparators.indexOf(charCode) != -1) {
// if the input character is not a decimal point,
// But it is one of the alternatives,
// Replace it with a decimal point
charCode = this.decimalSeparator;
if (Ext.isIE) {
// in the IE code of the pressed key can be substituted directly
e.browserEvent.keyCode = charCode.charCodeAt(0);
} else if (Ext.isGecko) {
// for gecko-engine slowing Event
e.stopEvent();
// create a new event with the modified code of the pressed key
var newEvent = document.createEvent('KeyEvents');
// Mandatory event must be cancelable
// As it can be reversed, if the decimal
// Delimiter is entered in the field
newEvent.initKeyEvent(
e.browserEvent.type,
e.browserEvent.bubbles,
true, //cancellable
e.browserEvent.view,
e.browserEvent.ctrlKey,
e.browserEvent.altKey,
e.browserEvent.shiftKey,
e.browserEvent.metaKey,
0, // keyCode
charCode.charCodeAt(0) // charCode
);
e.getTarget().dispatchEvent(newEvent);
// event generated, nothing doing.
return;
} else if (Ext.isWebKit) {
// stopped event
e.stopEvent();
// into webkit initKeyboardEvent dont work, use TextEvent
if (this.checkValue(charCode)) {
var newEvent = document.createEvent('TextEvent');
newEvent.initTextEvent(
'textInput',
e.browserEvent.bubbles,
true,
e.browserEvent.view,
charCode
);
e.getTarget().dispatchEvent(newEvent);
}
return;
}
}
if (!this.checkValue(charCode)) {
e.stopEvent();
}
// end hack
},
updateDecimalPrecision : function(prec, force) {
if (prec == this.decimalPrecision && force !== true) {
return;
}
if (!Ext.isNumber(prec) || prec < 1) {
this.allowDecimals = false;
} else {
this.decimalPrecision = prec;
}
var intRe = '^';
if (this.minValue < 0) {
intRe += '-?';
}
intRe += '\\d' + (Ext.isNumber(this.integerPrecision) ? '{1,' + this.integerPrecision + '}' : '+') + '$';
this.intRe = new RegExp(intRe);
if (this.allowDecimals) {
this.decRe = new RegExp('^\\d{1,' + this.decimalPrecision + '}$');
} else {
delete this.decRe;
}
},
fixPrecision : function(value) {
// support decimalSeparators
if (Ext.isString(value)) {
value = value.replace(new RegExp('[' + (this.allowedDecimalSeparators + this.decimalSeparator).replace("/([-.*+?^${}()|[\]\/\\])/g", "\\$1") + ']'), '.');
}
// end hack
var me = this,
nan = isNaN(value),
precision = me.decimalPrecision;
if (nan || !value) {
return nan ? '' : value;
} else if (!me.allowDecimals || precision <= 0) {
precision = 0;
}
console.info(parseFloat(parseFloat(value).toFixed(precision)));
return parseFloat(parseFloat(value).toFixed(precision));
}
});
field.updateDecimalPrecision(field.decimalPrecision, true);
}
});
Ext.preg('numberinputfilter', Ext.ux.form.NumberInputFilter);
Use any separator, what we like in a digital field, just listing them:
.....
{header: LANG['lenght'], width: 60, sortable: true, dataIndex: 'lenght', renderer: formatKM, align: 'center',
summaryType: 'sum',
summaryRenderer: Ext.util.Format.cifres2,
editor: new Ext.form.NumberField({
allowNegative: false,
decimalPrecision: 2,
plugins: new Ext.ux.form.NumberInputFilter({
allowedDecimalSeparators : ',.-'
}),
maxValue: 1000,
allowBlank: false
})
},
.....
How to conditionally change a color of a marker for an ExtJS line chart base on an y-axis value?
You need to change the "fill" parameter on markerConfig
Maybe, this solution work for you: Change Color plot Points
When do you want the colour change?, on any specify event or action?
So, I should to overide the drawSeries method.
I define a new chart line component:
Ext.define('RogovIndex.Chart.Line', {
extend: 'Ext.chart.series.Line',
alias: 'series.multycolorline',
drawSeries: function () {
//a lot of code
}});
And then I fire custom event ("beforemarkerrender") at this part of code:
if (showMarkers) {
count = 0;
for (i = 0; i < ln; i++) {
if (me.items[i]) {
item = markerGroup.getAt(count++);
if (item) {
me.addEvents('beforemarkerrender');
me.fireEvent('beforemarkerrender', item, endMarkerStyle, store, i);
rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
if (!item.attr.hidden) {
item.show(true);
}
}
}
}
for (; count < markerCount; count++) {
item = markerGroup.getAt(count);
item.hide(true);
}
}
So, all I left to do is change the type of seria and subscribe on this event (check type and listeners parts):
series: [
{
type: 'multycolorline',
axis: 'left',
xField: 'ValueDateString',
yField: 'Value',
style: {
stroke: '#aaa'
},
markerConfig: {
type: 'circle',
size: 6,
radius: 6,
'stroke-width': 0,
fill: 'url(#v-2)'
},
highlight: {
size: 7,
radius: 7
},
tips: {
trackMouse: true,
minWidth: 170,
renderer: function (storeItem, item) {
this.update('Value 2: ' + storeItem.get('Value'));
}
},
listeners: {
'beforemarkerrender': function (marker, markerStyle, store, index) {
var item = store.getAt(index);
if (item.get('Equal')) {
markerStyle.fill = 'url(#v-1)';
} else {
markerStyle.fill = 'url(#v-2)';
}
}
}
}
]