---
title: NativeScript
pagination_prev: demos/frontend/index
pagination_next: demos/desktop/index
sidebar_position: 2
sidebar_custom_props:
  summary: JS + Native Elements
---

The [NodeJS Module](/docs/getting-started/installation/nodejs) 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:

```ts
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"`

```ts
/* 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:

```ts
/* 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.

:::

<details><summary><b>Complete Example</b> (click to show)</summary>

0) Follow the official Environment Setup instructions (tested with "MacOS + iOS")

1) Create a skeleton NativeScript + Angular app:

```bash
ns create SheetJSNS --ng
```

2) Launch the app in the iOS simulator to verify that the demo built properly:

```bash
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`

3) From the project folder, install the library:

```bash
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
```

4) 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:

```ts title="src/app/item/items.component.ts"
// 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:

```xml title="src/app/item/items.component.html"
<!-- 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.

![NativeScript Step 4](pathname:///mobile/nativescript4.png)

5) Add the Import and Export buttons to the template:

```xml title="src/app/item/items.component.html"
<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>
```

```ts title="src/app/item/items.component.ts"
// 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:

![NativeScript Step 5](pathname:///mobile/nativescript5.png)

6) Implement import and export:

```ts title="src/app/item/items.component.ts"
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:

```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`:

```csv
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:

![NativeScript Step 7](pathname:///mobile/nativescript7.png)

</details>