How do I implement zingchart into angular2 - javascript

I have an existing project that I want to implement zingcharts on.
I have tried 3 different tutorials mainly from " https://blog.zingchart.com/2016/07/19/zingchart-and-angular-2-charts-back-at-it-again/ "blog.
However I can not get that working in my project.
So I decided I will try and implement it the most basic way first and later on try better ways. This is what I did but it does not shop up yet.
Being new to angular2 I am not quite sure if this will work.
I went to the ZingChart website and tried to implement this basic example ->https://www.zingchart.com/docs/getting-started/your-first-chart/
So I built 2 files chart.ts and chart.component.html and implemented the
"<script src="https://cdn.zingchart.com/zingchart.min.js"></script>"
in the index.html
//chart.ts
import { Component } from '#angular/core';
#Component({
selector: 'rt-chart',
templateUrl: './chart.component.html'
})
export class Chart
{
}
//chart.component.html
<!--Chart Placement[2]-->
<div id="chartDiv"></div>
<script>
var chartData = {
type: "bar", // Specify your chart type here.
title: {
text: "My First Chart" // Adds a title to your chart
},
legend: {}, // Creates an interactive legend
series: [ // Insert your series data here.
{ values: [35, 42, 67, 89]},
{ values: [28, 40, 39, 36]}
]
};
zingchart.render({ // Render Method[3]
id: "chartDiv",
data: chartData,
height: 400,
width: 600
});
</script>
I called it in my already working website as
It does not show up. What am I doing wrong? Is there something I am missing.
Angular2 is quite new to me.
Thanks

With latest angular2 version (#2.2.3) you can leverage special ZingChart directive like this:
zing-chart.directive.ts
declare var zingchart: any;
#Directive({
selector : 'zing-chart'
})
export class ZingChartDirective implements AfterViewInit, OnDestroy {
#Input()
chart : ZingChartModel;
#HostBinding('id')
get something() {
return this.chart.id;
}
constructor(private zone: NgZone) {}
ngAfterViewInit() {
this.zone.runOutsideAngular(() => {
zingchart.render({
id : this.chart.id,
data : this.chart.data,
width : this.chart.width,
height: this.chart.height
});
});
}
ngOnDestroy() {
zingchart.exec(this.chart.id, 'destroy');
}
}
where ZingChartModel is just a model of your chart:
zing-chart.model.ts
export class ZingChartModel {
id: String;
data: Object;
height: any;
width: any;
constructor(config: Object) {
this.id = config['id'];
this.data = config['data'];
this.height = config['height'] || 300;
this.width = config['width'] || 600;
}
}
Here's completed Plunker Example

Related

Error with Angular ag-grid: "Could not find component class XXX, did you forget to configure this component"

I am getting this error from Chrome when running an ag-grid application. Basically, I have following
export class CustomHeader implements IHeaderAngularComp {
private params: any;
#ViewChild('menuButton', { read: ElementRef }) public menuButton;
agInit(params): void {
this.params = params;
}
onMenuClicked() {
this.params.showColumnMenu(this.menuButton.nativeElement);
}
refresh(params: IHeaderParams): boolean {
return true;
}
}
...
ColumnDefs = {
...
{
field: "A column", editable: false, sortable: false, width: 90,
type: 'stringColumn', centered: true, pinned: 'left',
headerComponent: CustomHeader,
headerComponentParams: {
template: `
<div ref="eLabel" class="lmn-form-group">
<input ref="eCheck" type="checkbox">
<span>Use This</span>
</div>
`
}
},
...
}
The Chrome says it does not recognize this CustomHeader:
Could not find component class CustomHeader {
agInit(params) {
this.params = params;
}
onMenuClicked() {
this.params.showColumnMenu(this.menuButton.nativeElement);
}
refresh(params) {
return true;
}
}, did you forget to configure this component?"
Is there anything I am missing?
Have you seen the example in the documentation?
Note that you also need to provide the [frameworkComponents] grid property so that the grid knows to use the header component:
this.frameworkComponents = { agColumnHeader: CustomHeader };
If you come here in 2022 and you are not using the latest version ag-grid, do check the correct version documents here:
https://www.ag-grid.com/archive/
For me at version 25.3, still need to use frameworkComponents as #Shuheb said, but the latest version 27.3 docs do not have this property any more.
As the 25.3 doc says, There are two ways to register custom components:
By name.
Direct reference.
By Name
export class AppComponent {
frameworkComponents: [
'cubeComponent': CubeComponent
];
columnDefs: [
{
headerName: "Cube",
field: "value",
cellRenderer: 'cubeComponent',
}
]
//...other properties & methods
}
By Direct Reference
export class AppComponent {
columnDefs: [
{
headerName: "Cube",
field: "value",
cellRendererFramework: CubeComponent,
}
]
//...other properties & methods
}

How to assign HTML element ID, through an Angular component, to a JavaScript function?

I found a JavaScript library and a function, I struggled, but did include this JavaScript function in my Angular application.
The JavaScript code prints takes in an object (myData: two arrays of numbers), and passes this data to a function, which draws two charts, each chart corresponds to one of the two arrays of numbers.
First thing I had to do is figure out how to include this JavaScript in my Angular project, which wasn't easy. And I'm even still not sure if it worked or not, because I haven't finished connecting everything together.
So here's the JavaScript code:
<!DOCTYPE html>
<html>
<head>
<!-- This page was last built at 2020-10-16 19:40 -->
<meta charset="utf-8"/>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div class="myDiv" id="myDiv">
</div>
<div class="myDiv" id="myDiv2">
</div>
<script>
myData = {
"loss": [
0.6437230565968681,
.
.
.
0.015016184188425541
],
"accuracy": [
0.6740923523902893,
.
.
.
0.9943200945854187
]
}
dataPlot(myData)
function dataPlot(myData){
function range(start, end) {
var ans = [];
for (let i = start; i <= end; i++) {
ans.push(i);
}
return ans;
}
yLoss = myData['loss'];
yAccuracy = myData['accuracy'];
traceLoss = {
type: 'scatter',
x: range(yLoss.length>0?1:0,yLoss.length),
y: yLoss,
mode: 'lines',
name: 'Red',
line: {
color: 'rgb(219, 64, 82)',
width: 1
}
};
traceAccuracy = {
type: 'scatter',
x: range(yAccuracy.length>0?1:0,yAccuracy.length),
y: yAccuracy,
mode: 'lines',
name: 'Blue',
line: {
color: 'rgb(55, 128, 191)',
width: 1
}
};
var layoutLoss = {
title: 'Model Binary Crossentropy Loss',
xaxis: {
title: 'Epochs',
showgrid: false,
zeroline: false
},
yaxis: {
title: 'Loss',
showline: false
}
// for static size
,width: 900,
height: 500
};
var layoutAccuracy = {
title: 'Model Training Data Accuracy',
xaxis: {
title: 'Epochs',
showgrid: false,
zeroline: false
},
yaxis: {
title: 'Accuracy',
showline: false
}
// for static size
,width: 900,
height: 500
};
var dataLoss = [traceLoss];
var dataAccuracy = [traceAccuracy];
divs('myDiv','myDiv2')
function divs(div1, div2) {
Plotly.newPlot(div1, dataLoss, layoutLoss);
Plotly.newPlot(div2, dataAccuracy, layoutAccuracy);
}
}
</script>
</body>
</html>
If you copy that code to a .html file it'll work.
See at the bottom of the code this function:
function divs(div1, div2) {
Plotly.newPlot(div1, dataLoss, layoutLoss);
Plotly.newPlot(div2, dataAccuracy, layoutAccuracy);
}
This function takes div1 and div2 values, those are the IDs for the 2 divs containing the charts.
What I need is, first, to pass myData to the function, which is easy and doable, not a problem.
The problem is, how am I supposed to give the IDs of the divs to the JavaScript function, and have it know where those divs are to begin with?
I've been trying with #ViewChildren and ElementRef. But no success.
Here's my HTML template so far:
<div #divs *ngFor="let item of [1,2]" [id]="'myDiv-' + item">Div {{item}}+{{whatevs}}</div>
<button (click)="getDivs()">Get divs</button>
and the component:
import { Component, ElementRef, OnInit, QueryList, ViewChildren } from '#angular/core';
declare var dataPlot: any;
#Component({
selector: 'jhi-ml-model-data',
templateUrl: './ml-model-data.component.html',
})
export class MlModelDataComponent implements OnInit {
#ViewChildren("divs", { read: ElementRef }) divs!: QueryList<ElementRef>;
myData: any;
constructor() { }
getDivs(): void {
// this.divs.forEach((div: ElementRef) => div.nativeElement);
this.divs.forEach((div: ElementRef) => console.log(div.nativeElement.getAttribute('id')));
// this^ gets the IDs for both divs, not each one individually.
}
ngOnInit(): void {
this.myData = {
"loss": [0.6437230565968681,
.
.
.
0.39040950554258685],
"accuracy": [0.6740923523902893,
.
.
.
0.9943200945854187]
}
dataPlot(this.myData);
dataPlot.divs();
}
}
I feel like I've put myself in an untangleable mess, and feel like there must be

Use external JavaScript library in Angular 8 application

I am new to Angular and i want to develop a funnel-graph. i like the funnel-graph-js library. i tried a lot but haven't succeed.
here is my funnel-graph-directive.ts
import { Directive, ElementRef } from '#angular/core';
// import * as graph from '../../../assets/js/funnel-graph.js';
import * as graph from 'funnel-graph-js/dist/js/funnel-graph.js';
var graph = new FunnelGraph({
container: '.funnel',
gradientDirection: 'horizontal',
data: {
labels: ['Impressions', 'Add To Cart', 'Buy'],
subLabels: ['Direct', 'Social Media', 'Ads'],
colors: [
['#FFB178', '#FF78B1', '#FF3C8E'],
['#A0BBFF', '#EC77FF'],
['#A0F9FF', '#7795FF']
],
values: [
[3500, 2500, 6500],
[3300, 1400, 1000],
[600, 200, 130]
]
},
displayPercent: true,
direction: 'horizontal'
});
graph.draw();
#Directive({
selector: '[appFunnelGraph]'
})
export class FunnelGraphDirective {
style: any;
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
I have added these lines in my angular.json
"styles": [
"src/styles.scss",
"./node_modules/funnel-graph-js/dist/css/main.css",
"./node_modules/funnel-graph-js/dist/css/theme.css"
],
"scripts": [
"./node_modules/funnel-graph-js/dist/js/funnel-graph.js"
]
Here is the error i am getting
As long as you linked the javascript file in the html, it will work fine.
EDIT:
A better way to include an addition javascript file is to put it into the "scripts" section in the angular.json file. You can also add
declare const FunnelGraph: any
in order to compile without errors. This has been taken from an answer to a stackoverflow question and this guide. Remember to include the css files in that json too!
EDIT END
You get that error because the code tries to look for an HTML element with a class named "funnel", but cannot find it. Since this is a directive, it would be better if it was a little more generalized.
First of all, you should move your graph-generating code inside the constructor, since that's were the directive logic resides. To better generalize this directive, it would be best if you gave a unique id to that element and change the code accordingly. This is how I would do it:
HTML:
<div id="funnel-graph-1" appFunnelGraph></div>
JS:
import { Directive, ElementRef } from '#angular/core';
// It should be fine to just import this in the html with a script tag
// import * as graph from 'funnel-graph-js/dist/js/funnel-graph.js';
#Directive({
selector: '[appFunnelGraph]'
})
export class FunnelGraphDirective {
style: any;
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
var graph = new FunnelGraph({
// Generalize the container selector with the element id
container: '#' + el.nativeElement.id,
gradientDirection: 'horizontal',
data: {
labels: ['Impressions', 'Add To Cart', 'Buy'],
subLabels: ['Direct', 'Social Media', 'Ads'],
colors: [
['#FFB178', '#FF78B1', '#FF3C8E'],
['#A0BBFF', '#EC77FF'],
['#A0F9FF', '#7795FF']
],
values: [
[3500, 2500, 6500],
[3300, 1400, 1000],
[600, 200, 130]
]
},
displayPercent: true,
direction: 'horizontal'
});
graph.draw();
}
}
I ended up by creating service instead of using directive approach.
First i generated a service called dynamic-script-loader-service in
my dashboard module.
dynamic-service-loader.service.service.ts
import { Injectable } from '#angular/core';
interface Scripts {
name: string;
src: string;
}
export const ScriptStore: Scripts[] = [
{ name: 'chartjs', src: 'https://unpkg.com/funnel-graph-js#1.3.9/dist/js/funnel-graph.min.js' },
];
declare var document: any;
#Injectable()
export class DynamicScriptLoaderServiceService {
private scripts: any = {};
constructor() {
ScriptStore.forEach((script: any) => {
this.scripts[script.name] = {
loaded: false,
src: script.src
};
});
}
load(...scripts: string[]) {
const promises: any[] = [];
scripts.forEach((script) => promises.push(this.loadScript(script)));
return Promise.all(promises);
}
loadScript(name: string) {
return new Promise((resolve, reject) => {
if (!this.scripts[name].loaded) {
//load script
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = this.scripts[name].src;
if (script.readyState) { //IE
script.onreadystatechange = () => {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
this.scripts[name].loaded = true;
resolve({ script: name, loaded: true, status: 'Loaded' });
}
};
} else { //Others
script.onload = () => {
this.scripts[name].loaded = true;
resolve({ script: name, loaded: true, status: 'Loaded' });
};
}
script.onerror = (error: any) => resolve({ script: name, loaded: false, status: 'Loaded' });
document.getElementsByTagName('head')[0].appendChild(script);
} else {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
}
});
}
}
dashboard.component.ts
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { DynamicScriptLoaderServiceService } from '../dynamic-script-loader-service.service';
import * as FunnelGraph from 'funnel-graph-js';
function dashboardFunnel() {
const graph = new FunnelGraph({
container: '.funnel',
// gradientDirection: 'horizontal',
data: {
labels: ['Label 7', 'Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5', 'Label 6'],
colors: ['#00A8FF', '#00A8FF', '#00A8FF', '#00A8FF', '#00A8FF', '#00A8FF', '#00A8FF'],
// color: '#00A8FF',
values: [12000, 11000, 10000, 9000, 8000, 7000, 6000]
},
displayPercent: true,
direction: 'horizontal',
});
graph.draw();
}
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class DashboardComponent implements OnInit {
constructor(
private dynamicScriptLoader: DynamicScriptLoaderServiceService
) {}
ngOnInit() {
this.loadScripts();
dashboardFunnel();
}
private loadScripts() {
// You can load multiple scripts by just providing the key as argument into load method of the service
this.dynamicScriptLoader.load('chartjs', 'random-num').then(data => {
// Script Loaded Successfully
}).catch(error => console.log(error));
}
}
added providers in my dashboard.module.ts
providers: [DynamicScriptLoaderServiceService],
added css in my angular.json
"styles": [
"src/styles.scss",
"./node_modules/funnel-graph-js/dist/css/main.css",
"./node_modules/funnel-graph-js/dist/css/theme.css"
],
added div with class funnel in dashboard.component.html
<div class="funnel"></div>

Vis.js node tooltip doesn't show up on hover using ReactJS

Greetings,
In my project I am displaying a vis.js graph, using ReactJS and I would like to have the nodes of the displayed network react to the mouse-hover event by displaying a popup/tooltip. Every node has a title attribute, and thats what the tooltip should display.
This is how they do it in their example:
<!doctype html>
<html>
<head>
<title>Network | Interaction events</title>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis-network.min.css" rel="stylesheet" type="text/css" />
<style type="text/css">
#mynetwork {
width: 600px;
height: 400px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{ id: 1, label: 'Node 1', title: 'I have a popup!' },
{ id: 2, label: 'Node 2', title: 'I have a popup!' },
{ id: 3, label: 'Node 3', title: 'I have a popup!' },
{ id: 4, label: 'Node 4', title: 'I have a popup!' },
{ id: 5, label: 'Node 5', title: 'I have a popup!' }
]);
// create an array with edges
var edges = new vis.DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 }
]);
// create a network
var container = document.getElementById('mynetwork');
var data = {
nodes: nodes,
edges: edges
};
var options = {
interaction: { hover: true },
manipulation: {
enabled: true
}
};
var network = new vis.Network(container, data, options);
</script>
</body>
</html>
Here the popups work.
I am loading the node related data from a database, and the nodes are drawn according to that data just fine. Everything works properly, except the popup doesn't show when triggered by the hover event.
This is the relevant part of the component.tsx file:
import * as React from 'react';
import NodeGraph from 'react-graph-vis';
import { IEdge, ISkill } from '../../../models';
import Options from './skillTree.options';
import Props from './skillTree.props';
import State from './skillTree.state';
export default class extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
graph: { nodes: [], edges: [] },
options: Options,
selectedNode: undefined,
}
}
//...
private _skillTreeRequestCallback(graph: {
nodes: ISkill[],
edges: IEdge[]
}) {
graph.nodes = graph.nodes.map(skill => ({
...skill,
hidden: skill.Hidden,
image: skill.Image,
label: skill.Label,
id: skill.Id,
title: "Hardcoded popup message"
}));
graph.edges = graph.edges.map(edge => ({
...edge,
from: edge.From,
to: edge.To
}));
this.setState({ graph, isLoading: false });
}
//...
public render() {
return (this.state.graph.nodes.length > 0 ? <main style={this.props.style} >
<NodeGraph graph={this.state.graph} options={this.state.options}/>
</main> : <LoadingView style={this.props.style} />);
}
And the data arrives just fine from the database. The title attributes of the nodes are there, which I was able to log into the console after the query ran. That title attribute is what the vis.js canvas should display/draw as a popup when the hover event is triggered.
The relevant options that I load from this.state.options are:
interaction: {
hover: true
},
manipulation: {
enabled: true
},
Yet the popup message doesn't ever show up.
I tried to check whether the popup event actually happens, and seemingly it does, since the showPopup event of vis.js, -which happens when a popup is shown- does get triggered, the console.log I put into it runs, but the popup still doesn't actually show up on the screen.
In theory, in ReactJS I use the NodeGraph to draw the graph with the adequate data and it should do basically the same thing as this line:
var network = new vis.Network(container, data, options);
But apparently something with the popups gets lost in the ReactJS version, unless the reason is that I am doing something wrong.
Does anybody know of any solution that could help me display the title tooltip of a vis.js graph with a hover event in ReactJS?
Any help is much appreciated.
Thanks,
Balint
Cleanest way to do it,
in your index.css file add this line:
#import url("~vis/dist/vis.min.css");
Oddly by default the css is hiding the tooltip.
In your CSS set .vis-network: {overflow:visible;}
For me below CSS worked:
.vis-tooltip {
position: absolute;
}
I was having this issue as well a while back. It had to do with having the incorrect path to vis-network.min.css.
In the example they do this:
<link href="../../../dist/vis-network.min.css" rel="stylesheet" type="text/css" />
but in your own project that is probably not the correct path. Check your path and that should fix your problem.

