Angular ng-options value DOES NOT update when scope changes - javascript

I'm having problems creating a select option dropdown using ng-options.
Scope structure
vm.filters = {
perPage: 10,
settings: {
perPage: {
intervals: [
{
label: 'All',
value: null
}, {
label: 10,
value: 10
}, {
label: 25,
value: 25
}, {
label: 50,
value: 50
}, {
label: 100,
value: 100
}
]
}
}
}
Dom Element (angular generates the option tags)
<select name="selectPerPage" data-ng-model="vm.filters.perPage" data-ng-options="interval.value as interval.label for interval in vm.filters.settings.perPage.intervals">
<option label="All" value="object:null">All</option>
<option label="10" value="number:10" selected="selected">10</option>
<option label="25" value="number:25">25</option>
<option label="50" value="number:50">50</option>
<option label="100" value="number:100">100</option>
</select>
When I change the value property of the first object in the intervals array from null to an actual number, scope for the option elements do not update. Why? How can I get it to update? It's almost like ng-model isn't referencing when scope updates.

If you use ng-options, you should not hard-coded options as you did in your select. You can only hard one (generally a null value).
<select ng-model="vm.filters.perPage"
ng-options="interval.value as interval.label for interval in vm.filters.settings.perPage.intervals">
<option label="All" value="">All</option>
</select>
I don't know what type interval.value is (array, object, string?), but it should be saved in vm.filters.perPage (supposing vm.filters exists).

