I want to display some charts with Highcharts in my Symfony3 app.
To do this, I have to get some data from my DB and send it to Highcharts. Here comes the problem : I don't know how to do this. I created an array with all the data needed but I can't use it just by setting up a twig variable.
How can I send these datas ?
Here's my code :
Controller
$repartitionArray = [];
$statsArray = [];
for ($i = 0 ; $i < count($statscolle) ; $i++) {
$groupe[$i] = $statscolle[$i]->getGroupes()->getName();
$repartitionsColles[$i] = $em->getRepository(RepartitionColle::class)->findBy(['idStatColle' => $statscolle], ['note' => 'ASC']);
for ($j = 0; $j < count($repartitionsColles[$i]); $j++){
$note[$j] = $repartitionsColles[$i]->getNote();
$nombre[$j] = $repartitionsColles[$i]->getNombre();
$repartitionArray[$i] = [$note[$j], $nombre[$j]];
}
$statsArray[] = ["name" => $groupe[$i], "data" => $repartitionArray[$i]];
}
$series = $statsArray;
return $this->render("PACESStatistiqueBundle:Default:collapsideColle.html.twig", ["titre" => $colle->getNom(),
"data" => $series]);
Twig :
<div class="center">
<div id="repNotesColles" style="width:100%; height:400px;"></div>
</div>
{% block javascripts %}
<script src="http://code.highcharts.com/highcharts.js"></script>
<script>
$(document).ready(function () {
var repartitionNotesOfColle = Highcharts.chart('repNotesColles', {
chart: {
type: 'column'
},
title: {
text: {{ titre }}
},
xAxis: {
title: {
text: 'Notes'
}
},
yAxis: {
title: {
text: 'Effectif'
}
},
series:
});
})
</script>
{% endblock %}
Related
I am trying to get Jinja braces to stay in line for a javascript variable used in an apexchart.js call for a flask HTML page.
labels = JSON.parse({{ labels | tojson}})
data = JSON.parse({{ data | tojson}})
The problem I am having is that when I save the html the braces get formatted apart.
labels = JSON.parse({
{
labels | tojson
}
})
data = JSON.parse({
{
data | tojson
}
})
Is there away to stop Visual Studio Code from splitting the Jinja code apart?
Current Code Below:
<!-- language: lang-html -->
<!-- templates/index.html -->
{% extends 'base.html' %} {% block content %}
<title>Dashboard | {{title}}</title>
<div class="flex h-screen justify-center">
<div id="chart" class="h-1/4 w-1/2 content-center"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
labels = JSON.parse({
{
labels | tojson
}
})
data = JSON.parse({
{
data | tojson
}
})
var options = {
colors: ['#01ca95'],
chart: {
type: 'bar'
},
dataLabels: {
enabled: false
},
series: [{
name: 'sales',
data: data
}],
xaxis: {
categories: labels
},
yaxis: {
labels: {
formatter: function(data) {
//Setup new Formatting class variable
var Formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
return Formatter.format(data);
}
}
},
}
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
</script>
{% endblock content %}
i'm working in Django, to draw some graph in function of my modules. ie: if i have 2 modules, i want 2 charts , if 8 modules, 8 charts.
Modules are integreted in a plugin. so i can list all modules found in a plugin. i did this in Django:
def plugin_graph(request, hub_uid, plugin_uid):
request.session['is_admin'] = True
hub = Hub.objects.get(mac=hub_uid)
fields = []
if request.method == 'GET':
if 'field' in request.GET:
fields.append(request.GET['field'])
plugin = Plugin.objects.get(uid=plugin_uid, hub=Hub.objects.get(mac=hub_uid))
#on recupere lensemble desmodules de ce plug
modules = plugin.get_modules_list()
#pour chak module, tracer son graph
for m in modules:
modules_list = {'uid':m.uid,
'name':m.name,
'version':m.version,
'type':m.type,
'time':m.last_time(),
'rssi':m.rssi(),
'status_icon':m.status_icon()}
module = Module.objects.get(uid=m.uid, plugin=Plugin.objects.get(uid=plugin_uid, hub=Hub.objects.get(mac=hub_uid)))
historic = sorted(ModuleDocument.objects.filter(module=module), key=getKey)
values = get_graph_values(historic=historic, dates=None, fields=fields)
print values
field = None if len(fields) < 1 else fields[0]
return render(request, "admin/graph2.html",
{
'values': values,
'hub': hub_uid,
'plugin': plugin_uid,
'uid': m.uid,
'module': module,
'fields': module.get_number_fields(),
'field': field,
'level': 'module',
}
)
After recovering all my modules i draw the charts like that in javascript:
<script>
var ctx = document.querySelector("#chart");
var data = JSON.parse('{{ values }}'.replace(/"/g, '"'));
var labels = [];
var values = [];
var beginAtZero = true;
for (obj in data) {
labels.push(data[obj].x);
values.push(data[obj].y);
if (data[obj].y < 0) {
beginAtZero = false;
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: "{{field}}",
data: values,
borderColor: '#97168F'
}]
},
options: {
scales: {
xAxes: [{
time: {
unit: 'day'
}
}],
yAxes: [{
ticks: {
beginAtZero:beginAtZero
}
}]
}
}
});
</script>
My problem is just one chart is printed. i would like to put it in a for loop to recuperate all data & labels of each module to draw normally all charts i want.
Thank u for your help
You need to utilise the forloop.counter method in the django template to dynamically name a number of items, otherwise the charts will just get overwritten each time and you are just left with the last chart. Its worth noting that I include this code in the block content not a specific js block.
In the example below, the items that are dynamically named are;
The container div
<div id = 'emg-{{forloop.counter}}'>
The canvas
<canvas id="myChart-{{forloop.counter}}">
The chart JS function name
var func_name = "container-{{forloop.counter}}";
func_name = new Chart(
document.getElementById('myChart-{{forloop.counter}}'),{
type: 'scatter',
I've included the full code below. I appreciate my example is different from your posted code, but it demonstrates the concept of dynamically creating certain elements.
{% for time, name_list, value_list, latitude_list, longitude_list, polyline_list in data %}
{% with forloop.counter as track_object_counter %}
<div id = 'emg-{{forloop.counter}}'>
{% for item in emg_data %}
{% if item.loop_id == track_object_counter %}
<div id = "emg-plot" style="height:15rem; padding-top: 1rem;">
<canvas id="myChart-{{forloop.counter}}">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var y_data = {{item.data|strip_apostrophe}}
var x_data = '{{item.name}}'
var func_name = "container-{{forloop.counter}}";
func_name = new Chart(
document.getElementById('myChart-{{forloop.counter}}'),{
type: 'scatter',
data: {
datasets: [{
showLine: true,
label: x_data,
data: y_data,
backgroundColor: 'rgba(141,49,123,0.7)',
borderWidth: 2,
borderColor: 'rgba(141,49,123,0.7)'
}],
},
});
</script>
</canvas>
</div>
{% endif %}
{% endfor %}
</div>
{% endwith %}
{% endfor %}
I am performing a loop on angular2 with dynamic emoji values encoded with org.apache.commons:commons-lang3 library in android like \uD83D\uDEB5\uD83D\uDEB5\uD83D\uDEB5. I need to decode them in angular2 frontend. In itemsArr[index]['Posted Content'] = item[0]['document']['post_content']; I get the encoded content from backend. And
this is how I am trying to show them
Code as follows
ngOnInit() {
this.loading = true;
var itemsArr = [];
var reporterPromiseArr = [];
var postPromiseArr = [];
var posts = this._cb.getRelationalDataFromTable('reportPost').then(res => {
res.forEach(item => {
itemsArr.push({ 'Reported On': item.document.createdAt, 'Posted By': '', 'Posted On': '', 'postId': item.document.post_id });
postPromiseArr.push(this._cb.getRelationalDataFromTableNew('userActivityStream', [{ key: '_id', value: item.document.post_id }], ['product_id', 'user_id']));
reporterPromiseArr.push(this._cb.getRelationalDataFromTable('registration', [{ key: '_id', value: item.document.user_id }]));
});
return Promise.all(postPromiseArr);
}).then(res => {
res.forEach((item, index) => {
itemsArr[index]['Posted By'] = (item[0]['document']['user_id'] !== null) ? item[0]['document']['user_id']['document']['user_name'] : '';
itemsArr[index]['Posted On'] = item[0]['document']['createdAt'];
itemsArr[index]['Posted Type'] = item[0]['document']['type'];
itemsArr[index]['Total Likes'] = item[0]['document']['total_like'];
itemsArr[index]['Posted Content'] = item[0]['document']['post_content'];
itemsArr[index]['Image'] = decodeURI(item[0]['document']['image']);
});
//console.log(reporterPromiseArr);
return Promise.all(reporterPromiseArr);
}).then(res => {
res.forEach((item, index) => {
itemsArr[index]['Reported By'] = item[0]['document']['user_name'];
});
this.listingArr = itemsArr;
this.loading = false;
}).catch(err => {
this.loading = false;
console.log(err)
});
console.log(itemsArr);
//return Promise.all(reporterPromiseArr);
}
html as follows where i tried to show them in innerHTML but not working
<form class="tr" #requestData="ngForm" (ngSubmit)="onSubmit(requestData)" *ngFor="let item of listingArr; let i = index">
<div class="td">{{i + 1}}</div>
<div class="td">{{ item['Reported By'] }}</div>
<div class="td">{{item['Reported On'] | date: yMMMdjms }}</div>
<div class="td">{{ item['Posted By'] }}</div>
<div class="td">{{ item['Posted On'] | date: yMMMdjms }}</div>
<div class="td">{{ (item['Total Likes'] == null)?0:item['Total Likes'] }}</div>
<div class="td">{{ item['Posted Type'] }}</div>
<div [innerHTML]="item['Posted Content']"></div>
</div>
</form>
If you need them to be displayed properly on the page, you need to bind them with innerHTML in Angular2
<div [innerHTML]="the.path.to.property"></div>
Say I have a controller like this:
public function showAction(Request $request, $id, $context = array())
{
$url = $this->getPlayUrl($id, $context);
//some code here}
I want to use the variable $url in a frontend js file like this :
var html = '<iframe src=\''+ url +'\' name=\'viewerIframe\' id=\'viewerIframe\' width=\'100%\'allowfullscreen webkitallowfullscreen height=\'100%\' style=\'border:0px\'></iframe>';
How am I supposed to make this happen? For the code above ,it gave me 'undefined url' error.
Thank you.
Another option would be to have your template call a controller action (so you can pass parameters as required), for example, like this example for using highchart.js:
{% block javascripts %}
{{ parent() }}
<script src="/js/highcharts.js"></script>
<script src="/js/exporting.js"></script>
<script type="text/javascript">
{{ render(controller('AppBundle:Default:script')) }}
</script>
{% endblock %}
In the controller it looks like this:
public function scriptAction() {
$reports = $this->get('app.reports');
$chart = $reports->getDistsFYToDate();
return $this->render('Default/script.js.twig', array(
'chart' => $chart,
));
}
and script.js.twig looks like this:
$(document).ready(function () {
var options = {
chart: {
type: 'line'
},
title: {
text: 'Distributions, FY{{ chart.fy }} to date'
},
xAxis: {
categories: [{% for category in chart.categories %}{{category|raw}}{%endfor%}]
},
yAxis: {
title: {
text: 'Distributions'
}
},
series: {{ chart.series|raw }}
};
$('#linechart').highcharts(options);
})
Maybe it's better if you use HTML5 data attributes: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes
In you view add some script tags and assign a javascript vraible to the twig variable. Don't forget to quote the twig (if required).
Firstly, I would ensure that the $url always had a value;
return $this->render('MyWebBundle:Player:show.html.twig', array(
'file' => $file,
'url' => (empty($url)) ? '' : $url,
'context' => $context,
'player' => $player,
'agentInWhiteList' => $agentInWhiteList
));
Something like;
<script>
var jsVar = '{{ url }}';
var html = false;
if (jsVar.length > 0) {
html = '<iframe src=\''+ jsVar +'\' name=\'viewerIframe\' id=\'viewerIframe\' width=\'100%\'allowfullscreen webkitallowfullscreen height=\'100%\' style=\'border:0px\'></iframe>';
}
// test html !== false before using it
</script>
Twig malforms my array keys and adds " " brackets to my array keys passed from a symfony2 controller.
The array is passed to a Javascript graphics library which requires:
[{x:"disabled test object",y:"17",label:"disabled test object"}]
Instead {{ array|json_encode|raw }} as suggested by the Twig docs and other SO questions I've read through returns the unreadable:
[{"x":"disabled test object","y":"17","label":"disabled test object"}]
I figure this should not be complicated to achieve but going through the json_encode options so far has not resulted in a clear answer. I am unsure whether there is something I can do from PHP so I've added the tag for now.
EDIT:
I am now attempting to step through the array manually using Twig. {{dump(points)}} confirms it is filled properly
{% set points = chart.dataPoints|json_encode|raw %} <=== this was the problem
dataPoints:
{% for point in points %}
{{ dump(point) }}
{ x: {{ point.x }}, y: {{ point.y }}, label: {{ point.label }} }
{% if loop.lastIndex != true %}
,
{% endif %}
{% endfor %}
But this does not work either as the dump is never reached. Is this the correct way of trying to access objects in a foreach via Twig though? This code is an amalgamation of several Twig docs tutorials.
EDIT 2, the solution:
The line {% set points = chart.dataPoints|json_encode|raw %} turned the whole array into a single string, making it impossible for javascript to interpret as an array. After removing this, all that was left was to make sure the query result had the proper array keys and to transform the X-axis data before passing it to Twig.
$i = 0;
$points = array();
/** #var array $query_result*/
foreach($query_result as &$row) {
foreach($row as $value) {
$point[] = [
'x' => ($i)*10,
'y' => $value['y'],
'label' => $value['label']
];
$points[$i] = $point;
$i++;
}
}
Quotes are not a problem for CanvasJS. As you can see in the example below, "x": works (I took this example):
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: [
{ x: 1, y: 297571, label: "Venezuela"},
{ "x": 2, "y": 267017, label: "Saudi" }
]
}]
});
chart.render();
}
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
</head>
<body>
<div id="chartContainer" style="height: 200px; width: 100%;"></div>
</body>
</html>
So we need to provide a JSON array for dataPoints:
Define the PHP array in your Controller's function and pass it to the template:
public function myAction()
{
// …
$json = array(
array(
'x' => 1,
'y' => 297571,
'label' => 'Venezuela',
),
array(
'x' => 2,
'y' => 267017,
'label' => 'Saudi',
),
);
return array(
// …
'json' => $json,
);
}
Display the array in the template and pass it to dataPoints :
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.min.js"></script>
<script>
var json = {{ json|json_encode|raw }};
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Top Oil Reserves"
},
data: [{
dataPoints: json
}]
});
chart.render();
}
</script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
The rendered output will be:
// …
<script>
var json = [{"x":1,"y":297571,"label":"Venezuela"},{"x":2,"y":267017,"label":"Saudi"}];
// …
CanvasJS will be able to read this JavaScript array and display the chart.
It looks like the first one is a JavaScript object, and the second one is JSON, try running JSON.parse on the string like this to convert it back in to an object:
var fixed = JSON.parse('[{"x":"disabled test object","y":"17","label":"disabled test object"}]');
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse