angular return uploaded excel file in HTML grid - excel

I am struggling to get my code to display in HTML. I know I am probably missing something really simple, but I am stuck.
I have an Angular 7 app. I want to retrieve the xlsx data (worksheet) and display it in HTML. Simple right? My issue is that all the examples are in browser js and I need to retrieve it from a component. Here's the kicker, I already have the method and can print out json and html via console. log, but cannot find how to return it to an editable grid in HTML.
component.ts
fileUpload() {
let fileReader = new FileReader();
fileReader.onload = (e) => {
this.arrayBuffer = fileReader.result;
var data = new Uint8Array(this.arrayBuffer);
var arr = new Array();
for (var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
var workbook = XLSX.read(bstr, { type: "binary" });
var first_sheet_name = workbook.SheetNames[0];
var worksheet = workbook.Sheets[first_sheet_name];
console.log(XLSX.utils.sheet_to_json(worksheet, { raw: true }));
console.log(XLSX.utils.sheet_to_html(worksheet));
}
fileReader.readAsArrayBuffer(this.file);
}
html
<input type="file" style="display: inline-block;" (change)="incomingfile($event)" placeholder="Upload file" accept=".xls,.xlsx,.ods">
Upload
Currently I can retrieve any workbook/worksheet, but I simply cannot find how to return it to a ui grid of some sort from the .ts file.
I have been through numerous tutorials including ui-grid, ng-grid and https://redstapler.co/sheetjs-tutorial-convert-excel-html-table/
I appreciate I am probably being a dumbass, but any help would be appreciated. I cannot use a commercial component and simply need to render this in an editable table on my html page. I will then look at changing column headers and exporting the file via the backend SpringBoot REST to Mongo (all working)
I am aware that there are numerous similar questions on SO, but the working examples do not seem to display the data(even without any errors). Could be a versioning error etc. I also tried ui-grid, but came up with this issue: 'readAsArrayBuffer' on 'FileReader': parameter 1 is not of type 'Blob'. The example here is however also in .js and not via a typescript component: parameter is not of type 'Blob'
I am simply not able to bind the output to the HTML

Related

Convert HTML page to PDF with CSS3 Support

I'm working on a small project whereby I create multiple CVs (or resumes) via an interface I've built in Vue + Laravel, which I can then export to PDF.
I'm having issues though when I export the PDF. Laravel DOMPDF doesn't let me have CSS3 properties inside the PDF, for example flex, or CSS variables. I believe PDFs only support CSS 2.0, but I have seen multiple PDFs being exported that are an exact carbon-copy of the website. For example, resume.io - when you create a CV via their site, they can export it and make it look exactly like the website version.
My question is: does anyone know of a library that I could use that ties into Vue or Laravel that will produce a carbon-copy of the website template into a PDF?
I have tried a few JS libraries that take screenshots of certain elements on the page, then try and fit them together, but it just doesn't work. I basically need a specific element on the page to be selectable and then saved to a PDF. Please see my example below:
As you can see, the white area is the CV preview, so I need that whole section saved to a PDF, minus the right hand side menu and the top-bar. I'm planning on building some really cool templates, but if I can't use modern CSS practices then it's going to be quite hard to make them into a PDF.
At the moment, I've got two views, the CV preview which you can see above, then another view which re-uses partials that are inserted in the PDF template. Obviously though, reusing the partials which have modern CSS applied then makes the PDF break or look broken.
My stack:
Laravel
Vue.js
TailwindCSS
Laravel-DOMPDF
If anyone could advise on the best way to go about this, I'd really appreciate it.
TIA
Since you didn't mentioned the converted page is in Vue or Blade, I'll explain both way.
Here's the Library, which all you need is to design a Blade view, then do something like this
Route::get('/doc', function () {
//
$data = Marketers::all();
// LoadView with $data
$pdf = PDF::loadView('pdf',$data)->setPaper('A4');
// LoadView with Compact
$pdf = PDF::loadView('pdf',compact('data'))->setPaper('A4');
// Then Download it
return $pdf->download('pdf.pdf');
});
Now in Vue you need jsPDF and htmlToCanvas or htmlToIMage
i used HTMLtoImage because i had some character issues for persian language so I'll help you base on HtmlToImage Library.
<template>
// Part you want to Convert to pdf or ...
<div ref="contentz" id="jsPdf" >
// Contents
<div/>
</template>
downloadFull(t) {
let self = this
switch (t) {
case 1:
const doc = new jsPDF("l", "mm", "a4");
htmlToImage.toCanvas(document.getElementById('jsPdf'))
.then(function (canvas) {
var img = canvas.toDataURL("image/jpeg");
var width = doc.internal.pageSize.getWidth();
var height = doc.internal.pageSize.getHeight();
doc.addImage(img, 'JPEG', 0, 0, width, 0);
doc.save('app.pdf');
})
.catch(function (error) {
self.$notifications.failedNotificationOnGetData(self)
});
break;
case 2:
htmlToImage.toJpeg(document.getElementById('jsPdf'), { quality: 1 })
.then(function (dataUrl) {
var link = document.createElement('a');
link.download = 'kalabala.jpeg';
link.href = dataUrl;
link.click();
})
.catch(function (error) {
self.$notifications.failedNotificationOnGetData(self)
});
}
break;
default:
break;
}
}
So here's my function which downloadFull(t) t will be the file type, you might don't need it or you can improve it with simple if/else without switch, first you you will import libraries like :
import jsPDF from 'jspdf';
import htmlToImage from 'html-to-image';
Then set page dimensions for jsPDF, then simply use HtmlToImage to get Canvas then set width, height and image Canvas with variables then simply add image to doc and save it. my functions are same but in first switch case I'll get PDF, in second I'll get JPEG.
If you're trying to do the first way but get download link in Vue page you must do the controller just like i explained at above then in VUE when u do API call you should use BLOB to download the file. Here's the Example :
getPDF(type) {
axios({
url : '/api/api_name/exportPDF',
method: 'POST',
responseType: 'blob'
})
.then(res => {
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'pdf.pdf');
document.body.appendChild(link);
link.click();
})
},
Good Luck.

