9.2 KiB
title | pagination_prev | pagination_next | sidebar_position | sidebar_custom_props | ||
---|---|---|---|---|---|---|
NativeScript | demos/salesforce | demos/desktop/index | 2 |
|
The NodeJS Module can be imported from the main entrypoint or any script in the project.
:::warning Binary Data issues
NativeScript will not safely transmit binary or UTF-8 strings. XLSB, NUMBERS, XLSX, XLS, ODS, SYLK, and DBF exports are known to be mangled.
This is a known NativeScript bug.
This demo will focus on ASCII CSV files. Once the bug is resolved, XLSX and other formats will be supported.
:::
Integration Details
The @nativescript/core/file-system
package provides classes for file access.
Reading and writing data require a file handle. The following snippet searches typical document folders for a specified filename:
import { File, Folder, knownFolders, path } from '@nativescript/core/file-system';
function get_handle_for_filename(filename: string): File {
const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
const url: string = path.normalize(target.path + "///" + filename);
return File.fromPath(url);
}
The encoding ISO_8859_1
spiritually resembles the "binary"
SheetJS type
Reading data
File#readText(encoding.ISO_8859_1)
returns strings compatible with "binary"
/* get binary string */
const bstr: string = await file.readText(encoding.ISO_8859_1);
/* read workbook */
const wb = read(bstr, { type: "binary" });
Writing data
File#writeText
with the ISO_8859_1
encoding accepts "binary"
strings with
the caveat listed in the warning at the top of this section:
/* generate binary string */
const bstr: string = write(wb, { bookType: 'csv', type: 'binary' });
/* attempt to save binary string to file */
await file.writeText(bstr, encoding.ISO_8859_1);
Demo
The demo builds off of the NativeScript + Angular example. Familiarity with Angular and TypeScript is assumed.
:::note
This demo was tested on an Intel Mac on 2022 August 10. NativeScript version
(as verified with ns --version
) is 8.3.2
. The iOS simulator runs iOS 15.5
on an iPhone SE 3rd generation.
:::
Complete Example (click to show)
-
Follow the official Environment Setup instructions (tested with "MacOS + iOS")
-
Create a skeleton NativeScript + Angular app:
ns create SheetJSNS --ng
- Launch the app in the iOS simulator to verify that the demo built properly:
cd SheetJSNS
ns run ios
(this may take a while)
Once the simulator launches and the test app is displayed, end the script by
selecting the terminal and entering the key sequence CTRL + C
- From the project folder, install the library:
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
- To confirm the library was loaded, change the title to show the version. The differences are highlighted.
src/app/item/items.component.ts
imports the version string to the component:
// highlight-next-line
import { version } from 'xlsx';
import { Component, OnInit } from '@angular/core'
import { Item } from './item'
import { ItemService } from './item.service'
@Component({
selector: 'ns-items',
templateUrl: './items.component.html',
})
export class ItemsComponent implements OnInit {
items: Array<Item>
// highlight-next-line
version = `SheetJS - ${version}`;
constructor(private itemService: ItemService) {}
ngOnInit(): void {
this.items = this.itemService.getItems()
}
}
src/app/item/items.component.html
references the version in the title:
<!-- highlight-next-line -->
<ActionBar [title]="version"></ActionBar>
<GridLayout>
<ListView [items]="items">
<ng-template let-item="item">
<StackLayout [nsRouterLink]="['/item', item.id]">
<Label [text]="item.name"></Label>
</StackLayout>
</ng-template>
</ListView>
</GridLayout>
Relaunch the app with ns run ios
and the title bar should show the version.
- Add the Import and Export buttons to the template:
<ActionBar [title]="version"></ActionBar>
<!-- highlight-start -->
<StackLayout>
<StackLayout orientation="horizontal">
<Button text="Import File" (tap)="import()" style="padding: 10px"></Button>
<Button text="Export File" (tap)="export()" style="padding: 10px"></Button>
</StackLayout>
<!-- highlight-end -->
<ListView [items]="items">
<ng-template let-item="item">
<StackLayout [nsRouterLink]="['/item', item.id]">
<Label [text]="item.name"></Label>
</StackLayout>
</ng-template>
</ListView>
<!-- highlight-next-line -->
</StackLayout>
// highlight-start
import { version, utils, read, write } from 'xlsx';
import { Dialogs } from '@nativescript/core';
import { encoding } from '@nativescript/core/text';
import { File, Folder, knownFolders, path } from '@nativescript/core/file-system';
// highlight-end
import { Component, OnInit } from '@angular/core'
import { Item } from './item'
import { ItemService } from './item.service'
// highlight-start
function get_handle_for_filename(filename: string): [File, string] {
const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
const url: string = path.normalize(target.path + "///" + filename);
return [File.fromPath(url), url];
}
// highlight-end
@Component({
selector: 'ns-items',
templateUrl: './items.component.html',
})
export class ItemsComponent implements OnInit {
items: Array<Item>
version: string = `SheetJS - ${version}`;
constructor(private itemService: ItemService) {}
ngOnInit(): void {
this.items = this.itemService.getItems()
}
// highlight-start
/* Import button */
async import() {
}
/* Export button */
async export() {
}
// highlight-end
}
Restart the app process and two buttons should show up at the top:
- Implement import and export:
import { version, utils, read, write } from 'xlsx';
import { Dialogs } from '@nativescript/core';
import { encoding } from '@nativescript/core/text';
import { File, Folder, knownFolders, path } from '@nativescript/core/file-system';
import { Component, OnInit } from '@angular/core'
import { Item } from './item'
import { ItemService } from './item.service'
function get_handle_for_filename(filename: string): [File, string] {
const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
const url: string = path.normalize(target.path + "///" + filename);
return [File.fromPath(url), url];
}
@Component({
selector: 'ns-items',
templateUrl: './items.component.html',
})
export class ItemsComponent implements OnInit {
items: Array<Item>
version: string = `SheetJS - ${version}`;
constructor(private itemService: ItemService) {}
ngOnInit(): void {
this.items = this.itemService.getItems()
}
/* Import button */
async import() {
// highlight-start
/* find appropriate path */
const [file, url] = get_handle_for_filename("SheetJSNS.csv");
try {
/* get binary string */
const bstr: string = await file.readText(encoding.ISO_8859_1);
/* read workbook */
const wb = read(bstr, { type: "binary" });
/* grab first sheet */
const wsname: string = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
/* update table */
this.items = utils.sheet_to_json<Item>(ws);
Dialogs.alert(`Attempting to read to ${filename} in ${url}`);
} catch(e) { Dialogs.alert(e.message); }
// highlight-end
}
/* Export button */
async export() {
// highlight-start
/* find appropriate path */
const [file, url] = get_handle_for_filename("SheetJSNS.csv");
try {
/* create worksheet from data */
const ws = utils.json_to_sheet(this.items);
/* create workbook from worksheet */
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Sheet1");
/* generate binary string */
const wbout: string = write(wb, { bookType: 'csv', type: 'binary' });
/* attempt to save binary string to file */
await file.writeText(wbout, encoding.ISO_8859_1);
Dialogs.alert(`Wrote to ${filename} in ${url}`);
} catch(e) { Dialogs.alert(e.message); }
// highlight-end
}
}
Restart the app process.
Testing
The app can be tested with the following sequence in the simulator:
-
Hit "Export File". A dialog will print where the file was written
-
Open that file with a text editor. It will be a 3-column CSV:
id,name,role
1,Ter Stegen,Goalkeeper
3,Piqué,Defender
4,I. Rakitic,Midfielder
...
After the header row, add the line 0,SheetJS,Library
:
id,name,role
0,SheetJS,Library
1,Ter Stegen,Goalkeeper
3,Piqué,Defender
...
- Hit "Import File". A dialog will print the path of the file that was read. The first item in the list will change: