I want to put thick border in all my cells. this is an angular project, I am using typescript.
I can do this for 1 cell,
worksheet.getCell('A1').border = {
top: { style: 'thick' },
left: { style: 'thick' },
bottom: { style: 'thick' },
right: { style: 'thick' }
};
But I want to do something like 2 nested for loops . For each row , make each cell thick
Here is I tried : app.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import * as jspdf from 'jspdf';
import html2canvas from 'html2canvas';
// import * as XLSX from 'xlsx';
import * as ExcelJS from 'exceljs';
import * as FileSaver from 'file-saver';
import { ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-items-report',
templateUrl: './items-report.component.html',
styleUrls: ['./items-report.component.css']
})
export class ItemsReportComponent implements OnInit {
purchases: any;
constructor(private dataService: DataService) {
this.GetPurchases();
}
ngOnInit(): void {
}
async GetPurchases() {
const response = await this.dataService.GetPurchases();
const dataService = await response.json();
this.purchases = dataService;
}
downloadPDF() {
const data = document.getElementById('purchaseTable'); // Id of the table
html2canvas(data).then(canvas => {
// Few necessary setting options
const imgWidth = 208;
const pageHeight = 295;
const imgHeight = canvas.height * imgWidth / canvas.width;
const heightLeft = imgHeight;
const contentDataURL = canvas.toDataURL('image/png');
// Your 1st parameter (landscape [l] or portrait [p]) determines what becomes the width and the height.
const pdf = new jspdf('p', 'mm', 'a4'); // A4 size page of PDF
const position = 0;
/* addImage explained below:
param 1 -> image in code format
param 2 -> type of the image. SVG not supported. needs to be either PNG or JPEG.
all params are specified in integer
param 3 -> X axis margin from left
param 4 -> Y axis margin from top
param 5 -> width of the image
param 6 -> height of the image
*/
// pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight);
// pdf.addImage(contentDataURL, 'PNG', 18, 30, imgWidth - 17, imgHeight);
pdf.addImage(contentDataURL, 'PNG', 18, 30, imgWidth - 21, imgHeight);
pdf.save('MYPdf.pdf'); // Generated PDF
});
}
downloadExcel() {
const date = new Date().toISOString().slice(0, 10).split('-').reverse().join('/');
console.log(date);
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('My Sheet');
worksheet.columns = [
{ header: 'Id', key: 'id', width: 10},
{ header: 'Name', key: 'name', width: 32 },
{ header: 'Quantity', key: 'quantity', width: 15 },
{ header: 'Rate', key: 'rate', width: 15 },
{ header: 'Date', key: 'date', width: 15 },
{ header: 'Total', key: 'total', width: 15 }
];
for (const purchase of this.purchases) {
worksheet.addRow({
id: purchase.item_id ,
date: purchase.item_purchase_date.toString().slice(0, 10).split('-').reverse().join('/'),
name: purchase.item_name,
quantity: purchase.item_quantity,
rate: purchase.item_rate,
total: purchase.item_rate * purchase.item_quantity
})
.alignment = { horizontal: 'left' };
}
worksheet.getRow(1).font = { bold: true };
// Iterate over all rows (including empty rows) in a worksheet
worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
console.log('Row ' + rowNumber + ' = ' + JSON.stringify(row.values));
row.eachCell({ includeEmpty: true }, (cell, rowNumber) => {
// ...please make my cell thick here
// i cant no longer write a1 or b1
// i need to access all cells - including empty cells
});
});
book.xlsx.readFile('export.xlsx');
}
I need to make each of my cells thick, inside for loop. So please help me how to access each cell in a loop without writing a1 or b1
Worksheet gives you a columns property on which you can iterate and use it like :-
worksheet.columns.forEach(column => {
column.border = {
top: { style: "thick" },
left: { style: "thick" },
bottom: { style: "thick" },
right: { style: "thick" }
};
});
To put border in all cells :-
exceljs version 1.12.0
worksheet.columns.forEach((col) => {
col.style.font = { name: 'Comic Sans MS' };
col.style.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } };
})
Related
hi i want charting stock data in web page.
i found sample project in github
https://github.com/tvjsx/tvjs-xp
i change code and connect the binance and receive and charting real time data.
i have some problem after add online receive data chart lagend bouttun not work and i cant add layer.
please help me.
thanks
<trading-vue :data="dc" :width="this.width" :height="this.height"
title-txt="TVJS XP" :key="resetkey"
:chart-config="{DEFAULT_LEN:70}"
ref="tvjs"
:legend-buttons="['display', 'settings', 'up', 'down', 'add', 'remove']"
:toolbar="true"
:index-based="index_based"
:color-back="colors.colorBack"
:color-grid="colors.colorGrid"
:color-text="colors.colorText"
:extensions="ext"
:overlays="ovs"
:x-settings="xsett">
</trading-vue>
<span class="gc-mode">
<input type="checkbox" v-model="index_based">
<label>Index Based</label>
</span>
export default {
name: 'DataHelper',
icon: '⚡',
description: 'Real-time updates. Play with DataCube in the console',
props: ['night', 'ext', 'resetkey'],
components: {
TradingVue
},
mounted() {
window.addEventListener('resize', this.onResize)
this.onResize()
// Load the last data chunk & init DataCube:
let now = Utils.now()
this.load_chunk([now - Const.HOUR4, now]).then(data => {
this.dc = new DataCube({
ohlcv: data['dc.data'],
// onchart: [{
// type: 'EMAx6',
// name: 'Multiple EMA',
// data: []
// }],
offchart: [
// {
// type: 'BuySellBalance',
// name: 'Buy/Sell Balance, $lookback',
// data: [],
// settings: {}
// },
{
name: "RSI, 20",
type: "Range",
data: [],
settings: {
"upper": 70,
"lower": 30,
"backColor": "#9b9ba316",
"bandColor": "#666"
}
},
],
datasets: [{
type: 'Trades',
id: 'binance-btcusdt',
data: []
}]
}, { aggregation: 100 })
// Register onrange callback & And a stream of trades
this.dc.onrange(this.load_chunk)
this.$refs.tvjs.resetChart()
this.stream = new Stream(WSS)
this.stream.ontrades = this.on_trades
window.dc = this.dc // Debug
window.tv = this.$refs.tvjs // Debug
})
},
methods: {
onResize(event) {
this.width = window.innerWidth
this.height = window.innerHeight - 50
},
// New data handler. Should return Promise, or
// use callback: load_chunk(range, tf, callback)
async load_chunk(range) {
let [t1, t2] = range
let x = 'BTCUSDT'
let q = `${x}&interval=1m&startTime=${t1}&endTime=${t2}`
let r = await fetch(URL + q).then(r => r.json())
return this.format(this.parse_binance(r))
},
// Parse a specific exchange format
parse_binance(data) {
if (!Array.isArray(data)) return []
return data.map(x => {
for (var i = 0; i < x.length; i++) {
x[i] = parseFloat(x[i])
}
return x.slice(0,6)
})
},
format(data) {
// Each query sets data to a corresponding overlay
return {
'dc.data': data
// other onchart/offchart overlays can be added here,
// but we are using Script Engine to calculate some:
// see EMAx6 & BuySellBalance
}
},
on_trades(trade) {
this.dc.update({
t: trade.T, // Exchange time (optional)
price: parseFloat(trade.p), // Trade price
volume: parseFloat(trade.q), // Trade amount
'datasets.binance-btcusdt': [ // Update dataset
trade.T,
trade.m ? 0 : 1, // Sell or Buy
parseFloat(trade.q),
parseFloat(trade.p)
],
// ... other onchart/offchart updates
})
}
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize)
if (this.stream) this.stream.off()
},
computed: {
colors() {
return this.$props.night ? {} : {
colorBack: '#fff',
colorGrid: '#eee',
colorText: '#333'
}
},
},
data() {
return {
dc: {},
width: window.innerWidth,
height: window.innerHeight,
index_based: false,
xsett: {
'grid-resize': { min_height: 30 }
},
ovs: Object.values(Overlays),
}
}
}
How would like to modify this codepen https://codepen.io/varcharles/pen/qNQpRv
When hovering a red dot the box on right should change is background related on which hotspot is selected. So, four different images related to 4 different hotspots.
const dataField = document.querySelector('.data');
const points = [
{
x: '30px',
y: '50px',
data: 'Targeting Lasers',
},
{
x: '460px',
y: '210px',
data: 'Targeting Lasers'
},
{
x: '250px',
y: '350px',
data: 'Sheild Generators'
},
{
x: '3890px',
y: '550px',
data: 'Sensor Array'
}
];
points.forEach((point) => {
let img = document.createElement('img');
img.style.left = point.x;
img.style.top = point.y;
img.title = point.data;
img.className= 'overlay-image';
img.src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/544303/Target_Logo.svg"
overlay.appendChild(img);
img.data = point.data;
img.addEventListener('mouseenter', handleMouseEnter);
img.addEventListener('mouseleave', handleMouseLeave);
});
function handleMouseEnter(event) {
dataField.innerHTML = event.currentTarget.data;
}
function handleMouseLeave(event) {
dataField.innerHTML = ' ';
}
Can someone please help me? Thank you a lot for your attention
You can just add more data and assign each data object to the images. The following will change the background image when hovering the hotspot.
const overlay = document.querySelector('.image-overlay');
const dataField = document.querySelector('.data');
const points = [
{
x: '320px',
y: '50px',
data: {
title: 'Extended Cockpit',
image: "url('https://dummyimage.com/320x320/ff0000/fff')",
}
},
{
x: '460px',
y: '210px',
data: {
title: 'Targeting Lasers',
image: "url('https://dummyimage.com/320x320/00ff00/fff')",
}
},
{
x: '250px',
y: '350px',
data: {
title: 'Sheild Generators',
image: "url('https://dummyimage.com/320x320/0000ff/fff')",
}
},
{
x: '3890px',
y: '550px',
data: {
title: 'Sensor Array',
image: "url('https://dummyimage.com/320x320/000000/fff')",
}
}
];
points.forEach((point) => {
let img = document.createElement('img');
img.style.left = point.x;
img.style.top = point.y;
img.title = point.data.title;
img.className= 'overlay-image';
img.src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/544303/Target_Logo.svg"
overlay.appendChild(img);
// Sets title and image data attributes
img.data = point.data;
img.addEventListener('mouseenter', handleMouseEnter);
img.addEventListener('mouseleave', handleMouseLeave);
});
function handleMouseEnter(event) {
// Set title and background image based on data set in target
dataField.innerHTML = event.currentTarget.data.title;
dataField.style.backgroundImage = event.currentTarget.data.image;
}
function handleMouseLeave(event) {
// Reset
dataField.innerHTML = ' ';
dataField.style.backgroundImage = 'none';
}
I have one download button and table that store user data in user.component.html file and when user clicks on the download button then it exports all the table's data into an Excel file.
I want to display a loading spinner when the download takes longer than 1.5 seconds to start.
I am using angular 8.
user.component.html file:
<div class ="container">
<button (click)="generateExcel()">
Generate Excel</button>
<table>
-----table related data
<\table>
</div>
user.component.ts file:
generateExcel() {
//Excel Title, Header, Data
const title = 'Car buyers Report';
const header = ["Year", "Month", "User", "Model"]
const data = [
[2007, 1, "jo", "Volkswagen Passat"],
[2007, 1, "mike ", "Toyota Rav4"],
[2007, 1, "david", "Toyota Avensis"],
[2007, 1, "milenda ", "Volkswagen Gol"]
];
//Create workbook and worksheet
let workbook = new Workbook();
let worksheet = workbook.addWorksheet('Car Data');
//Add Row and formatting
let titleRow = worksheet.addRow([title]);
titleRow.font = { name: 'Comic Sans MS', family: 4, size: 16, underline: 'double', bold: true }
worksheet.addRow([]);
let subTitleRow = worksheet.addRow(['Date : ' + this.datePipe.transform(new Date(), 'medium')])
//Add Image
let logo = workbook.addImage({
base64: logoFile.logoBase64,
extension: 'png',
});
worksheet.addImage(logo, 'E1:F3');
worksheet.mergeCells('A1:D2');
//Blank Row
worksheet.addRow([]);
//Add Header Row
let headerRow = worksheet.addRow(header);
// Cell Style : Fill and Border
headerRow.eachCell((cell, number) => {
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF00' },
bgColor: { argb: 'FF0000FF' }
}
cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } }
})
// worksheet.addRows(data);
// Add Data and Conditional Formatting
data.forEach(d => {
let row = worksheet.addRow(d);
let qty = row.getCell(5);
let color = 'FF99FF99';
if (+qty.value < 500) {
color = 'FF9999'
}
qty.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: color }
};
});
worksheet.getColumn(3).width = 30;
worksheet.getColumn(4).width = 30;
worksheet.addRow([]);
//Footer Row
let footerRow = worksheet.addRow(['This is system generated excel sheet.']);
footerRow.getCell(1).fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFCCFFE5' }
};
footerRow.getCell(1).border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } }
//Merge Cells
worksheet.mergeCells(`A${footerRow.number}:F${footerRow.number}`);
//Generate Excel File with given name
workbook.xlsx.writeBuffer().then((data) => {
let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
fs.saveAs(blob, 'CarData.xlsx');
})
}
I imagine your writeBuffer call is what takes you the most time.
As writeBuffer is asynchronous, you could use rxjs observables which allow you to have more complex behaviors than a classic async/await.
First convert your promise to an observable :
// here you have a promise
const wBufferPromise = workbook.xlsx.writeBuffer();
// and here you have an observable, hence the $ char in the variable name
const wBufferObservable = from(wBufferPromise );
You can then probably user timeoutWith from RxJS (which i never used so I won't be able to help you here.
Another possible solution would be to call setTimeout(myFunc,1500) where myFunc sets your spinner visibility if needed.
Edit :
Please have a look at Eliseo's comment.
I am working with the google slides API and attempting to make a slide based on user inputted text. This is my text creating function,
var num = 1;
function createSlide(auth) {
//AUTHENTICATION
const slides = google.slides({version: 'v1', auth});
//CHANGING VARS
var slideId = 'slide_' + num;
var pageId = slideId;;
var textId = 'text_box_' + num;
var elementId = textId;
var iIndex = num;
//SIZING
var pt350 = {
magnitude: 350,
unit: 'PT',
};
//ALL REQUESTS GO IN requests
var requests = [{
createSlide: {
insertionIndex: iIndex,
objectId: pageId,
slideLayoutReference: {
predefinedLayout: 'BLANK'
}
},
//CREATE THE TEXTBOX
createShape: {
objectId: elementId,
shapeType: 'TEXT_BOX',
elementProperties: {
pageObjectId: pageId,
size: {
height: pt350,
width: pt350,
},
transform: {
scaleX: 1,
scaleY: 1,
translateX: 350,
translateY: 100,
unit: 'PT',
},
},
},
},
//INSERT TEXT
{
insertText: {
objectId: elementId,
insertionIndex: iIndex,
text: txt,
},
}];
//BATCH UPDATE
return slides.presentations.batchUpdate({
presentationId,
resource: {
requests,
},
}, (err, res) => {
if (err) {
error(err);
}else{
console.log('Success');
//INCREASES COUNTER BY 1
num = num + 1;
//ASKS IF A NEW SLIDE WANTS TO BE CREATED
askYOrN ();}});}
And it produces this error:
Error: Invalid value at 'requests[0]' (oneof), oneof field 'kind' is already set. Cannot set 'createShape'
The text is inputted and stored correctly. Does anyone have a solution? Thanks in advance.
How about this modification?
Modification points:
Put createSlide, createShape and insertText to each element in the array of requests.
insertionIndex at insertText starts from 0.
Modified script:
Please modify requests as follows.
var requests = [
{
createSlide: {
insertionIndex: iIndex,
objectId: pageId,
slideLayoutReference: {
predefinedLayout: 'BLANK',
},
},
},
{
//CREATE THE TEXTBOX
createShape: {
objectId: elementId,
shapeType: 'TEXT_BOX',
elementProperties: {
pageObjectId: pageId,
size: {
height: pt350,
width: pt350,
},
transform: {
scaleX: 1,
scaleY: 1,
translateX: 350,
translateY: 100,
unit: 'PT',
},
},
},
},
{
//INSERT TEXT
insertText: {
objectId: elementId,
insertionIndex: iIndex - 1,
text: txt,
},
}
];
Note:
This modified script supposes that Slides API is enabled and the scope of https://www.googleapis.com/auth/presentations is included in the scopes.
References:
presentations.batchUpdate
InsertTextRequest
If this was not what you want, I'm sorry.
I am using react-plotly to generate a large timeline of data (10,000-100,000 points) and I animate across the data in another window. I need to get a scrubber (vertical line) that moves with a react-property representing time, but I need to update the scrubber without updating the rest of the timeline, since it would take so long to do so. How can I get just the vertical line to update?
Edit: Was asked for code
In the following code, the backtracks and thresholds objects are Uint32Arrays and represent the y-axis of traces, where the x-axes are the Uint32Arrays backtracksTime and thresholdsTime. What I am trying to get is a vertical line at the x-coordinate currentTime.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Plotly from 'plotly.js';
import Plot from 'react-plotly.js';
import styles from './style.scss';
export default class ThresholdWindow extends Component {
static propTypes = {
name: PropTypes.string,
backtracks: PropTypes.object,
backtracksTime: PropTypes.object,
thresholds: PropTypes.object,
thresholdsTime: PropTypes.object,
currentTime: PropTypes.number,
}
constructor(props) {
super(props);
this.state = {
plotRevision: 0,
width: 0,
height: 0,
};
}
componentDidMount() {
const resizeObserver = new ResizeObserver(entries => {
const oldPlotRevision = this.state.plotRevision;
const rect = entries[0].contentRect;
this.setState({
plotRevision: oldPlotRevision + 1,
height: rect.height,
width: rect.width,
});
});
resizeObserver.observe(this.container);
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.plotRevision !== nextState.plotRevision) {
return true;
} else if (this.props.currentTime !== nextProps.currentTime) {
return true;
}
return false;
}
render() {
const data = [
{
name: 'Threshold',
type: 'scattergl',
mode: 'lines',
x: this.props.thresholdsTime,
y: this.props.thresholds,
side: 'above',
},
{
name: 'Backtracks',
type: 'scattergl',
mode: 'lines',
x: this.props.backtracksTime,
y: this.props.backtracks,
},
{
name: 'Current Time',
type: 'scattergl',
mode: 'lines',
x: [this.props.currentTime, this.props.currentTime],
y: [0, 1],
yaxis: 'y2',
},
];
return (
<div className={styles['threshold-window']} ref={(el) => { this.container = el; }}>
<Plot
divId={`backtracks-${this.props.name}`}
className={styles['threshold-graph']}
ref={(el) => { this.plot = el; }}
layout={{
width: this.state.width,
height: this.state.height,
yaxis: {
fixedrange: true,
},
yaxis2: {
side: 'right',
range: [0, 1],
},
margin: {
l: 35,
r: 15,
b: 20,
t: 15,
},
legend: {
orientation: 'h',
y: 1,
},
}}
revision={this.state.plotRevision}
data={data}
/>
</div>
);
}
}
Edit2: I don't actually see the currentTime line anywhere, so I'm pretty sure there's a bug somewhere.
With react-plotly.js the performance should be decent, as it will only redraw what it needs to.