Skip to content

Commit 0eba014

Browse files
committed
Creating the updateOnlyWithRevision property
1 parent 8c2a889 commit 0eba014

9 files changed

+131
-30
lines changed

FAQ.md

+5
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ export class AppPlotlyComponent {
3030
}
3131
}
3232
```
33+
34+
35+
## The graph is too slow when interacting, what could I do ?
36+
37+
Angular checks all the data everytime to see if there is a change to be applied, sometimes this brings unexpected slowness when treating a large data to be displayed. To avoid this check, set the property `updateOnlyWithRevision` to `true`. When you need the component to update, you can use the `revision` property (a number) to force it to update. Simply incrementing it (e.g.: `this.revision += 1`) will force the component to be updated.

README.md

+19-18
Original file line numberDiff line numberDiff line change
@@ -94,24 +94,25 @@ The `plotly.js` is bundled within the angular code. To avoid this, please read [
9494

9595
**Warning**: for the time being, this component may _mutate_ its `layout` and `data` props in response to user input, going against React rules. This behaviour will change in the near future once https://github.com/plotly/plotly.js/issues/2389 is completed.
9696

97-
| Prop | Type | Default | Description |
98-
| ------------------------ | ---------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
99-
| `[data]` | `Array` | `[]` | list of trace objects (see https://plot.ly/javascript/reference/) |
100-
| `[layout]` | `Object` | `undefined` | layout object (see https://plot.ly/javascript/reference/#layout) |
101-
| `[frames]` | `Array` | `undefined` | list of frame objects (see https://plot.ly/javascript/reference/) |
102-
| `[config]` | `Object` | `undefined` | config object (see https://plot.ly/javascript/configuration-options/) |
103-
| `[revision]` | `Number` | `undefined` | When provided, causes the plot to update _only_ when the revision is incremented. |
104-
| `[updateOnLayoutChange]` | `Boolean` | `true` | Flag which determines if this component should watch to changes on `layout` property and update the graph |
105-
| `[updateOnDataChange]` | `Boolean` | `true` | Flag which determines if this component should watch to changes on `data` property and update the graph |
106-
| `(initialized)` | `Function(figure, graphDiv)` | `undefined` | Callback executed after plot is initialized. See below for parameter information. |
107-
| `(update)` | `Function(figure, graphDiv)` | `undefined` | Callback executed when when a plot is updated due to new data or layout, or when user interacts with a plot. See below for parameter information. |
108-
| `(purge)` | `Function(figure, graphDiv)` | `undefined` | Callback executed when component unmounts, before `Plotly.purge` strips the `graphDiv` of all private attributes. See below for parameter information. |
109-
| `(error)` | `Function(err)` | `undefined` | Callback executed when a plotly.js API method rejects |
110-
| `[divId]` | `string` | `undefined` | id assigned to the `<div>` into which the plot is rendered. |
111-
| `[className]` | `string` | `undefined` | applied to the `<div>` into which the plot is rendered |
112-
| `[style]` | `Object` | `{position: 'relative', display: 'inline-block'}` | used to style the `<div>` into which the plot is rendered |
113-
| `[debug]` | `Boolean` | `false` | Assign the graph div to `window.gd` for debugging |
114-
| `[useResizeHandler]` | `Boolean` | `false` | When true, adds a call to `Plotly.Plot.resize()` as a `window.resize` event handler |
97+
| Prop | Type | Default | Description |
98+
| -------------------------- | ---------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
99+
| `[data]` | `Array` | `[]` | list of trace objects (see https://plot.ly/javascript/reference/) |
100+
| `[layout]` | `Object` | `undefined` | layout object (see https://plot.ly/javascript/reference/#layout) |
101+
| `[frames]` | `Array` | `undefined` | list of frame objects (see https://plot.ly/javascript/reference/) |
102+
| `[config]` | `Object` | `undefined` | config object (see https://plot.ly/javascript/configuration-options/) |
103+
| `[revision]` | `Number` | `undefined` | When provided, causes the plot to update _only_ when the revision is incremented. |
104+
| `[updateOnLayoutChange]` | `Boolean` | `true` | Flag which determines if this component should watch to changes on `layout` property and update the graph. |
105+
| `[updateOnDataChange]` | `Boolean` | `true` | Flag which determines if this component should watch to changes on `data` property and update the graph. |
106+
| `[updateOnlyWithRevision]` | `Boolean` | `false` | If `true`, this component will update only when the property `revision` is increased. |
107+
| `(initialized)` | `Function(figure, graphDiv)` | `undefined` | Callback executed after plot is initialized. See below for parameter information. |
108+
| `(update)` | `Function(figure, graphDiv)` | `undefined` | Callback executed when when a plot is updated due to new data or layout, or when user interacts with a plot. See below for parameter information. |
109+
| `(purge)` | `Function(figure, graphDiv)` | `undefined` | Callback executed when component unmounts, before `Plotly.purge` strips the `graphDiv` of all private attributes. See below for parameter information. |
110+
| `(error)` | `Function(err)` | `undefined` | Callback executed when a plotly.js API method rejects |
111+
| `[divId]` | `string` | `undefined` | id assigned to the `<div>` into which the plot is rendered. |
112+
| `[className]` | `string` | `undefined` | applied to the `<div>` into which the plot is rendered |
113+
| `[style]` | `Object` | `{position: 'relative', display: 'inline-block'}` | used to style the `<div>` into which the plot is rendered |
114+
| `[debug]` | `Boolean` | `false` | Assign the graph div to `window.gd` for debugging |
115+
| `[useResizeHandler]` | `Boolean` | `false` | When true, adds a call to `Plotly.Plot.resize()` as a `window.resize` event handler |
115116

116117
**Note**: To make a plot responsive, i.e. to fill its containing element and resize when the window is resized, use `style` or `className` to set the dimensions of the element (i.e. using `width: 100%; height: 100%` or some similar values) and set `useResizeHandler` to `true` while setting `layout.autosize` to `true` and leaving `layout.height` and `layout.width` undefined. This will implement the behaviour documented here: https://plot.ly/javascript/responsive-fluid-layout/
117118

src/app/demo/demo.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ <h5>Angular Plotly</h5>
1616
<li><a [routerLink]="['/frames']">Frames</a></li>
1717
<li><a [routerLink]="['/timeout-update']">Timeout Update</a></li>
1818
<li><a [routerLink]="['/huge-memory-usage']">Huge Memory Usage</a></li>
19+
<li><a [routerLink]="['/slow-example']">Slow Example</a></li>
1920
</ul>
2021
</div>
2122
</div>

src/app/demo/demo.module.ts

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { MemoryLeakComponent } from './memory-leak/memory-leak.component';
2222
import { FramesComponent } from './frames/frames.component';
2323
import { TimeoutUpdateComponent } from './timeout-update/timeout-update.component';
2424
import { HugeMemoryUsageComponent } from './huge-memory-usage/huge-memory-usage.component';
25+
import { SlowExampleComponent } from './slow-example/slow-example.component';
2526

2627

2728
const demoRoutes: Routes = [
@@ -35,6 +36,7 @@ const demoRoutes: Routes = [
3536
{ path: 'frames', component: FramesComponent, data: { title: 'Frames' } },
3637
{ path: 'timeout-update', component: TimeoutUpdateComponent, data: { title: 'Timeout Update' } },
3738
{ path: 'huge-memory-usage', component: HugeMemoryUsageComponent, data: { title: 'Huge Memory Usage' } },
39+
{ path: 'slow-example', component: SlowExampleComponent, data: { title: 'Slow example' } },
3840

3941
{ path: '', redirectTo: '/home', pathMatch: 'full' },
4042
];
@@ -65,6 +67,7 @@ PlotlyViaCDNModule.plotlyVersion = '1.49.4';
6567
FramesComponent,
6668
TimeoutUpdateComponent,
6769
HugeMemoryUsageComponent,
70+
SlowExampleComponent,
6871
],
6972
exports: [DemoComponent],
7073
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<div>
2+
<plotly-plot [data]="data" [layout]="layout" [updateOnLayoutChange]="false" [updateOnDataChange]="false" [updateOnlyWithRevision]="false"></plotly-plot>
3+
4+
<div class="row">
5+
<div class="col">
6+
<code>[updateOnLayoutChange]="false"</code>
7+
</div>
8+
<div class="col">
9+
<code>[updateOnDataChange]="false"</code>
10+
</div>
11+
<div class="col">
12+
<code>[updateOnlyWithRevision]="false"</code>
13+
</div>
14+
</div>
15+
16+
<plotly-plot [data]="data" [layout]="layout" [updateOnLayoutChange]="true" [updateOnDataChange]="true" [updateOnlyWithRevision]="true"> </plotly-plot>
17+
18+
<div class="row">
19+
<div class="col">
20+
<code>[updateOnLayoutChange]="true"</code>
21+
</div>
22+
<div class="col">
23+
<code>[updateOnDataChange]="true"</code>
24+
</div>
25+
<div class="col">
26+
<code>[updateOnlyWithRevision]="true"</code>
27+
</div>
28+
</div>
29+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
.row {
4+
display: flex;
5+
}
6+
7+
.col {
8+
flex: 1;
9+
align-self: center;
10+
justify-content: center;
11+
text-align: center;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
@Component({
4+
selector: 'plotly-slow-example',
5+
templateUrl: './slow-example.component.html',
6+
styleUrls: ['./slow-example.component.scss'],
7+
})
8+
export class SlowExampleComponent implements OnInit {
9+
data: any;
10+
layout: any;
11+
12+
ngOnInit() {
13+
this.layout = { showlegend: false };
14+
this.data = this.getData();
15+
}
16+
17+
getData() {
18+
const startValue = 0;
19+
const stopValue = 100000;
20+
const pointNum = 10000;
21+
const traceNum = 10;
22+
const currValue = startValue;
23+
const step = (stopValue - startValue) / (pointNum - 1);
24+
25+
const data = [];
26+
27+
for (let j = 0; j < traceNum; j++) {
28+
const X = [];
29+
const Y = [];
30+
for (let i = 0; i < pointNum; i++) {
31+
X.push(currValue + step * i);
32+
Y.push(this.gaussianRand() * 8 + j * 5);
33+
}
34+
data.push({
35+
type: 'scattergl',
36+
mode: 'line',
37+
x: X,
38+
y: Y
39+
});
40+
}
41+
42+
return data;
43+
}
44+
45+
gaussianRand() {
46+
let rand = 0;
47+
for (let i = 0; i < 6; i += 1) {
48+
rand += Math.random();
49+
}
50+
return rand / 6 - 0.5;
51+
}
52+
}

src/app/shared/plot/plot.component.spec.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,21 @@ describe('PlotComponent', () => {
7373
it('should update the graph when the data changes', (done) => {
7474
spyOn(component, 'updatePlot');
7575
component.data = [{ y: [10, 15, 13, 17], type: 'box' }];
76-
expect(component.datarevision).toEqual(0);
76+
7777
component.createPlot().then(() => {
7878
component.ngDoCheck();
7979
expect(component.updatePlot).not.toHaveBeenCalled();
80-
expect(component.datarevision).toEqual(0);
8180

8281
component.data = [{ y: [11, 15, 13, 17], type: 'box' }];
8382
component.ngDoCheck();
8483
expect(component.updatePlot).toHaveBeenCalled();
85-
expect(component.datarevision).toEqual(1);
8684

8785
component.ngDoCheck();
8886
expect(component.updatePlot).toHaveBeenCalledTimes(1);
89-
expect(component.datarevision).toEqual(1);
9087

9188
component.data[0].y[0] = 12;
9289
component.ngDoCheck();
9390
expect(component.updatePlot).toHaveBeenCalledTimes(2);
94-
expect(component.datarevision).toEqual(2);
9591
done();
9692
});
9793
});
@@ -107,6 +103,9 @@ describe('PlotComponent', () => {
107103
component.ngDoCheck();
108104
expect(component.updatePlot).toHaveBeenCalled();
109105

106+
component.ngDoCheck();
107+
expect(component.updatePlot).toHaveBeenCalledTimes(1);
108+
110109
component.layout.title = 'title three ';
111110
component.ngDoCheck();
112111
expect(component.updatePlot).toHaveBeenCalledTimes(2);

src/app/shared/plot/plot.component.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export class PlotComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
3333
public resizeHandler?: (instance: Plotly.PlotlyHTMLElement) => void;
3434
public layoutDiffer: KeyValueDiffer<string, any>;
3535
public dataDiffer: IterableDiffer<Plotly.Data>;
36-
public datarevision: number = 0;
3736

3837
@ViewChild('plot', {static: true}) plotEl: ElementRef;
3938

@@ -51,6 +50,7 @@ export class PlotComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
5150

5251
@Input() updateOnLayoutChange = true;
5352
@Input() updateOnDataChange = true;
53+
@Input() updateOnlyWithRevision = false;
5454

5555
@Output() initialized = new EventEmitter<Plotly.Figure>();
5656
@Output() update = new EventEmitter<Plotly.Figure>();
@@ -144,6 +144,10 @@ export class PlotComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
144144
}
145145

146146
ngDoCheck() {
147+
if (this.updateOnlyWithRevision) {
148+
return false;
149+
}
150+
147151
let shouldUpdate = false;
148152

149153
if (this.updateOnLayoutChange) {
@@ -173,7 +177,6 @@ export class PlotComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
173177
}
174178

175179
if (shouldUpdate && this.plotlyInstance) {
176-
this.datarevision += 1;
177180
this.updatePlot();
178181
}
179182
}
@@ -228,17 +231,13 @@ export class PlotComponent implements OnInit, OnChanges, OnDestroy, DoCheck {
228231
}
229232

230233
updatePlot() {
231-
console.log('updatePlot', this.plotlyInstance);
232234
if (!this.plotlyInstance) {
233235
const error = new Error(`Plotly component wasn't initialized`);
234236
this.error.emit(error);
235237
throw error;
236238
}
237239

238-
const layout = {
239-
...{datarevision: this.datarevision},
240-
...this.layout,
241-
};
240+
const layout = {...this.layout};
242241

243242
return this.plotly.update(this.plotlyInstance, this.data, layout, this.config, this.frames).then(() => {
244243
const figure = this.createFigure();

0 commit comments

Comments
 (0)