How to build multiple drill down using highchart correctly? - yii

I want to make multiple drill down chart using framework Yii2. I want to make student's chart based by age. I have installed highcharts via composer. I also have registered it to my php file, but I don't know why the drilldown chart doesn't work. I think, it's because my incorrect codes.
Here is my tables:
facultyin2011
department2011
major2011
Here is my controller:
<?php
namespace app\controllers;
use yii\web\Controller;
use yii\helpers\Json;
class MagisterrinciController extends Controller
{
public function actionIndex()
{
//data usia fakultas
$faculty = (new \yii\db\Query())
->select(['Faculty'])
->from('facultyin2011')
->limit(10)
->column();
$age1 = (new \yii\db\Query())
->select(['lessthan25'])
->from('facultyin2011')
->limit(10)
->column();
$age2 = (new \yii\db\Query())
->select(['btween25to29'])
->from('facultyin2011')
->limit(10)
->column();
$age1 = array_map('floatval', $age1);
$age2 = array_map('floatval', $age2);
$data['ageforfacultystudent'] = json_encode($faculty);
$data['age1'] = json_encode($age1);
$data['age2'] = json_encode($age2);
// next
$department = (new \yii\db\Query())
->select(['Department'])
->from('department2011')
->limit(10)
->column();
$agedepartment1 = (new \yii\db\Query())
->select(['lessthan25'])
->from('department2011')
->limit(10)
->column();
$agedepartment2 = (new \yii\db\Query())
->select(['btween25to29'])
->from('department2011')
->limit(10)
->column();
$agedepartment1 = array_map('floatval', $agedepartment1);
$agedepartment2 = array_map('floatval', $agedepartment2);
$data['agefordepartmentstudent'] = json_encode($department);
$data['agedepartment1'] = json_encode($agedepartment1);
$data['agedepartment2'] = json_encode($agedepartment2);
//next
$majorstudent = (new \yii\db\Query())
->select(['major'])
->from('major2011')
->limit(10)
->column();
$agemajor1 = (new \yii\db\Query())
->select(['lessthan25'])
->from('department2011')
->limit(10)
->column();
$agemajor2 = (new \yii\db\Query())
->select(['btween25to29'])
->from('department2011')
->limit(10)
->column();
$agemajor1 = array_map('floatval', $agemajor1);
$agemajor2 = array_map('floatval', $agemajor2);
$data['ageformajorstudent'] = json_encode($majorstudent);
$data['agemajor1'] = json_encode($agemajor1);
$data['agemajor2'] = json_encode($agemajor2);
return $this->render('index',$data);
}
}
Here is my index view:
<?php
use app\assets\HighchartsAsset;
HighchartsAsset::register($this);
$this->title = 'Highcharts Test';
?>
<!-- data selanjutnya -->
<div class="container">
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="x_panel">
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
<?php $this->registerJs("
$(function () {
// Create the chart
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Basic drilldown'
},
xAxis: {
categories: $ageforfacultystudent
},
legend: {
enabled: false
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
}
}
},
series: [
{
name: 'lessthan25',
data: $age1,
drilldown: 'department1'
},
{
name: 'btween25to29',
data: $age2,
drilldown: 'department2'
}],
drilldown: {
series: [{
id: 'department1',
name: 'Departemen',
data: [{
name: 'lessthan25',
data: $agedepartment1,
drilldown: 'major1'
},
{
name: 'btween25to29',
data: $agedepartment2,
drilldown: 'major2'
}
]
}, {
id: 'major1',
data: $agemajor1
},
{
id: 'major2',
data: $agemajor2
}]
}
})
});
")?>
</div>
</div>
here is the result:
those charts can't be drilled down.
When I checked the console, there is no error message.
Could anybody please correct my codes above? It's gonna help me a lot. Thank you in advance

Related

How to add a Date Selector to Chartjs in the context of Vue.js