ExtJS 4.2.1 XTemplate and subtemplates (statics)

I got a custom Ext.Component with a view XTemplates. I do need some of theese Templates outside of the view in my controller too.
Is it possible to refer to static members in functions of a XTemplate. Or is there another much better way???
something like this:
Ext.define('app.view.ApplicationHeader', {
extend: 'Ext.Component',
name: 'app-header',
xtype: 'app-header',
height: 67,
margin: 0,
statics: {
mainIconTpl: new Ext.XTemplate('someTemplate'),
navigationItemsTpl: new Ext.XTemplate( 'anotherTemplate'),
userInfoTpl: new Ext.XTemplate('userTemplate')
},
html: new Ext.XTemplate('... {[ this.renderMainIcons() ]} {[ this.renderUserInfo() ]} ...',
'... {[ this.renderNavigationBarItems() ]} ...',
{
me: this,
renderMainIcons: function () {
return view.static.mainIconTpl.apply(MR.Sitemap.Items);
},
renderUserInfo: function () {
return view.static.userInfoTpl.apply();
},
renderNavigationBarItems: function () {
return view.static.navigationItemsTpl.apply();
}
}).apply()
});
i also dont know how i could apply subtemplates which are members of the view. I declared them global right know which i really dont like to do.
please!
Your code is not working because the apply method of the main template is called before the class definition (i.e. the define method) is even called.
You can create your static template that uses the other static members of the class in the post-create function (see the last param of the define method).
Then in order for the template to be available, I would override the initComponent method and set the html property there.
Ext.define('app.view.ApplicationHeader', {
extend: 'Ext.Component',
name: 'app-header',
xtype: 'app-header',
height: 67,
margin: 0,
statics: {
mainIconTpl: new Ext.XTemplate('someTemplate'),
navigationItemsTpl: new Ext.XTemplate('anotherTemplate'),
userInfoTpl: new Ext.XTemplate('userTemplate')
},
initComponent: function() {
// Here, your statics are available, and you're in the scope of your
// class *instance*
this.html = this.self.viewTemplate.apply();
this.callParent(arguments);
}
}, function() {
// In the post create function, this is the class constructor
// (i.e. app.view.ApplicationHeader)
var cls = this;
// In fact, you could also create your sub templates here if you prefer
// e.g.
// cls.useInfoTpl = new Ext.XTemplate('userTemplate')
// So, viewTemplate will be a static property of the class
cls.viewTemplate = new Ext.XTemplate('... {[ this.renderMainIcons() ]} {[ this.renderUserInfo() ]} ...',
'... {[ this.renderNavigationBarItems() ]} ...', {
renderMainIcons: function() {
return cls.mainIconTpl.apply();
},
renderUserInfo: function() {
return cls.userInfoTpl.apply();
},
renderNavigationBarItems: function() {
return cls.navigationItemsTpl.apply();
}
});
});
According to the link, you should be able to put this directly in your XTemplate. No need for statics
{[ MyApp.tpls.someOtherTpl.apply(values) ]}
Multiple templates in Nested List
You could also try putting all of these XTemplates in initComponent instead since you're not injecting any values for XTemplate after initial component render. The apply() will just return you an HTML fragment which should be able to be appended anywhere within the XTemplate.
If you're trying to put logical or conditional tpl operators i.e. <tpl for="parent.someVar">...</tpl> in any of the sub XTemplates, then that's another problem so it all depends on what you're trying to accomplish.
Ext.define('app.view.ApplicationHeader', {
extend: 'Ext.Component',
name: 'app-header',
xtype: 'app-header',
height: 67,
margin: 0,
initComponent: function() {
var me = this,
me.mainIconTpl = new Ext.XTemplate('someTemplate'),
me.navigationItemsTpl = new Ext.XTemplate( 'anotherTemplate'),
me.userInfoTpl = new Ext.XTemplate('userTemplate');
me.tpl = new Ext.XTemplate(
'...', me.mainIconTpl.apply(MR.Sitemap.Items),
'...', me.navigationItemsTpl.apply(someValues),
'...', me.userinfoTpl.apply(someValues),
'...'
);
Ext.apply(me, {
html: me.tpl
});
me.callParent();
}
});

Categories

Resources