you can try with ng-repeat, this will update the options dynamically. But unfortunately if the first element is selected when you update the data, the ng-change event won't be fired which means the value binded with ng-model won't be updated and you have to update it manually.
angular.module("app", [])
.controller("myCtrl", function($scope) {
$scope.filters = {
perPage: {
intervals: [{
label: 'All',
value: null
}, {
label: 10,
value: 10
}, {
label: 25,
value: 25
}, {
label: 50,
value: 50
}, {
label: 100,
value: 100
}]
}
};
$scope.update = function() {
$scope.filters.perPage.intervals[0].value = 1000;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<!--<select name="selectPerPage" data-ng-model="perPage" data-ng-options="interval.value as interval.label for interval in filters.perPage.intervals"> -->
<select name="selectPerPage" data-ng-model="perPage">
<option ng-repeat="interval in filters.perPage.intervals" value="{{interval.value}}">{{interval.label}}</option>
</select>
{{perPage}}
<button ng-click="update()">Update</button>
</div>

Related

change data on chart by selecting the year in dropdowns

i am displaying a chart in an angular app. I have two dropdowns to select starting year and ending year and i need my chart do display values based on the years picked. The X axis is composed by 18 cities, and the Y axis is the range "IAP" values. My data set is composed by "imob" and "rend" values, i.e. to calculate my "IAP" values for each city i need to do this operation:
IAP of city x from year 1 to year 2 = (imob.endingyear/imob.startingyear)/(rend.endingyear/rend.startingyear)
For example, when i select the starting year 2013 and the ending year 2018, the chart has to display the IAP values of each city in that interval of time. I hope my explanation is not too much confusing.
So far i have my dropdown in iapchart.controller.html file and it looks like this:
<div ng-controller="YearpickerController">
<form name="myForm">
<label for="singleSelect"> Ano Início: </label><br>
<select name="singleSelect" ng-model="data.singleSelect">
<option value="option-1i">2011</option>
<option value="option-2i">2012</option>
<option value="option-3i">2013</option>
<option value="option-4i">2014</option>
<option value="option-5i">2015</option>
<option value="option-6i">2016</option>
<option value="option-7i">2017</option>
<option value="option-8i">2018</option>
<option value="option-9i">2019</option>
<option value="option-10i">2020</option>
</select><br>
</form>
</div>
<div ng-controller="YearpickerController">
<form name="myForm">
<label for="singleSelect"> Ano Fim: </label><br>
<select name="singleSelect" ng-model="data.singleSelect">
<option value="option-1f">2011</option>
<option value="option-2f">2012</option>
<option value="option-3f">2013</option>
<option value="option-4f">2014</option>
<option value="option-5f">2015</option>
<option value="option-6f">2016</option>
<option value="option-7f">2017</option>
<option value="option-8f">2018</option>
<option value="option-9f">2019</option>
<option value="option-10f">2020</option>
</select><br>
</form>
</div>
To build my chart i have used Highcharts and so far its looking like this
But the dropdowns are not working, i need some sort of event listener right? I am new to angular js and i dont know how to go from here.
My code in iapchart.component.ts is looking like this so far:
#Component({
selector: 'app-widget-iapchart',
templateUrl: './iapchart.component.html',
styleUrls: ['./iapchart.component.scss']
})
export class IAPchartComponent implements OnInit, OnDestroy {
title = 'app';
imobsListSubs: Subscription;
imobsList: Imob[];
chartOptions;
Highcharts = Highcharts;
constructor(private imobsApi: ImobsApiService) { }
ngOnInit(): void{
this.imobsListSubs = this.imobsApi
.getImobs()
.subscribe(res => {
this.imobsList = res;
},
console.error
);
this.chartOptions = {
chart: {
type: 'column'
},
title: {
text: 'Índice Valorização'
},
xAxis: {
type: 'category',
labels: {
rotation: -45,
style: {
fontSize: '13px',
fontFamily: 'Verdana, sans-serif'
}
}
},
yAxis: {
min: 0,
title: {
text: 'Valorização'
}
},
legend: {
enabled: false
},
tooltip: {
pointFormat: 'Valor IAP: <b>{point.y:.1f} </b>'
},
credits: {
enabled: false
},
exporting: {
enabled: true,
},
series: [{
name: 'Valor IAP',
data: [
['Aveiro', 1.2],
['Beja', 0.8],
['Braga', 1.2],
['Bragança', 1.7],
['Castelo Branco', 1.1],
['Coimbra', 0.7],
['Évora', 1.4],
['Faro', 1.2],
['Guarda', 1.0],
['Leiria', 0.7],
['Lisboa', 1.5],
['Portalegre', 1.2],
['Porto', 1.4],
['Santarém', 1.2],
['Setúbal', 0.6],
['Viana do Castelo', 0.9],
['Vila Real', 1.3],
['Viseu', 0.8]
],
dataLabels: {
enabled: true,
rotation: 0,
color: '#000000',
align: 'right',
format: '{point.y:.1f}', // one decimal
y: -20, // 10 pixels down from the top
style: {
fontSize: '13px',
fontFamily: 'Times New Roman, sans-serif'
}
}
}]
};
HC_exporting(Highcharts);
setTimeout(() =>{
window.dispatchEvent(
new Event('resize')
);
}, 300);
}
ngOnDestroy() {
this.imobsListSubs.unsubscribe();
}
The data I provided there is only an example of IAP values, i also need to know how to write my dataset there without making the code very big...
The functionality that you are looking for is already built into the Highcharts stock, the only difference is that instead of the dropdown list you can use the range selector input to manipulate the axis range. Range selectors are using the axis.setExtremes() method under the hood, so to make it work you would need to call the axis.setExtremes() method with ranges given in the ms.
API references:
https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes
Live demo:
https://stackblitz.com/edit/highcharts-angular-basic-line-brzswa?file=src/app/app.component.ts

Is there a stable way to set echarts axis limits from html id value

I'm writing an electron.js app where I'm using echart.js to display a heatmap.
Two html input tags are used to let the user set the axis limits for the heatmap.
<span>
<input type="number" id="min_limit" value= 1>
<input type="number" id="max_limit" value= 2>
</span>
In the javascript code I'm simply using the following to get this content:
data_min = document.getElementById('min_limit').value;
data_max = document.getElementById('max_limit').value;
The problem occurs when I try to use these two variables within the echart option:
option = {
...
visualMap: {
min: data_min,
max: data_max,
...
}
}
I get the following error:
"Uncaught DOMException: Failed to execute 'addColorStop' on 'CanvasGradient': The value provided ('undefined') could not be parsed as a color."
The interesting thing is that the program works when I try any of the following (only one of the input values respectively but not both at the same time):
option = {
...
visualMap: {
min: 0,
max: data_max,
... // No errors
}
}
option = {
...
visualMap: {
min: data_min,
max: 1,
... // No errors
}
}
option = {
...
visualMap: {
min: 0,
max: 1,
... // No errors
}
}
And calling*:
myChart.setOption(option);
To display the chart.
Any suggestions? I'm new to html and JavaScript so there may be an obvious error that I'm missing.
*EDIT: Forgot to mention that I'm using "setOption" to change the appearance.
You can't set echarts option directly. Need to use setOption for add data, see documentation.
Update
Your implementation does not work because Echarts not understand what need do with inputs. Getting value you in fact calling the getter (special built-in function) of input component. The Echarts does't know how to call getter, it's expecting just value.
With regard to the implementation I would have the following way.
Define function that read value for inputs.
Define function that update Echart's min/max.
Attach event listener to body element for listen inputs change event.
And then after receive the event call the update Echarts.
var myChart = echarts.init(document.getElementById('main'));
var hours = ['12a', '1a', '2a', '3a', '4a', '5a', '6a',
'7a', '8a', '9a','10a','11a',
'12p', '1p', '2p', '3p', '4p', '5p',
'6p', '7p', '8p', '9p', '10p', '11p'];
var days = ['Saturday', 'Friday', 'Thursday',
'Wednesday', 'Tuesday', 'Monday', 'Sunday'];
var data = [[0,0,5],[0,1,1],[0,2,0],[0,3,0],[0,4,0],[0,5,0],[0,6,0],[0,7,0],[0,8,0],[0,9,0],[0,10,0],[0,11,2],[0,12,4],[0,13,1],[0,14,1],[0,15,3],[0,16,4],[0,17,6],[0,18,4],[0,19,4],[0,20,3],[0,21,3],[0,22,2],[0,23,5],[1,0,7],[1,1,0],[1,2,0],[1,3,0],[1,4,0],[1,5,0],[1,6,0],[1,7,0],[1,8,0],[1,9,0],[1,10,5],[1,11,2],[1,12,2],[1,13,6],[1,14,9],[1,15,11],[1,16,6],[1,17,7],[1,18,8],[1,19,12],[1,20,5],[1,21,5],[1,22,7],[1,23,2],[2,0,1],[2,1,1],[2,2,0],[2,3,0],[2,4,0],[2,5,0],[2,6,0],[2,7,0],[2,8,0],[2,9,0],[2,10,3],[2,11,2],[2,12,1],[2,13,9],[2,14,8],[2,15,10],[2,16,6],[2,17,5],[2,18,5],[2,19,5],[2,20,7],[2,21,4],[2,22,2],[2,23,4],[3,0,7],[3,1,3],[3,2,0],[3,3,0],[3,4,0],[3,5,0],[3,6,0],[3,7,0],[3,8,1],[3,9,0],[3,10,5],[3,11,4],[3,12,7],[3,13,14],[3,14,13],[3,15,12],[3,16,9],[3,17,5],[3,18,5],[3,19,10],[3,20,6],[3,21,4],[3,22,4],[3,23,1],[4,0,1],[4,1,3],[4,2,0],[4,3,0],[4,4,0],[4,5,1],[4,6,0],[4,7,0],[4,8,0],[4,9,2],[4,10,4],[4,11,4],[4,12,2],[4,13,4],[4,14,4],[4,15,14],[4,16,12],[4,17,1],[4,18,8],[4,19,5],[4,20,3],[4,21,7],[4,22,3],[4,23,0],[5,0,2],[5,1,1],[5,2,0],[5,3,3],[5,4,0],[5,5,0],[5,6,0],[5,7,0],[5,8,2],[5,9,0],[5,10,4],[5,11,1],[5,12,5],[5,13,10],[5,14,5],[5,15,7],[5,16,11],[5,17,6],[5,18,0],[5,19,5],[5,20,3],[5,21,4],[5,22,2],[5,23,0],[6,0,1],[6,1,0],[6,2,0],[6,3,0],[6,4,0],[6,5,0],[6,6,0],[6,7,0],[6,8,0],[6,9,0],[6,10,1],[6,11,0],[6,12,2],[6,13,1],[6,14,3],[6,15,4],[6,16,0],[6,17,0],[6,18,0],[6,19,0],[6,20,1],[6,21,2],[6,22,2],[6,23,6]];
data = data.map(function (item) {
return [item[1], item[0], item[2] || '-'];
});
option = {
tooltip: {
position: 'top'
},
animation: false,
grid: {
height: '50%',
top: '10%'
},
xAxis: {
type: 'category',
data: hours,
splitArea: {
show: true
}
},
yAxis: {
type: 'category',
data: days,
splitArea: {
show: true
}
},
visualMap: {
min: 0,
max: 10,
calculable: true,
orient: 'horizontal',
left: 'center',
bottom: '15%'
},
series: [{
name: 'Punch Card',
type: 'heatmap',
data: data,
label: {
show: true
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
function updateChart(values){
myChart.setOption({ visualMap: values })
};
document.body.addEventListener('change', (event) => {
var targets = document.querySelectorAll('.changeable');
var new_values = [...targets].map(target => {
var key_name = target.id.split('_')[0];
return { [key_name]: target.value };
});
var new_values = Object.assign(new_values[0], new_values[1]);
updateChart(new_values);
});
myChart.setOption(option);
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.7.0/echarts.min.js"></script>
<span>
<input type="number" class="changeable" id="min_limit" value= 1>
<input type="number" class="changeable" id="max_limit" value= 5>
</span>
<div id="main" style="width: 600px;height:400px;"></div>

How can I add custom props to an Option using React Select?

My goal is to add a tooltip when the user expands the menu and hovers over the items listed. To do this, I need to add data-tip and data-for to the menu items.
The options I'm passing to React Select looks like this:
[
{ value: 1, label: 'Onw' },
{ value: 2, label: 'Two' },]
I'd like to just add the prop info there but I don't think there's a way. Do I need to customize the rendering of each option? Any tips on how to do this would be appreciated. Thanks.
You can simply wrap your custom Option inside a function then assign custom props for it
components={{ Option: (optionProps) => <Option {...optionProps} onEditItem={onEditItem}
onDeleteItem={onDeleteItem} /> }}
react-select seems to be only accepting value and label as props (and possibly other HTML attributes that an <option> accepts), so I did this with the help of a function:
const options = [
{ value: "a", label: "A", tooltip: "yellow" },
{ value: "b", label: "B", tooltip: "orange" },
{ value: "c", label: "C", tooltip: "blue" },
];
function getTooltip(value) {
return options.find(x => x.value === value).tooltip;
}
/* A custom option component as an example */
const Option = (props) => {
return (
<components.Option {...props}>
<div data-tip={getTooltip(props.value)}>
{props.label}
</div>
</components.Option>
);
};
/* Pass the custom component to <Select> */
return (
<Select options={options} components={{ Option }} />
)

Redraw Highcharts on Vuejs app

I'm building a little pension calculator where I want to show the value of the pension pot depending on the users current and retirement age.
I'm looking to take the user's data, number fields bound to a Vue model and then computing the value of the pot based on that data.
That works just fine but having an issue with not being able to have Highcharts redraw the chart when the models change.
The redrawChart method fires but the chart stays the same with the console telling me Chart.redraw is not a function.
Redraw is an option for the Highcharts API so not sure where I'm failing.
Here's the markup:
<div id="app">
<input type="number" min="55" v-model.number="age" v-on:change="redrawChart">{{ age }}
<br>
<input type="number" min="1" max="1000000" v-model.number="currentPensionValue" v-on:change="redrawChart">{{ currentPensionValue }}
<br>
<input type="number" min="56" v-model.number="retireAge" v-on:change="redrawChart">{{ retireAge }}
<br>
<Chart :data="data" :steven="steven" :age-pot-value="agePotValue"></Chart>
and the associated Vue code
const Chart = Vue.component('Chart', {
props: ['data', 'steven', 'agePotValue'],
template: `
<div>
<p>{{ data }}</p>
<h1>{{ steven }}</h1>
<h1>{{ agePotValue }}</h1>
<div id="thechart"></div>
</div>
`,
mounted() {
var highchartsOptions = {
chart: {
type: 'area',
renderTo: 'thechart'
},
credits: {
enabled: false
},
tooltip: {
enabled: false
},
title: {
text: ''
},
xAxis: {
allowDecimals: false,
title: {
text: 'Age'
}
},
yAxis: {
title: {
text: 'Pot Value'
},
labels: {
formatter: function () {
return '£' + this.value / 1000 + 'k';
}
},
opposite: true,
},
plotOptions: {},
series: [{
name: '',
data: this.agePotValue
}],
credits: false
}
Highcharts.chart(highchartsOptions)
}
});
new Vue({
el: '#app',
data: {
age: 55,
currentPensionValue: 22000,
retireAge: 87
},
computed: {
data() { return (this.currentPensionValue) / (this.retireAge - this.age) },
steven() { return this.data * 1.4 },
agePotValue() {
var vm = this;
var agePotValue = [[vm.age, (vm.data)], [vm.retireAge, vm.currentPensionValue]];
return agePotValue;
}
},
components: { Chart },
methods: {
redrawChart() {
Chart.redraw();
}
}
})
Here's the the fiddle https://jsfiddle.net/stevieg_83/naLqzunz/11/
Any help appreciated.
Please see this working fiddle
https://jsfiddle.net/naLqzunz/12/
I make a little change on your approach, component will be watching for changes
watch:{
data(){this.redraw()},
steven(){this.redraw()},
agePotValue(){this.redraw()},
},
and redraws method just update chart data (that fires redraw automatically)
methods:{
redraw(){
this.chart.series[0].setData(this.agePotValue,true);
}
},

Two-way binding is not recognising variable change in AngularJS

I have a slider that is used to changed a value in my controller like so
index:
<div>
<rzslider
rz-slider-model="slider.value"
rz-slider-options="slider.options">
</rzslider>
<p>{{slider.value}}</p>
</div>
controller:
app.controller('gbarchartController', function($scope) {
$scope.slider = {
value: 20,
options: {
floor: 0,
ceil: 30
}
};
When i change the value of the slider, the code below displays it on screen as 2 way binding should.
<p>{{slider.value}}</p>
The problem is , if i try to access this variable through a function in my controller, it uses the initial value of 20 that has been set. eg. If i change the slider to 10, the below code still uses the value 20. the variable in question is $scope.slider.value . The value is used to cut the length of an array, that in turn is used to display a graph using highchart.js.
$scope.graph = function(metric) {
Highcharts.chart(metric, {
chart: {
type: 'column'
},
title: {
text: 'Number Of Executions'
},
colors: [
'#7CFC00', '#FF0000'
],
xAxis: {
categories: numExe.hash.slice(0, $scope.slider.value),
title: {
text: 'Query Hash Value'
}
},
yAxis: {
title: {
text: '% of Total Executions'
}
},
tooltip: {
shared:true
},
series: [{
name: 'Baseline',
data: numExe.base.slice(0, $scope.slider.value)
},
{
name: 'Comparative',
data: numExe.compare.slice(0, $scope.slider.value)
}]
});
}
Long story short, The variable changes in the html but does not change in the controller. Any help would be great thanks.
I don't think Highcharts monitors its data for changes, so it won't detect changes to your series data (see docs here). Additionally the series data passed in to the graph is static, so it won't update after the slider changes. You'll need to create a local Series variable to hold the series data, and update the series data when the slider changes. I'm not sure what numExe is so I can't say how the data is generated, but the Series object has setData() method (with a redraw parameter) that should do the trick.

Categories

Resources