Customize the quick launch items for certain pages in sharepoint

I have requirement where client wants to customize the items in quick launch for only certain
pages.So, I want to change the items in the quick launch with some other items for a few pages.(Not about cahnging the style of quick launch. Its about the replacingthe content in quick launch)
I hope using CEWP, I can achive this.But I am not much aware how to do it.
You can have two approachs here:
1) creating a webpart to replace the quicklaunch: This way you can read the Navigation from SPWeb, and build it your own.
2) Using jQuery to change the html loading the page. In this approach, I would apply a 'display:none' to quicklaunch, make the changes in html, and then 'display:block' back. The con in this solution is that you must rely on the names/titles/urls of the items, so if an admin changes, it could break it.
I had followed following steps to achive the goal
1.. Added a CEWP in the page
Created a text file with Following script and added it to shared dcouments
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
function startClock(){
var div= document.getElementById('s4-leftpanel-content');
var spans= div.getElementsByTagName('span');
for (index = spans.length - 1; index >= 0; index--) {
spans[index].parentNode.removeChild(spans[index]);
}
var urls= div.getElementsByTagName('a');
for (index = urls.length - 1; index >= 0; index--) {
urls[index].parentNode.removeChild(urls[index]);
}
var pTag = document.createElement('p');
pTag.innerHTML = "HR Report";
div.appendChild(pTag);
var aTag = document.createElement('ul');
div.appendChild(aTag);
var newLi = document.createElement('li');
aTag.appendChild(newLi);
var a= document.createElement('a');
a.setAttribute('href',"url");
a.innerHTML = "report2";
newLi.appendChild(a);
//do onload work
}
if(window.addEventListener){
window.addEventListener('load',startClock,false); //W3C
}
else{
window.attachEvent('onload',startClock); //IE
}
</script>
enter code here
Paste the url text file in shared documents in CEWP as content link(Edit web part >>content link>>paste url)
Now, existing items in the Quick Launch is removed and new items are added

multiple d3 visualizations in one HTML document

I want to have multiple d3 visualizations in one document. My idea is to store some configuration such as url of the RESTful service along with the SVG tag attributes. On document load d3 would read in the attributes and load the data from the server and create the visualization. In this way I could edit / rearrange the document while keeping the visualizations intact.
My question is whether there is already a standardized way (best practice) of doing this? Or is there some plugin or something I could use?
change: I realized that I should mention that I want to create different documents with the same code. Consequently the document defines the content of the visualization (not the code).
A sample document with SVG attributes:
...
<head>
...
<svg width="200"... src="localhost:8000/rest/host1/cpu" type="os.line">...</svg>
<svg width="200"... src="localhost:8000/rest/host1/memory" type="os.bar">...</svg>
in the end I decided to move the configuration into paragraphs (integrates well with redactor):
<p class="plot" src="localhost:8000/rest/host1/cpu" type="os.line">...</p>
<p class="plot" src="localhost:8000/rest/host1/memory" type="os.bar">...</p>
... here is my code:
define(['lib/d3.v3.min'], function(ignore) {
var visualise = function(plugins) {
var _host = function(src) {
return src.split("/")[4];
};
var _plot = function(type) {
var parts = type.split(".", 2);
return plugins[parts[0]][parts[1]];
};
d3.selectAll("p.plot")
.each(function() {
var div = d3.select(this);
var plot = _plot(div.attr("type"));
var url = div.attr("src");
var host = _host(url);
d3.csv(url, function(data) {
div.call(plot, data, host);
});
});
};
return {visualise: visualise};
});
comments, improvements are more than welcome.
Why not just append each visualization to a different div?

