GoJS Diagram not loading at initial call - javascript

I have been trying to load a graph from a default selected value in a drop down list. This is the html for the select element.
<select class="custom-select" id="activity">
<option disabled value="">Select an activity</option>
<option selected value="Running">Running</option>
</select>
And then the javascript goes like this.
function loop() {
setTimeout(function () {
load(activities.value);
loop();
}, 5000);
}
var activities = document.getElementById("activity");
load(activities.value);
I tried this and the graph just dows not not load up. But then i tried this
function loop() {
setTimeout(function () {
load(activities.value);
loop();
}, 5000);
}
var activities = document.getElementById("activity");
load(activities.value);
loop();
function load(activity){
graph= {"class": "go.GraphLinksModel",
"nodeDataArray": [{}],
"linkDataArray": [{}]
}
myDiagram.model = go.Model.fromJson(graph);
}
And the initial function call just before loop() does not load the graph. But after 5 seconds once the loop kicks in the graph loads up. And every 5 seconds it keeps loading up just like it should. I also tried adding a onchange event listener to the drop down with 2 more options and added.
activities.addEventListener("change", () => {
load(activities.value);});
and once i changed back and forth the graphs load up.
I also tried the myDiagram.requestUpdate(); right after the load(activities.value);.
Where an going wrong? What am i doing wrong?. Appreciate all advices and questions for any more clarification and ofcourse some answers if anyone can help.

I found the solution to be the problem to be the load function initially being called before the div element is available. After adding a window.addEventListener('DOMContentLoaded', init); it is working fine now.

Related

Multiple text-areas being added after an update event

I am making a simple code editor using codemirror. I am at the last stage but a bug has occurred which I am not able to get my heads around.
I have a select html tag with three options:
<select class="language" id="lang" onclick="update()">
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="python">C</option>
</select>
And my update() function is:
function update() {
var select = $('#lang').val();
var editor = CodeMirror.fromTextArea($('#text'), {
mode: select,
theme: "blackboard",
});
}
update();
Now what happens is that everytime I select a different option from the dropdown, a new Code editor is added below the previous one.
Do you have some idea where I am going wrong in the code and how I can workaround?
Change your JS to this:
let editor;
function update() {
var select = $('#lang').val();
editor = editor ?? CodeMirror.fromTextArea($('#text'), {
theme: "blackboard",
});
editor.setOption('mode', select);
}
update();
This makes it so if editor already exists, it is not created again, but instead re-used.

hiding overlay after some time just like standard video player

i am making a custom video player in which there is an overlay containing the controls of the video player
my player starts to work in full length and height.
now i want to hide the overlay after 5 seconds i stop the mouse over.
now the problem is that when the below function mouse over in .ts file is called the synchronization of the timer is harmed.
so if i move my mouse continuously the overlay starts to flicker.
please provide me the solution to the problem.
following is my html code
<div class="video-container" #videoFullscreen>
<div class="video-wrapper" mouse-move>
<video class="video video-js" data-dashjs-player id="myVideo" autoplay #videoPlayer>
<source src="{{ videoSource }}" type="video/mp4" />
</video>
<!-- overlay -->
<div class="overlay" [class.hideOverlay]="hideTop">
<!-- top controls -->
.
.
<!-- lower controls -->
</div>
</div>
this is my type script code
#HostListener('document:mousemove', [ '$event' ]) //fuction to display and hide element sue to mouseover
onMouseMove($event) {
this.hideTop = false;
setTimeout(() => {
this.hideTop = true;
}, 5000);
}
this is my css code :
.overlay {
display: flex;
}
.hideOverlay {
display:none;
}
please help me to solve this problem.
Store the lastHover time and compare against it.
private lastHover = 0;
#HostListener(...)
onMouseMove($event) {
this.lastHover = new Date().getTime()
This.hideTop = true;
setTimeout( () => {
...
if(lastHover + 5000 < new Date().getTime()) {
This.hideTop = true;
}
}, 5000)
}
A neat solution would be to use rxjs to solve this like shown below:
ngOnInit(): void {
fromEvent<MouseEvent>(document, 'mousemove').pipe(tap(() => {
console.log("show it!");
this.hideTop = false
}), switchMap((event) =>
timer(5000).pipe(tap(() => {
console.log("hideit");
this.hideTop = true;
}))
)).subscribe();
}
Don't forget to unsubscribe if your component gets destroyed to prevent memory leaks!
First we make an Observable from the documents mousemove event.
Now if the event triggers we set hideTop to true.
And here comes the interesting part: we use switchMap with a timer Observable. switchMap automatically unsubscribes from the inner Observable if the outer one emits a new value. Therefore the inner Observable only emits after the user actually stopped moving the mouse for 5 seconds.
Apologies that my answer is in jQuery, but the concept is fairly basic
What we need to do is check if the timeout event has already been fired, and reset it on a mousemove event during that time. This is done by checking if the class for hiding the element is applied or not
//Timer variable
var timer;
//Detect mousemove event on parent element
$("html").on("mousemove", "#outer", function() {
//Is the element already hidden?
if ($("#inner").hasClass("hide")) {
//Show the element
$("#inner").removeClass("hide")
} else {
//Reset the timer to 5 seconds
clearTimeout(timer);
timer = setTimeout(hideBox, 5000);
}
})
function hideBox() {
$("#inner").addClass("hide")
}
https://jsfiddle.net/xcL52zf3/1/
You'll need to swap out the jQuery event handlers and element targetting with the equivalent for you TypeScript library

Check if chart is rendered in apexcharts

I am destroying the chart but when it's not rendered I get error.
Is there a way to check if chart is rendered, then destroy it?
if(chart)
chart.destroy()
Each time i destroy an object that does not exist i get TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.
Also i need to render it again if it's not rendered, i won't render it again and again. I need that check
The linked documentation states that render() returns a promise once the chart is drawn to the page.
The code however seems to return that promise immediately (which makes sense) and resolves that promise, when the chart was drawn.
As far as I can see, it should be sufficient to set and keep a state-flag after the promise is resolved like so:
let chart = new ApexCharts(el, options);
chart.render().then(() => chart.ohYeahThisChartHasBeenRendered = true);
/* ... */
if (chart.ohYeahThisChartHasBeenRendered) {
chart.destroy();
}
Update after comment
Yes this works! I made this runnable example for you (typically this is the duty of the person asking the question ;) ) Press the button and inspect the log):
<html>
<head>
<title>chart test</title>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<head>
<body>
<div id="chart"></div>
<script>
let options = {
chart: { type: 'line' },
series: [{ name: 'sales', data: [30,40,35,50,49,60,70,91,125] }],
xaxis: { categories: [1991,1992,1993,1994,1995,1996,1997, 1998,1999]}
},
chart = new ApexCharts(document.querySelector("#chart"), options),
logChart = () => console.log(chart),
destroyChart = () => {
if (chart.ohYeahThisChartHasBeenRendered) {
chart.destroy();
chart.ohYeahThisChartHasBeenRendered = false;
}
};
chart.render().then(() => chart.ohYeahThisChartHasBeenRendered = true);
</script>
<button onclick="logChart()">Log chart</button>
<button onclick="destroyChart()">Destroy chart</button>
</body>
</html>
I suspect that you tried something like this to check for the flag:
chart.render().then(() => chart.ohYeahThisChartHasBeenRendered = true);
console.log(chart.ohYeahThisChartHasBeenRendered);
It will not do what you expect because the promise is not resolved yet.
Update after another comment
As pointed out by a comment there is a related known issue with apexcharts:
https://github.com/apexcharts/apexcharts.js/pull/415
Even though this question asks to "check if the chart is rendered", the code suggests that they actually want to "check if the chart exists". I would also like to check if a chart exists before rendering it, and I suspect this is the more common issue.
I'm not sure about the accepted answer here. It seems that this answer always creates a new chart, hence there is no need to check if the chart exists.
I worked on this for some time - got no help from documentations- and finally discovered the Apex object. Check out Apex._chartInstances: this field is undefined before any charts render, and as they render, they store references here. After at least one rendering, the length of this field is equal to the number of existing charts.
Check if any charts have ever existed: (Apex._chartInstances === undefined)
Check if any charts currently exist: (Apex._chartInstances.length > 0)
Access the id's of existing charts: (Apex._chartInstances[0].id)
These bits were enough to make it work for my case. Hope this helps somebody else.
I was able to use the beforeMount and mounted events to check if the chart was rendered or not. For some reason, I am not able to catch the error that ApexChart throws.
My logic:
in beforeMount, make a delayed call to error handler and set error flag to true.
in mounted, set error flag to false. When the error handler runs, if the error flag is false, you skip othwe
{
...
chart: {
type: "scatter",
height: height,
events: {
beforeMount: function (chartContext, config) {
setTimeout(() => {
PAGE_DATA.ShowChartError = true;
showChartErrorMessage($(chartContext.el));
}, 1000);
},
mounted: function (chartContext, config) {
PAGE_DATA.ShowChartError = false;
},
},
},
...
}
Error handler,
function showChartErrorMessage($el) {
if (PAGE_DATA.ShowChartError) {
// show error msg
$el.siblings(".error-help-container").removeClass("hidden");
// hide chart div
$el.hide();
}
PAGE_DATA.ShowChartError = false;
}
I tried all sorts of suggestions on destroying a rendered chart and nothing seemed to work. Finally I tried this and it worked.
Before you render it, put a destroy in a try catch with no error, basically an on error resume next and then render it.
var chart22 = new ApexCharts(document.querySelector("#row2-2"), options1);
try{
chart22.destroy();
}
catch{
}
chart22 = new ApexCharts(document.querySelector("#row2-2"), options1);
chart22.render();

cannot get dynamic added custom select to work in jQuery Mobile

I have looked through all of the similar questions available at the time of this question, and none of the solutions presented worked in the below code. Google was also not helpful except that I did find a few issues with dynamic code where the entire menu was not wrapped, but those issues should be fixed with either the trigger or enhanceWithin methods - which have been tried here.
I am a fairly new with javascript and the jquery library and this is my first app with jquery mobile.
The raw html as generated from the php file:
<div class="cell_container force_org_select">
<label for"force_org[new_555]" class="ui-hidden-accessible">Troop Type</label>
<select name="force_org[new_555]" id="force_org[new_555]" class="roster_cell" data-mini="true">
<option value="hq">HQ</option>
<option value="elite">Elite</option>
<option value="solo">Solitaire</option>
<option value="formation">Std Formation</option>
</select>
The Javascript function that handles the dynamic injection:
$(document).on('click','.add_item', function(event) {
event.preventDefault();
var the_link = $(this).attr('href')
var area = getParameterByName(the_link, 'area');
var type = getParameterByName(the_link, 'type');
var squad_id = getParameterByName(the_link, 'squad_id');
var vehicle = getParameterByName(the_link, 'vehicle');
var divider = getParameterByName(the_link, 'divider');
var preset = $('#preset').val();
$.post(cmd_ajax.ajaxurl,{action: 'cmd_add_item_mobile', type: type, preset: preset, squad_id: squad_id, vehicle: vehicle, divider: divider}, function(data) {
if(type == 'squad' || type == 'divider') {
$('#list').append(data).enhanceWithin();
//$('#list').append(data).trigger("refresh");
//$('#list').append(data).trigger("create");
$('.squad_help_button').tooltipster({
contentCloning: true,
trigger: 'custom',
triggerOpen: {
click: true,
tap: true
},
triggerClose: {
click: true,
tap: true
}
});
}
else {
$('#' + area).append(data).enhanceWithin();
}
//console.log("squad_id:"+this_id);
set_unit_sortable();
});
return false;
});
I also tried adding the .selectmenu("refresh",true) within the function and that seems to do nothing. The custom selects that are not dynamically generated work fine.
If I use the data-native-menu="false" attribute on the generated select menus, the popup does not function and you cannot select anything, if I remove the attribute, the native select works as it should.
I thought about using a selectmenu() refresh at the very end of the function, but I can't seem to catch the element id of the select menu either. My only guess is that it isn't created yet in the DOM when I try to retrieve it.

Highcharts - get visible series name dynamically

I want to make a function which send dynamically all visible series name in a Highchart instance to a PHP function.
For example, in this chart, I want to get this array : [Salle, PR].
If I click on Internet, the serie become visible and I want to get [Salle, Internet, PR].
To do this, I tried to use legendItemClick event and make a function that check if each serie is visible to add it to an array but I can't figure out how to use the visible option to do this.
Do you have an idea ?
As of now, I don't have much code to share :
plotOptions: {
series: {
events: {
legendItemClick: function(){
}
}
}
}
If you retain the pointer to your chart like this:
var ch = Highcharts.chart(_chart_data_);
Then later you can access the whole chart structure. What you will be interested in is the series array.
ch.series[]
It contains array of all your series. Series with visible attribute set to true are the ones that currently displayed. So,it might be something like this:
var ch = Highcharts.chart(...
plotOptions: {
series: {
events: {
legendItemClick: function(){
ch.series.forEach(function(sr){
if(sr.visible){
console.log(sr.name, "visible!");
}
});
}
}
}
}
...);
However, there is a catch with your approach, that on actual legend click your current action for the legend is not yet processed.so the output you will see is the output for the previous state, before current click.
So for that reason you may try to use setTimeout to get your listing after the event is applied:
events: {
legendItemClick: function(){
setTimeout(
function(){
ch.series.forEach(
function(sr){
if(sr.visible){
console.log(sr.name, "visible!");
}
}
)
},20);
}
}
Try this and check the console log: http://jsfiddle.net/op8142z0/

Categories