I am new to Vuejs, i have implemented chart using Chartjs, i want to add date selector to my chartjs. I have used 4 buttons, so whenever i click any of the button my chart should change accordingly.
Initially i tried implementing each button with each method, but i don't feel that as an effective way to do so.
> Here is my code: https://jsfiddle.net/95kspb70/3/
I want the chart to change when ever i click any of the button that i have used, i mean i have used 4 buttons, suppose say i clicked 3m, my chart should show previous 3months data only.
Please help me with this, I don't know how exactly i should do it in order to solve this.
You need to filter the data based on the date and rerender the chart for the new data.
Here is the working jsfiddle
I add click event #click="selectPeriod" inside html:
<div id='app'>
<div id="range-selector">
<input type="button" id="1m" #click="selectPeriod" class="period ui-button" value="1m" />
<input type="button" id="3m" #click="selectPeriod" class="period ui-button" value="3m" />
<input type="button" id="6m" #click="selectPeriod" class="period ui-button" value="6m" />
<input type="button" id="all" #click="selectPeriod" class="period ui-button" value="All" />
</div>
<canvas ref='chart' width='800' height='600'></canvas>
</div>
and following js code:
new Vue({
data: () => ({
date: [
1567295500.0,
1567893700.0,
1568615220.0,
1569024000.0,
1569888120.0,
1572456400.0,
1572772560.0,
1574809200.0,
1576706160.0,
1577718000.0,
1578610800.0,
1582650220.0,
1583174000.0,
1584063360.0,
1587427200.0,
1587573420.0,
1588637800.0,
1589587420.0,
1589989800.0
],
challenge: [
0.45,
2.12,
2.55,
3.15,
4.16,
5.56,
6.258,
7.256,
8.364,
9.154,
10.245,
11.654,
12.364,
13.785,
14,
15.32,
16.87,
17.852,
18.254,
19
],
data: []
}),
mounted() {
this.data = this.date.map((date, index) => ({
x: new Date(date * 1000),
y: this.challenge[index]
}))
this.buildGraph(this.data);
},
methods: {
buildGraph(data) {
console.log(data.length);
let ctx = this.$refs.chart.getContext('2d')
let chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'month',
displayFormats: {
month: 'MMM YYYY'
}
}
}],
yAxes: [{
ticks: {
callback: function(value, index, values) {
return value + 'k';
}
}
}]
}
}
})
},
selectPeriod(event) {
let period = event.target.value;
let minValue = new Date(Math.max(...this.date) * 1000);
let axisXMin = new Date(Math.min(...this.date) * 1000);
switch (period) {
case "1m":
minValue.setMonth(minValue.getMonth() - 1);
break;
case "3m":
minValue.setMonth(minValue.getMonth() - 3);
break;
case "6m":
minValue.setMonth(minValue.getMonth() - 6);
break;
case "1y":
minValue.setYear(minValue.getFullYear() - 1);
break;
default:
minValue = axisXMin;
}
let data = this.data.filter(el => {
return el.x >= minValue
});
this.buildGraph(data);
}
}
}).$mount('#app')

In Nuxt.js, load a simple Google map

I am doing this successfully on another project and I do not understand at all, why I am getting an empty div here instead of a map. The map should render here:
<template>
<div class="row mt-5">
<div style="height: 500px; width: 500px;" ref="map"></div>
</div>
</template>
And the code:
head() {
return {
title: 'Properties',
script: [{
src: "https://maps.googleapis.com/maps/api/js?key=XXX",
hid: "map",
defer: true,
}]
}
},
mounted(){
const mapOptions = {
zoom: 18,
center: new window.google.maps.LatLng(43.758773, -79.391785),
disableDefaultUI: true,
zoomControl: true
};
const map = new window.google.maps.Map(this.$refs.map, mapOptions);
const position = new window.google.maps.LatLng(43.758773, -79.391785)
const marker = new window.google.maps.Marker({ position })
marker.setMap(map)
},
Inside your mapOptions object change center to center: { lat: 43.758773, lng: -79.391785 }
You can reference this https://developers.google.com/maps/documentation/javascript/examples/map-latlng-literal

vue select all checkboxes with value generated by computed methods