Accessing HTML controls inside iFrame

How do you access html controls inside an iframe from javascript in CRM?
I have:
var height = document.getElementById("IFRAME_TransactionProduct_RA").contentWindow.document.getElementById("txt").value;
but that results in "Error on page" and the content is not loaded.
The element I want to access is an html input with id of 'txt':
<input id="txt" type="hidden" />
Here's an example how you copy a value from a CRM field to a control in an embedded HTML control in an IFRAME. I'm assuming the names of the web resource and the field. You'll have to adapt those. You also might throw in a try-catch in case CRM throws in en exception (got the joke?) and please mind that I'm typing the code on my phone so there might be a typo somewhere (auto-correction, yey).
var source = Xrm.Page.data.entity.attributes.get("oneCoolField")
var information = source.getValue();
var customHtml = Xrm.Page.ui.controls.get("WebResource_EmbeddedHtmlContent");
var destination = customHtml.getObject().contentWindow.document;
if(destination) {
var customControl = destination.getElementById("elementToAccess");
if(customControl) {
customControl.value = information;
}
}
EDIT:
This gets you to the web resource.
var customHtml = Xrm.Page.ui.controls.get("WebResource_EmbeddedHtmlContent");
This gets you to the DOM of the IFRAME.
var destination = customHtml.getObject().contentWindow.document;
This gets you to the control on the custom page.
var customControl = destination.getElementById("elementToAccess");
This gets you the contents of the control.
var contents = customControl.innerHTML;
Which part fails on your computer?
With jQuery:
$(Xrm.Page.ui.controls.get('IFRAME_TransactionProduct_RA').getObject()).contents().find('#txt').val();
Pure JS:
Xrm.Page.ui.controls.get('IFRAME_TransactionProduct_RA').getObject().contentWindow.document.getElementById('txt').value;
http://msdn.microsoft.com/en-us/library/gg334266.aspx#BKMK_getObject

Template binding with nested for loops WinJS

I've got an issue where I'm using template.render to render an array of items based on a html template. Each item in the array also contains another array, that I want to bind to another template, within the parent element for the area. I know I can use a grid layout for groups, but I'm trying to accomplish this another way, so please, no suggestions to use a different control, I'm just curious as to why the following doesn't work correctly.
//html templates
<div id="area-template" data-win-control="WinJS.Binding.Template">
<h1 class="area-title" data-win-bind="innerHTML:title"></h1>
<div class="items">
</div>
</div>
<div id="item-template" data-win-control="WinJS.Binding.Template">
<h2 class="item-title" data-win-bind="innerHTML:title"></h2>
</div>
// JS in ready event
var renderer = document.getElementsByTagName('section')[0];
var area_template = document.getElementById('area-template').winControl;
var item_template = document.getElementById('item-template').winControl;
for (var i = 0; i < areas.length; i++) {
var area = areas.getAt(i);
area_template.render(area, renderer).done(function (el) {
var item_renderer = el.querySelector('.items');
for (var j = 0; j < area.items.length; j++) {
var item = area.items[j];
item_template.render(item, item_renderer).done(function (item_el) {
});
}
});
}
So what should happen, is that after it renders the area, in the "done" function the newly created element (el) gets returned, I'm then finding it's ".items" div to append the items to. However, this appends all the items to the first div created. If it was the last div, it might make more sense due to closures, but the fact it happens on the first one is really throwing me off!
What's interesting, is that if I replace my template render function using document.createElement and el.appendChild, it does display correctly e.g: (in the done of area render)
area_template.render(area, renderer).done(function (el) {
var item = area.items[j];
var h2 = document.createElement('h2');
h2.innerText = item.title;
el.appendChild(h2);
}
although I've realised this is el it is appending it to, not the actual .items div of the el
I'm not quite sure what could be going on here. It appears the value of el is getting updated correctly, but el.querySelector is either always returning the wrong ".items" div or it's getting retained somewhere, however debugging does show that el is changing during the loop. Any insight would be greatly appreciated.
thanks
I've worked out what is going on here. The "el" returned in the render promise is not the newly created element as I thought. It's the renderer and the newly created html together. Therefore el.querySelector('.items') is always bringing back the first '.items' it finds. I must have misread the docs, but hopefully someone else will find this information useful in case they have the same error.
I guess one way around this would be to do item_rendered = el.querySelectorAll('.items')[i] and return the numbered '.items' based on the position in the loop
e.g
for (var i = 0; i < areas.length; i++) {
var area = areas.getAt(i);
area_template.render(area, renderer).done(function (el) {
var item_renderer = el.querySelectorAll('.items')[i];
for (var j = 0; j < area.items.length; j++) {
var item = area.items[j];
var h2 = document.createElement('h2');
h2.innerText = item.title;
item_renderer.appendChild(h2);
}
});
}

Resources