I am implementing an offline feature for my clients that would allow them to submit forms offline and re-submit later(mainly for no service). I want to allow a fail safe for them to email the data to us just in case the submission gets stuck. I cam using the Ionic Email Composer plugin and everything works fine if I want to send The JSON data, however I would prefer if my users were to just send a CSV file. I am unsure on what is the best option to convert my JSON code to CSV and then send as an attachment.
.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
import { Storage } from '#ionic/storage';
import { EmailComposer } from '#ionic-native/email-composer';
import { Angular5Csv } from 'angular5-csv/Angular5-csv';//Testing
import { ReapService } from '../../services/reap-service';
#IonicPage()
#Component({
selector: 'page-offline-data',
templateUrl: 'offline-data.html',
})
export class OfflineDataPage {
private offlineData:any [] =[];
private isData:boolean = false;
private emailJSONData:any = [];
private test:any;
constructor(public navCtrl: NavController,
public navParams: NavParams,
public storage: Storage,
public viewCtrl: ViewController,
private emailComposer: EmailComposer,
public reap: ReapService) {
}
ionViewDidLoad(){
this.storage.get('offlineSubmission').then((data)=>{
this.offlineData=data;
console.log(data);
/************ TESTING *********************/
console.log(JSON.stringify(data));
for(let i=0;i<data.length;i++){
this.emailJSONData.push(data[i]["Info"]);
}
console.log(this.emailJSONData);
this.test = new Angular5Csv(this.emailJSONData,'Form data');
console.log(this.test.csv);
});
if(this.offlineData==[]){
this.isData = false;
console.log(this.isData);
}
else{
this.isData = true;
}
}
dismiss() {
this.viewCtrl.dismiss();
}
sendEmail(){
this.emailComposer.isAvailable().then((available: boolean) =>{
if(available) {
//Now we know we can send
}
},(err) => {
this.reap.presentToast('Error Emailing Data');
console.log(err);
});
let email = {
to: 'john#doe.com',
cc: 'jane#doe.comm',
//bcc: ['john#doe.com', 'jane#doe.com'],
attachments: [
'file://'+this.test+'.csv'//Where I want to include the csv
],
subject: 'Offline Data Submissions',
body: this.emailJSONData.toString(),//Currently sending the raw JSON in body
isHtml: false
};
// Send a text message using default options
this.emailComposer.open(email);
}
}
JSON test code: I only need the Info part of the Array.
[{"Type":"SIG","Info":[{"personnel":{"ID":"1","FullName":"Test User","default":"false"}},{"signature":"data:image/png;base64,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"}],"Status":"Pending"}]
I am currently testing with Angular5csv, but I'm not sure If I can use Ionic File or any other useful plugins that may help get what I need.
You can download the csv file into IOS device file system using cordova-plugin-file. You can attach the same file path using attachment property in Email Composer plugin.
So I finally found my answer, which in my case would not be ideal to create a csv from the complex objects I will be using, but an example here:
convertToCSV(offlineData) {
var csv: any = ''
var line: any = ''
var offlineLength = offlineData.length
console.log(offlineLength);
//Header
for (var i = 0; i < offlineLength; i++) {
var formType = offlineData[i]['Type'];
line += formType+ '\r\n\r\n';
var SpT = offlineData[i]['Info'].length
for (var k = 0; k < SpT; k++){
line += Object.keys(offlineData[i]['Info'][k]);
if (line != '') line += ';'
}
line += '\r\n\r\n';
}
csv += line + '\r\n';
//offlineData
for (let i = 0; i < offlineLength; i++) {
line = ''
for (var j = 0; j < SpT; j++) {
if (line != '') line += ';'
switch(offlineData[i]['GPS'][j]){
case offlineData[i]['GPS'][j] == 'ID':
line += offlineData[i]['GPS'][j]['ID'];
break;
case 'Lat':
line += 'Lat';
break;
case ['Lon']:
line += 'Lon';
break;
default:
line += 'Test';
break;
}
}
csv += line + '\r\n'
}
return csv
}
saveAsCsv() {
var csv: any = this.convertToCSV(this.csvData)
console.log(csv)
var fileName: any = "test.csv"
this.File.writeFile(this.File.dataDirectory, fileName, csv ,{ replace: true })
.then(() => {
let email = {
to: 'email#email',
attachments: [
this.file.dataDirectory + 'test.csv'
],
subject: 'subject',
body: 'body text...',
isHtml: true
};
this.emailComposer.open(email);
})
.catch((err) => {
console.error(err);
});
}
Of course this isn't the exact data I was using in my question, but the convertToCSV function would allow you to manipulate your object and then spit it out to csv via email.
Credit for my answer is here.
Related
So, I have those "cards" to which are attached files.
I want to be able to display the content of these files (when possible; I do not expect to show binary files obviously, but text, pdf, images,...) to the user.
Upon a longPress on an attachment, the openAttachment() function is be called. That function downloads the file from the server if necessary and then (tries to) open it:
// Opens an attachment
const openAttachment = async (attachment) => {
try {
// Download file if not already done
const fileInfo = await FileSystem.getInfoAsync(FileSystem.cacheDirectory + attachment.name)
let uri
if (!fileInfo.exists) {
console.log('Downloading attachment')
resp = await FileSystem.downloadAsync(
server.value + `/index.php/apps/deck/api/v1.0/boards/${route.params.boardId}/stacks/${route.params.stackId}/cards/${route.params.cardId}/attachments/${attachment.id}`,
FileSystem.cacheDirectory + attachment.name,
{
headers: {
'Authorization': token.value
},
},
)
console.log(resp)
uri = await FileSystem.getContentUriAsync(resp.uri)
} else {
console.log('File already in cache')
uri = await FileSystem.getContentUriAsync(fileInfo.uri)
}
console.log('Opening file ' + uri)
Sharing.shareAsync(uri);
} catch {
Toast.show({
type: 'error',
text1: i18n.t('error'),
text2: error.message,
})
console.log(error)
}
}
The issue always arrise at the Sharing.shareAsync(uri); line: Whatever I put there, it fails:
Sharing.shareAsync(uri) does not seem to be supported on my platform: https://docs.expo.dev/versions/latest/sdk/sharing/
Linking.openURL(uri) does not support the file:// scheme (the uri is in the form file:///var/mobile/Containers/Data/Application/5C1CB402-5ED1-4E17-B907-46111AE3FB7C/Library/Caches/test.pdf)
await WebBrowser.openBrowserAsync(uri) (from expo-web-browser) does not seem to be able to open local files
How am I supposed to do to display those files? Anyone has an idea?
Cyrille
I found a solution using react-native-file-viewer
// Opens an attachment
const openAttachment = async (attachment) => {
try {
// Download file if not already done
const fileInfo = await FileSystem.getInfoAsync(FileSystem.cacheDirectory + "attachment.name")
let uri
if (!fileInfo.exists) {
console.log('Downloading attachment')
const resp = await FileSystem.downloadAsync(
server.value + `/index.php/apps/deck/api/v1.0/boards/${route.params.boardId}/stacks/${route.params.stackId}/cards/${route.params.cardId}/attachments/${attachment.id}`,
FileSystem.cacheDirectory + attachment.name,
{
headers: {
'Authorization': token.value
},
},
)
console.log(resp)
uri = await FileSystem.getContentUriAsync(resp.uri)
} else {
console.log('File already in cache')
uri = await FileSystem.getContentUriAsync(fileInfo.uri)
}
console.log('opening file', uri)
FileViewer.open(uri)
} catch(error) {
Toast.show({
type: 'error',
text1: i18n.t('error'),
text2: error.message,
})
console.log(error)
}
}
I am creating excel file from user data but unfortunately its not generating file and even don't know what is the error so I can at-least try to solve that error.
Specially this issue occur in iOS platform only.
Please find below code to generate excel file:
public createXSLX(): Promise<any> {
return new Promise((resolve) => {
let sheet = XLSX.utils.json_to_sheet(this.data);
let wb = {
SheetNames: ["export"],
Sheets: {
"export": sheet
}
};
let wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});
function s2ab(s) {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
let blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
resolve(blob);
});
}
This above function works very well in android but in iOS its not generation file from provided data.
How I call above function code :
onExportNew = function (toEmail) {
this.createXSLX().then((xclBlob) => {
let time = new Date().getTime();
let fileName: string = "roster_" + time + ".xlsx";
var fs = ''
if (this.platform.is('ios')){
fs = this.file.documentsDirectory;
}else{
fs = this.file.externalDataDirectory;
}
console.log("File Path:- ",fs)
this.file.writeFile(fs, fileName, xclBlob, true).then(() => {
let fp = fs + fileName;
let email = {
// to: 'lmahajan#cisco.com',
// cc: 'erika#mustermann.de',
// bcc: ['john#doe.com', 'jane#doe.com'],
to: toEmail,
attachments: [fp],
subject: 'Roster Excel File',
body: '<h1>PFA</h1>',
isHtml: true
};
this.emailComposer.open(email).then(() => {
this.showDone = true;
}).catch(() => {
let toast = this.toastCtrl.create({
message: 'Could not open email composer',
duration: 3000
});
toast.present();
});
}).catch(() => {
this.displayAlert('Error', 'error creating file at: ' + fs);
});
}).catch(() => {
console.log("Excel file creation error");
});
}
Guide me if missing anything in above code.
Thanks in advance!
This is the function I am using to upload file but is is giving me the error : Length is undefined. what I have to change in this code. where to give path of file to upload.
fileChange(event) {
let fileList: FileList = event.target.files;
if(fileList) {
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('uploadFile', file, file.name);
let headers = new Headers();
/** No need to include Content-Type in Angular 4 */
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
this.http.post(`assets/Files/info.txt`, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log(fileList),
error => console.log(error)
)
}
}
you need to use xhr request to transfer files
fileChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
let files: FileList = target.files;
if(files) {
let file: File = files[0];
this.upload(file)
}
}
public upload(filedata: File) {
let url = 'your url'
if (typeof filedata != 'undefined') {
return new Promise((resolve, reject) => {
let formData: any = new FormData();
let xhr = new XMLHttpRequest();
formData.append('icondata', filedata, filedata.name);
xhr.open('POST', url, true);
xhr.setRequestHeader('Authorization', 'JWT ' + localStorage.getItem('id_token'));
xhr.send(formData);
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
resolve(JSON.parse(xhr.responseText));
}
}
});
}
}
I understand that this is not the functionality you want to have but with no backend you can not upload files to be persistent, they should be stored somewhere. If you just wanna manipulate file names for instance, skip the express part in my answer. I personally used this code which I altered to upload multiple files.
In your Component :
import {FormArray, FormBuilder, FormControl, FormGroup} from "#angular/forms";
declare FormBuilder in the constructor:
constructor (private http: Http, private fb: FormBuilder) {}
in ngOnInit() set a variable as follows :
this.myForm = this.fb.group({chosenfiles: this.fb.array([])});
this is the code for the upload method :
// invoke the upload to server method
// TODO
// Should be in a service (injectable)
upload() {
const formData: any = new FormData();
const files: Array<File> = this.filesToUpload;
//console.log(files);
const chosenf = <FormArray> this.myForm.controls["chosenfiles"];
// iterate over the number of files
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i], files[i]['name']);
// store file name in an array
chosenf.push(new FormControl(files[i]['name']));
}
this.http.post('http://localhost:3003/api/upload', formData)
.map(files => files.json())
.subscribe(files => console.log('upload completed, files are : ', files));
}
the method responsible for the file change :
fileChangeEvent(fileInput: any) {
this.filesToUpload = <Array<File>>fileInput.target.files;
const formData: any = new FormData();
const files: Array<File> = this.filesToUpload;
console.log(files);
const chosenf = <FormArray> this.myForm.controls["chosenfiles"];
// iterate over the number of files
for(let i =0; i < files.length; i++){
formData.append("uploads[]", files[i], files[i]['name']);
// store file name in an array
chosenf.push(new FormControl(files[i]['name']));
}
}
Template is something like this
<input id="cin" name="cin" type="file" (change)="fileChangeEvent($event)" placeholder="Upload ..." multiple/>
Notice multiple responsible for allowing multiple selections
The express API which will handle the request uses multer after an npm install
var multer = require('multer');
var path = require('path');
specify a static directory which will hold the files
// specify the folder
app.use(express.static(path.join(__dirname, 'uploads')));
As specified by multer
PS: I did not investigate multer, as soon as i got it working, i moved to another task but feel free to remove unnecessary code.
var storage = multer.diskStorage({
// destination
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
var upload = multer({ storage: storage });
And finally the endpoint
app.post("/api/upload", upload.array("uploads[]", 12), function (req, res) {
console.log('files', req.files);
res.send(req.files);
});
Hello I am creating a application using office.js which will be used in excel and word application addIn and I have written some code that actually gives the text of entire row cell by cell. but as my requirement was to maintain styles and of every cell and store them in database so that when again addin runs it should load the data in same format it was stored. Currently it is just text i am getting in response. I have asked a similar question like this which was to get the text with styles from current cell that really works great.
How do I get the formatting the Current cell of the table in Word using office.js
There is another thing if it is possible to get the cell html by row and column position that will also solve the problem.
Thank you!
Hi i found the solution of my problem but this is solution for word only this is not working in excel but this was working for me so i am writing here :-
function Addtable() {
var document = Office.context.document;
var headers = [["Cities"]];
var rows = [['<b>Hello there</b> <ul><li>One</li><li>Two</li></ul>'], ['Roma'], ['Tokyo'], ['Seattle']];
var html = '<table>';
html += '<thead>';
for (var i = 0; i < headers.length; i++) {
html += '<tr>';
var cells = headers[i];
for (var j = 0; j < cells.length; j++) {
html += '<th>' + cells[j] + '</th>';
}
html += '</tr>';
}
html += '</tr>';
html += '</thead>';
html += '<tbody>';
for (var i = 0; i < rows.length; i++) {
html += '<tr>';
var cells = rows[i];
for (var j = 0; j < cells.length; j++) {
html += '<td>' + cells[j] + '</td>';
}
html += '</tr>';
}
html += '</tbody>';
html += '</table>';
Office.context.document.setSelectedDataAsync(html, { coercionType: Office.CoercionType.Html }, function (asyncResult) {
document.bindings.addFromSelectionAsync(Office.BindingType.Table, function (result) {
console.log(result);
var binding = result.value;
binding.addHandlerAsync(Office.EventType.BindingSelectionChanged, onBindingSelectionChanged);
});
});
}
The above is the function that i call when i want to generate a table with html values in it.
and bellow is is code that i am using to get the value of the current cell and replacing with some dummy value.
var onBindingSelectionChanged = function (results) {
if (!isExecuted) {
Word.run(function (context) {
var tableCell = context.document.getSelection().parentTableCell;
context.load(tableCell);
return context.sync()
.then(function () {
if (tableCell.isNull == true) {
//selection is not within a cell.....
console.log("selection not in a header");
}
else {
// the selection is inside a cell! lets get the content....
var body = tableCell.body;
var html = tableCell.body.getHtml();
var tableHtml = tableCell.body.getHtml();
context.sync()
.then(function () {
var cellHtml = html.value;
var newHtml = "<table><tr><td><ul><li>yellow</li></ul></td></tr></table";
// Option 1
body.insertHtml(newHtml, Word.InsertLocation.replace);
// Option 2
//body.clear();
//body.insertHtml(newHtml, Word.InsertLocation.end);
return context.sync().then(function () {
console.log('HTML was successfully replaced.');
}).catch(function (e) {
console.log(e.message);
});
}).catch(function (e) {
console.log(e.message);
});
}
}).catch(function (e) {
console.log(e.message);
});
});
isExecuted = true;
}
else {
isExecuted=false;
}
};
Thank you!
From the API page, I gather there's no function for what I'm trying to do. I want to read text from a file storing it as a list of strings, manipulate the text, and save the file. The first part is easy using the function:
abstract List<String> readAsLinesSync([Encoding encoding = Encoding.UTF_8])
However, there is no function that let's me write the contents of the list directly to the file e.g.
abstract void writeAsLinesSync(List<String> contents, [Encoding encoding = Encoding.UTF_8, FileMode mode = FileMode.WRITE])
Instead, I've been using:
abstract void writeAsStringSync(String contents, [Encoding encoding = Encoding.UTF_8, FileMode mode = FileMode.WRITE])
by reducing the list to a single string. I'm sure I could also use a for loop and feed to a stream line by line. I was wondering two things:
Is there a way to just hand the file a list of strings for writing?
Why is there a readAsLinesSync but no writeAsLinesSync? Is this an oversight or a design decision?
Thanks
I just made my own export class that handles writes to a file or for sending the data to a websocket.
Usage:
exportToWeb(mapOrList, 'local', 8080);
exportToFile(mapOrList, 'local/data/data.txt');
Class:
//Save data to a file.
void exportToFile(var data, String filename) =>
new _Export(data).toFile(filename);
//Send data to a websocket.
void exportToWeb(var data, String host, int port) =>
new _Export(data).toWeb(host, port);
class _Export {
HashMap mapData;
List listData;
bool isMap = false;
bool isComplex = false;
_Export(var data) {
// Check is input is List of Map data structure.
if (data.runtimeType == HashMap) {
isMap = true;
mapData = data;
} else if (data.runtimeType == List) {
listData = data;
if (data.every((element) => element is Complex)) {
isComplex = true;
}
} else {
throw new ArgumentError("input data is not valid.");
}
}
// Save to a file using an IOSink. Handles Map, List and List<Complex>.
void toFile(String filename) {
List<String> tokens = filename.split(new RegExp(r'\.(?=[^.]+$)'));
if (tokens.length == 1) tokens.add('txt');
if (isMap) {
mapData.forEach((k, v) {
File fileHandle = new File('${tokens[0]}_k$k.${tokens[1]}');
IOSink dataFile = fileHandle.openWrite();
for (var i = 0; i < mapData[k].length; i++) {
dataFile.write('${mapData[k][i].real}\t'
'${mapData[k][i].imag}\n');
}
dataFile.close();
});
} else {
File fileHandle = new File('${tokens[0]}_data.${tokens[1]}');
IOSink dataFile = fileHandle.openWrite();
if (isComplex) {
for (var i = 0; i < listData.length; i++) {
listData[i] = listData[i].cround2;
dataFile.write("${listData[i].real}\t${listData[i].imag}\n");
}
} else {
for (var i = 0; i < listData.length; i++) {
dataFile.write('${listData[i]}\n');
}
}
dataFile.close();
}
}
// Set up a websocket to send data to a client.
void toWeb(String host, int port) {
//connect with ws://localhost:8080/ws
//for echo - http://www.websocket.org/echo.html
if (host == 'local') host = '127.0.0.1';
HttpServer.bind(host, port).then((server) {
server.transform(new WebSocketTransformer()).listen((WebSocket webSocket) {
webSocket.listen((message) {
var msg = json.parse(message);
print("Received the following message: \n"
"${msg["request"]}\n${msg["date"]}");
if (isMap) {
webSocket.send(json.stringify(mapData));
} else {
if (isComplex) {
List real = new List(listData.length);
List imag = new List(listData.length);
for (var i = 0; i < listData.length; i++) {
listData[i] = listData[i].cround2;
real[i] = listData[i].real;
imag[i] = listData[i].imag;
}
webSocket.send(json.stringify({"real": real, "imag": imag}));
} else {
webSocket.send(json.stringify({"real": listData, "imag": null}));
}
}
},
onDone: () {
print('Connection closed by client: Status - ${webSocket.closeCode}'
' : Reason - ${webSocket.closeReason}');
server.close();
});
});
});
}
}
I asked Mads Agers about this. He works on the io module. He said that he decided not to add writeAsLines because he didn't find it useful. For one it is trivial to write the for loop and the other thing is that you have to parameterize it which the kind of line separator that you want to use. He said he can add it if there is a strong feeling that it would be valuable. He didn't immediately see a lot of value in it.