My page has a Select All checkbox at the top where upon clicking it, it should have checked all the checkboxes. Here's my code:
<div class="columns bottom-border">
<div class="column">Student</div>
<div><a v-on:click="revokePoints()">Revoke</a><br/><input type="checkbox" v-model="selectAll">Select All</div>
</div>
<div class="columns" v-for="(behavior) in sortBehaviors(behaviorList)" :key="behavior._id">
<div class="column">{{ behavior.studentID.firstName }} </div>
<div class="column is-1"><center><input type="checkbox" :value="setCheckedValue(behavior.dataType,behavior._id,behavior.studentID._id,behavior.actionDate)" :id="setCheckedValue(behavior.dataType,behavior._id,behavior.studentID._id,behavior.actionDate)" v-model="checkedIDs"></center></div>
</div>
data() {
return {
positiveName: '',
behaviorList: [],
checkedIDs: [],
selected: []
};
},
computed:{
selectAll: {
get: function () {
return this.behaviorList ? this.selected.length == this.behaviorList.length : false;
},
set: function (value) {
var mySelected = [];
let self = this;
if (value) {
this.behaviorList.forEach(function (behavior) {
var getDataType = behavior.dataType
var getID = behavior._id
var getStudentID = behavior.studentID._id
var getActionDate = behavior.actionDate
var getGeneratedID = self.setCheckedValue(getDataType,getID,getStudentID,getActionDate);
mySelected.push(getGeneratedID);
});
}
self.selected = mySelected;
console.log("self selected")
console.log(self.selected)
}
}
},
methods: {
setCheckedValue(dataType,id,studentID,actionDate){
return "1:" + dataType + "|2:" + id + "|3:" + studentID + "|4:" + actionDate
},
revokePoints(){
var pointsToRevoke = this.checkedIDs;
console.log("pointsToRevoke")
console.log(pointsToRevoke)
}
When I click on the Select All checkbox, console will display that self.selected will have the id of all the checkboxes. But the issue is the checkbox for all the values displayed are not checked...
It is difficult to help because your code is not completed. But I would approach that a bit differently. I hope this codepen can help you.
const list = [
{ id: 1, name: 'New York', checked: true },
{ id: 2, name: 'Sydney', checked: false },
{ id: 3, name: 'London', checked: false },
{ id: 4, name: 'Chicago', checked: true }
]
new Vue({
el: '#app',
data() {
return {
list,
isAllChecked: false
};
},
methods: {
checkAll: function() {
this.list = this.list.map(city => ({ ...city,
checked: !this.isAllChecked
}))
this.isAllChecked = !this.isAllChecked
}
},
computed: {
getAllCheckedIDs: function() {
return this.list.filter(city => city.checked).map(city => city.id)
},
getNotAllCheckedIDs: function() {
return this.list.filter(city => !city.checked).map(city => city.id)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="city in list" :key="city.id">
<label>
{{city.name}}
<input type="checkbox" v-model="city.checked" />
</label>
</li>
</ul>
<button #click="checkAll">Check all</button>
<br/>
<div>Checked IDs: {{getAllCheckedIDs}}</div>
<div>Not Checked IDs: {{getNotAllCheckedIDs}}</div>
</div>

Vue.js - Reactivity of a select whose value is the selected object and not its id

I'm using vue.js 2.3 and element-ui. I'm facing reactivity issues with a select dropdown whose value is the item picked.
Problems
The select is not automatically filled with the initial object value
Notices
If I change :value="item" to :value="item.name"
and form: {option: {name:'blue', price: ''}} to form: {option:'blue'}
It is working
Questions
Is there a way to make the select dropdown fully reactive when its value is not just a string or a id but rather the whole object that has been selected
https://jsfiddle.net/LeoCoco/aqduobop/
<
div style='margin-bottom:50px;'>
My form object :
{{form}}
</div>
<el-button #click="autoFill">Auto fill</el-button>
<el-select v-model="form.option" placeholder="Select">
<el-option v-for="item in options" :key="item.name" :label="item.name" :value="item">
</el-option>
</el-select>
</div>
var Main = {
data() {
const options = [
{name: 'blue', price: '100$'},{name: 'red', price: '150$'},
]
return {
currentItem: 0,
options,
form: {
option: {name:'', price: ''},
},
testForm: {
option:{name:'red', price: '150$'}
},
}
},
methods: {
autoFill() {
this.form = Object.assign({}, this.testForm); // -> Does not work
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Your issue is that when the selected value is an object, then form.option needs to be the same object in order for it to be selected in the select list.
For example if I change the fiddle code to this, I think it works the way you expect.
var Main = {
data() {
const options = {
'color': [{name: 'blue', price: '100$'},{name: 'red', price: '150$'}],
'engine': [{name: '300hp', price: '700$'},{name: '600hp', price: '2000$'}],
}
let currentCategory = 'color'
return {
currentCategory,
currentItem: 0,
options,
form: {
option: options[currentCategory][0]
},
testForm: {
option:{name:'blue', price: '100$'}
},
}
},
methods: {
autoFill() {
this.form = Object.assign({}, this.testForm); // -> Does not work
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Here is your fiddle updated.
You said your values are coming from an ajax call. That means that when you set form.option you need to set it to one of the objects in options[currentCategory].

Keeping track of an array of components

I have a really simple Vue app:
<div id="app">
<item v-for="item in items" v-bind:title="item.title" v-bind:price="item.price"
#added="updateTotal(item)"></item>
<total v-bind:total="total"></total>
</div>
And a Vue instance:
Vue.component('item',{
'props' : ['title', 'price'],
'template' : "<div class='item'><div>{{ title }} – ${{total}} </div><button class='button' #click='add'>Add</button></div>",
'data' : function(){
return {
quantity : 0
}
},
'computed' : {
total : function(){
return (this.quantity * this.price).toFixed(2);
}
},
methods : {
add : function(){
this.quantity ++;
this.$emit('added');
}
}
});
Vue.component('total', {
'props' : ['total'],
'template' : "<div class='total'>Total: ${{ total }}</div>",
});
var app = new Vue({
'el' : '#app',
'data' : {
'total' : 0,
'items': [
{
'title': 'Item 1',
'price': 21
}, {
'title': 'Item 2',
'price': 7
}
],
},
methods : {
'updateTotal' : function(item){
console.log('updating');
this.total += item.price;
}
}
});
Demo link:
https://codepen.io/EightArmsHQ/pen/rmezQq?editors=1010
And what I'd like to do is update the <total> component as the various items are added to the cart. I have it working at the moment, however it doesn't seem very elegant.
Right now, I add the price of each item to a total. What I'd really like to do is have the total as a computed property, and then every time an item component is changed, loop through them all adding the quantity * price of each. Is there a way I can do this?
One option I have come up with just now is replacing my updateTotal method in the main app to the below:
methods : {
'updateTotal' : function(item){
item.quantity += 1;
}
},
computed : { total : function(){
var t = 0;
for(var i = 0; i < this.items.length; i ++){
t += this.items[i].quantity * this.items[i].price;
}
return t;
}
}
So, beginning to store the quantity of each item inside the Vue app, not the component. But it makes more sense to store the quantity of each item inside its own component... doesn't it? What is the best way of handling this?
Maybe counter-intuitively, the components only need their data as props. The items (as data objects) are defined in the parent; just define quantity there, too. Then use those data items in the components, but make changes via events to the parent.
With an array that includes the quantities, it's easy to create the computed total you want.
Vue.component('item', {
'props': ['item'],
'template': "<div class='item'><div>{{ item.title }} – ${{total}} </div><button class='button' #click='add'>Add</button></div>",
'computed': {
total: function() {
return (this.item.quantity * this.item.price).toFixed(2);
}
},
methods: {
add: function() {
this.$emit('added');
}
}
});
Vue.component('total', {
'props': ['total'],
'template': "<div class='total'>Total: ${{ total }}</div>",
});
var app = new Vue({
'el': '#app',
'data': {
'items': [{
'title': 'Item 1',
'price': 21,
'quantity': 0
}, {
'title': 'Item 2',
'price': 7,
'quantity': 0
}],
},
computed: {
total() {
return this.items.reduce((a, b) => a + (b.price * b.quantity), 0).toFixed(2);
}
},
methods: {
updateTotal(item) {
++item.quantity;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<div id="app">
<item v-for="item in items" v-bind:item="item" #added="updateTotal(item)"></item>
<total v-bind:total="total"></total>
</div>