I am trying to implement ng2-charts in my Angular 2 project and I was wondering about creating custom onclick events. Meaning, I want to override the current onclick events on the carts to do some custom functions (redirect to a page, have a modal show up, etc).
Is there a simple way to do this? Is it built in at all?
Any insight would be appreciated it
I found this solution at https://github.com/valor-software/ng2-charts/issues/489
public chartClicked(e: any): void {
if (e.active.length > 0) {
const chart = e.active[0]._chart;
const activePoints = chart.getElementAtEvent(e.event);
if ( activePoints.length > 0) {
// get the internal index of slice in pie chart
const clickedElementIndex = activePoints[0]._index;
const label = chart.data.labels[clickedElementIndex];
// get value by index
const value = chart.data.datasets[0].data[clickedElementIndex];
console.log(clickedElementIndex, label, value)
}
}
}
Try to read DOCS
They have pretty good and understandable explanation of use.
There-are built-in 2 event handlers:
Events
chartClick: fires when click on a chart has occurred, returns information regarding active points and labels
chartHover: fires when mousemove (hover) on a chart has occurred, returns information regarding active points and labels
In code it looks like that:
<base-chart class="chart"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[options]="lineChartOptions"
[colors]="lineChartColours"
[legend]="lineChartLegend"
[chartType]="lineChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></base-chart>
</div>
that chartHovered and chartClicked are your custom functions, which could has another names, and do custom things like showing modal, redirect to url etc.
public chartClicked(e: any): void {
console.log(e);
}
e.active[0]._model and e.active[0]._view contain information about the part of the chart you clicked (i.e. label).
I hope my answer is correct. After much searching for the only solution I found was:
public chartClicked(e:any):void {
if(e.active.length > 0){
var points = [];
var pointSelected = e.active[0]._chart.tooltip._model.caretY;
var legends = e.active[0]._chart.legend.legendItems;
for (var i = 0; i < e.active.length; ++i) {
points.push(e.active[i]._model.y);
}
let position = points.indexOf(pointSelected);
let label = legends[position].text
console.log("Point: "+label)
}}
After checking multiple places, I got it working like this for click event.
HTML:
<div class="chart">
<canvas
baseChart
[data]="pieChartData"
[type]="pieChartType"
[options]="pieChartOptions"
[plugins]="pieChartPlugins"
(chartHover)="chartHovered($event)"
>
</canvas>
</div>
TS:
public chartHovered(e: any): void {
if (e.event.type == "click") {
const clickedIndex = e.active[0]?.index;
console.log("Clicked index=" + clickedIndex);
}
}
Ref
Related
iam passing an array of object containing photo url to swiper container,
for unverified photo iam showing a a text,
on each slide change i have to check photos verification status
the variable is changing to true or false but dom is not updating even when i inspect it shows
ng-reflect-ng-if :'true;
but i have to scroll to a specific slide on user clicking on specific thumbnail
for that iam using
this.photoPopupSlider.swiperRef.slideTo(RowIndex, sliderSpeed);
at this time onSlidChange event triggers updates the dom correctly
my component.html 👇
<div style="padding: 0">
<swiper
[config]="sliderConfig"
class="sliderBox"
[pagination]="false"
(slideChange)="onSlideChange($event)"
#photoPopupSlider
>
<ng-template swiperSlide *ngFor="let pic of myPhotos">
<div
class="slide_wrap"
style="height: 68vh; background: #f3f4f9"
>
<img
[src]="pic.image"
class="slide_img"
style="height: auto; width: auto; max-width: 100%"
/>
</div>
</ng-template>
</swiper>
<span
id="photo_under_verification"
*ngIf="underVerification"
class="ph_uv"
>Photo under verification</span
>
</div>
my component.ts file 👇
underVerification = false;
onSlideChange([swiper]: any) {
const index = swiper.activeIndex;
this._ps.sliderIndex = index;
this.sliderIndex = index;
console.log("sliderChanged", this.sliderIndex);
const photoShowingInSlider = this.myPhotos[index];
const isPhotoUnderVerification =
photoShowingInSlider?.photostatus == "0" ? true : false;
this.underVerification = isPhotoUnderVerification;
console.log("under", isPhotoUnderVerification);
// const photUnderVerifyMsg = document.getElementById(
// "photo_under_verification"
// ).style;
// if (isPhotoUnderVerification) photUnderVerifyMsg.display = "inline";
// else photUnderVerifyMsg.display = "none";
}
actually i was able to implement the logic by using document.getElemntById
those commented lines of code ( i thing it is a bad way in angular to access dom that way )
how can implement this login in angular way ( means not accessing dom directly ) ?
Well, you have to tell Angular to run change detection as the code under onSlideChange (and other events) runs outside of Zone
Note that Swiper Angular component all events emits outside of NgZone
for better perfomance. Dont forget to use ngzone.run or
ChangeDetector if you need to change view (e.g slides) in event
handlers (e.g slideChange).
import the ChangeDetectorRef in the constructor and call the detectChange method in onSlideChange method
this.cd.detectChanges()
Here is the working code. I just take a random stackblitz link so adapt to your swiper version: https://stackblitz.com/edit/swiper-angular-example-rmgv2b
I'm learning angular via youtube, but I'm trying to do something new, and I'm getting an error on that, my code is attached below, help me out.
I want to setAttribute like this div.setAttribute('(click)',"popUp($event)"); but I got error.
TypeScript
export class AppComponent {
createEl(){
console.time("timer");
for (let i = 0; i < 10; i++) {
let div = document.createElement("div");
div.textContent = `Hello, World! ${i}`;
div.setAttribute('(click)',"popUp($event)");
document.getElementById('divEl')?.appendChild(div);
};
console.timeEnd("timer");
}
HTML
<div id="divEl"></div>
<button (click)="createEl()">click me</button>
Error
This is not really the angular way of doing things. Try to avoid operations on document such as document.createElement.
A better way to achieve this would be to define what the repeating element would look like in the template and drive it from an array. That way we can keep the template doing display and the typescript doing processing, and Angular handling everything in between.
HTML
<div id="divEl">
<div *ngFor="let row of rows; index as i;" (click)="popUp($event)">
Hello, World! {{i}}
</div>
</div>
<button (click)="createEl()">click me</button>
Typescript
export class AppComponent {
rows: unknown[] = [];
createEl():void {
this.rows.push('something');
}
popUp(event:Event):void {}
}
More reading on loops: https://angular.io/api/common/NgForOf
That's right check below.
div.addEventListener('click', (e) => {
this.popUp(e);
});
Problem is you are trying to do angular stuff with pure javascript.
<div (click)="method()"> is angular.
In javascript you'd do someting like this <button onclick="myFunction()">Click me</button>
Other options are to use event handlers https://www.w3schools.com/js/js_htmldom_eventlistener.asp
Anyhow, angular doesn't recommend changes the DOM because then it won't recognize those changes. Here are multiple examples ho to properly change the dom
Correct way to do DOM Manipulation in Angular 2+
https://medium.com/#sardanalokesh/understanding-dom-manipulation-in-angular-2b0016a4ee5d
`
You can set the click event as shown below instead of using setAttribute
div.addEventListener('click', (e) => {
this.popUp(e);
});
(click) is not an html attribute, it is Angular event binding syntax
This syntax consists of a target event name within parentheses to the left of an equal sign, and a quoted template statement to the right.
You cannot use that with JavaScript. Use
div.onclick = popUp;
export class AppComponent {
createEl(){
console.time("timer");
for (let i = 0; i < 10; i++) {
let div = document.createElement("div");
div.textContent = `Hello, World! ${i}`;
div.addEventListener('click', (e) => {
this.popUp(e);
});
document.getElementById('divEl')?.appendChild(div);
};
console.timeEnd("timer");
}
I am creating a basic todo - list and the input field seems to save all the data entered as a drop down list and I am unable to fix it.I have tried using the.reset() fn and also other options shown here like using val("") or val(0) and nothing seems to work.So can anyone suggest how I can fix this. I had tried the return fn and the val function as well but it still wouldn't work.Thanks in advance.
const addForm = document.querySelector('.add');
const todolst = document.querySelector('.todolst');
const deletelst = document.querySelector('.delete')
const task = addForm.add.value.trim();
const newTemplate = task => {
const html =
<li class="list-group-item d-flex justify-content-between align-items-center"><span>${task}</span><i class="far fa-trash-alt delete"></i></li>
todolst.innerHTML += html
}
// to add a tag
addForm.addEventListener('Enter', e => {
e.preventDefault();
if (task.length) {
newTemplate(task);
addForm.val(0);
}
});
[enter image description here][1]
[1]: https://i.stack.imgur.com/gIdeL.png
Well, I managed to have found a solution which was by just clearing the cache on my browser. Although not the one I was expecting but seemed to have done the job. I tried the reset() and the clear() options which seemed have cleared the text just entered but not the previous ones that got stored onto the drop-down box.
I am implementing Drag and Drop in React.js. How to pass custom react component in I am implementation Darg and Drop in React.js. I want to have my custom react component as a preview image of my draggle component. How to pass custom react component in setDragImage. I do not want an image.
onDragStart = (e, index) => {
this.draggedIndex = index;
this.draggedItem = this.state.list[index];
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/html", e.target.parentNode);
e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
};
The expected result is a custom preview component.
Thanks in advance
Was searching for the same thing until I finally stumbled upon the answer. Hope this helps someone (TSX):
private onDragStart(e: React.DragEvent) {
let image: JSX.Element = (<></>); // <== whatever you want here
var ghost = document.createElement('div');
ghost.style.transform = "translate(-10000px, -10000px)";
ghost.style.position = "absolute";
document.body.appendChild(ghost);
e.dataTransfer.setDragImage(ghost, 0, 0);
ReactDOM.render(image, ghost);
}
(This is a little over-simplified as you also need to remove the ghost from the DOM after a drop).
Am using ng2-charts - https://github.com/valor-software/ng2-charts
I have a pie chart, if I hard code the data when I declare the variable at the top of my component.ts file like in the example my pie chart displays.
But I obviously want to make the pie char data dynamic. I can call the data through a service (which is a number), add the number to the data array, and the pie chart does not work. But if I do console log, the array prints out with the new data/number I have added to it.
I need to redraw the table somehow. Can't figure out how.
public pieChartLabels:string[] = ['Red Flags','Green Flags'];
public pieChartData: number[] = [200, 400];
public chartType:string = 'pie';
public redFlagsTotal: any;
public greenFlagsTotal: any;
constructor(private dataService:flagService) {
let component = this;
this.redFlagsTotal = this.dataService.getRedFlags().then(function(result){
component.redFlagsTotal = result.length;
console.log(component.redFlagsTotal);
component.pieChartData.push(component.redFlagsTotal);
console.log(component.pieChartData);
});
this.greenFlagsTotal = this.dataService.getGreenFlags().then(function(result){
component.greenFlagsTotal = result.length;
console.log(component.greenFlagsTotal);
component.pieChartData.push(component.greenFlagsTotal);
console.log(component.pieChartData);
});
}
You can hide and show the chart for a millisecond like this:
refreshChart(){
this.mychart.show = false;
setTimeout(()=>{
this.mychart.show = true;
},1);
}
in the template use than mychart.show with *ngIf like this:
<div style="display: block">
<canvas baseChart
*ngIf="mychart.show"
[data]="pieChartData"
[labels]="pieChartLabels"
[chartType]="pieChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
in your function than you can use the refreshChart() function when you want to refresh the chart.
EDIT:
If you reinitialize the array than the char should update automatically, instead of this:
component.pieChartData.push(component.greenFlagsTotal);
do this:
let temp = [...component.pieChartData, ...component.greenFlagsTotal];
component.pieChartData = [...temp];
Solved! Hide the canvas until the data has loaded.
<div *ngIf="pieChartData.length > 1" style="display: block">
<canvas baseChart
[data]="pieChartData"
[labels]="flagPieChartLabels"
[chartType]="chartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
Another way to make your chart data dynamic is to bind the chart directive via ViewChild like so:
...
export class HomeComponent {
#ViewChild(BaseChartDirective)
public chart: BaseChartDirective;
void updateChart() {
this.chart.chart.update(); // This re-renders the canvas element.
}
Now you can call updateChart everytime your dataset has changed to keep your chart up to date!
Hiding the canvas with *ngIf reload the whole component.
It seems that if you push data and label then remove them after 1 ms, it will reload the correct chart data.
reloadChart(){
this.pieChartLabels.push('reload');
this.pieChartData.push(1);
setTimeout(() => {
this.pieChartData.pop();
this.pieChartLabels.pop();
},1);
}